• 游戏输入控制利器:DirectInput


    ------------------------------------------------------------------------------------------------------------------------------  

    在本篇文章中,我们一起详细探索了DirectInput这套在PC游戏即时控制方面一手遮天的API。下面先来看一下这篇一万多字文章的大体脉络。首先我们对DirectInput接口进行了整体上的讲解,然后深入DirectInput的使用步骤进行具体的探索,最后浅出,归纳出DirectInput使用五步曲,方便大家的快速掌握。文章最后,我们配了一个比较好玩的demo来让大家对本篇文章所学的DirectInput的使用融会贯通,最后提供了这个demo详细注释的源代码下载。先放一个demo的截图吊吊大家胃口,哈哈:

    一、引言


    众所周知,在普通的Windows程序中,用户通过键盘或者鼠标输入的消息并不是应用程序直接处理的,而是通过Windows的消息机制转发给Windows操作系统的。Windows操作系统对这些消息进行响应后,在通过回调应用程序的窗口过程函数进行相应的消息处理。

    这显然满足不了对于性能要求比较苛刻的游戏程序的。在DirectX中,微软为我们提供了名为DirectInput接口对象来实现用户输入的。DirectInput直接和硬件驱动打交道,因此处理起用户的输入来说非常迅速。

    首先需要给大家说明的是,DirectInput这套API自DirectX8更新以来,功能已经足够完善了。所以尽管当前DirectX的最新版本上升到了DirectX 11,DirectInput还是DirectX 8那个版本时代的老样子,API的内容和功能随着最近几个版本的更迭却原封不动,名称上也保留了8这个版本号,依然叫DirectInput 8,可谓以不变应万变。即目前最新版本的DirectInput ,依旧是DirectInput 8。

    二、DirectInput接口概述


    DirectInput作为DirectX的组件之一,自然依然是一些COM对象的集合。DirectInput由IDirectInput8、IDirectInputDevice8,IDirectInputEffect这三个接口组成,这三个接口中又分别含有各自的方法。

    总的来说,当前版本的DirectInputAPI中,三个接口,四十七个方法,组成了这个在电脑游戏开发中不可或缺的组件。

    由于IDirectInput8 API整体来说规模不大,说白了也就是三个接口,四十七个方法,不妨我们在文章中将他们一一列举出来,也让大家窥一窥DirectInput API的全貌。

    1.IDirectInput8接口 函数一览

    IDirectInput8::ConfigureDevices Method

    IDirectInput8::CreateDevice Method

    IDirectInput8::EnumDevices Method

    IDirectInput8::EnumDevicesBySemantics Method

    IDirectInput8::FindDevice Method

    IDirectInput8::GetDeviceStatus Method

    IDirectInput8::Initialize Method

    IDirectInput8::RunControlPanel Method

    2.IDirectInputDevice8接口 函数一览

    IDirectInputDevice8::Acquire Method

    IDirectInputDevice8::BuildActionMap Method

    IDirectInputDevice8::CreateEffect Method

    IDirectInputDevice8::EnumCreatedEffectObjects Method

    IDirectInputDevice8::EnumEffects Method

    IDirectInputDevice8::EnumEffectsInFile Method

    IDirectInputDevice8::EnumObjects Method

    IDirectInputDevice8::Escape Method

    IDirectInputDevice8::GetCapabilities Method

    IDirectInputDevice8::GetDeviceData Method

    IDirectInputDevice8::GetDeviceInfo Method

    IDirectInputDevice8::GetDeviceState Method

    IDirectInputDevice8::GetEffectInfo Method

    IDirectInputDevice8::GetForceFeedbackState Method

    IDirectInputDevice8::GetImageInfo Method

    IDirectInputDevice8::GetObjectInfo Method

    IDirectInputDevice8::GetProperty Method

    IDirectInputDevice8::Initialize Method

    IDirectInputDevice8::Poll Method

    IDirectInputDevice8::RunControlPanel Method

    IDirectInputDevice8::SendDeviceData Method

    IDirectInputDevice8::SendForceFeedbackCommand Method

    IDirectInputDevice8::SetActionMap Method

    IDirectInputDevice8::SetCooperativeLevel Method

    IDirectInputDevice8::SetDataFormat Method

    IDirectInputDevice8::SetEventNotification Method

    IDirectInputDevice8::SetProperty Method

    IDirectInputDevice8::Unacquire Method

    IDirectInputDevice8::WriteEffectToFile Method

    3.IDirectInputEffect接口 函数一览

    IDirectInputEffect::Download Method

    IDirectInputEffect::Escape Method

    IDirectInputEffect::GetEffectGuid Method

    IDirectInputEffect::GetEffectStatus Method

    IDirectInputEffect::GetParameters Method

    IDirectInputEffect::Initialize Method

    IDirectInputEffect::SetParameters Method

    IDirectInputEffect::Start Method

    IDirectInputEffect::Stop Method

    IDirectInputEffect::Unload Method

    其中,IDirectInput8作为DirectInput API中最主要的接口,用于初始化系统以及创建输入设备接口,DirectInput中其他的所有的接口都需要依赖于我们的IDirectInput8之上,都是通过这个接口进行查询的。而DirectInputDevice8接口用于表示各种输入设备(如键盘、鼠标和游戏杆),并提供了相同的访问和控制方法。对于某些输入设备(如游戏杆和鼠标),都能通过查询各自的IDirectInputDevice8接口对象,得到另一个接口IDirectInputEffect8。而IDirectInputEffect8接口则用于控制设备的力反馈效果。

     

    三、DirectInput使用步骤详解



    1.头文件和库文件的包含

    我们首先需要注意的是,在使用DirectInput时,需要保证我们包含了DInput.h头文件,并且在项目中已经链接了DInput8.lib库文件。

    当然,库文件我们也可以动态添加:

    #pragma comment(lib, "dinput8.lib")     // 使用DirectInput必须包含的头文件,注意这里有8
     

    2.创建DirectInput接口和设备

    在DirectInput中我通过们调用DirectInputCreate函数创建并初始化IDirectInput接口,我们可以在MSDN中查到该函数的声明如下:

     1 HRESULT DirectInput8Create(
     2 
     3 HINSTANCE hinst,
     4 
     5 DWORD dwVersion,
     6 
     7 REFIID riidltf,
     8 
     9 LPVOID * ppvOut,
    10 
    11 LPUNKNOWN punkOuter
    12 
    13 )

    ■ 第一个参数,HINSTANCE类型的hinst,表示我们当前创建的DirectInput的Windows程序句柄,这个值填我们在WinMain函数的参数中的实例句柄就可以了。

    ■ 第二个参数,DWORD类型的dwVersion,表示我们当前使用的DirectInput版本号,通常可以取DIRECTINPUT_VERSION或者DIRECTINPUT_HEADER_VERSION,这两个值对应的是同一个值,为0x0800。所以我们在这里还可以直接填0x0800。

    归根揭底的话,可以通过【转到定义】大法在dinput.h中查到有如下代码:

    1 #define DIRECTINPUT_HEADER_VERSION 0x0800
    2 
    3 #ifndef DIRECTINPUT_VERSION
    4 
    5 #define DIRECTINPUT_VERSION DIRECTINPUT_HEADER_VERSION

    大体意思很清楚了吧,先定义一下DIRECTINPUT_HEADER_VERSION=0x0800,然后再说如果没有定义DIRECTINPUT_VERSION的话,就定义一个DIRECTINPUT_VERSION= DIRECTINPUT_HEADER_VERSION。

    ■ 第三个参数,REFIID类型的riidltf,表示接口的标志,通常取IID_IDirectInput8就可以了。

    ■ 第四个参数,LPVOID 类型的* ppvOut,用于返回我们新创建的IDirectInput8接口对象的指针。

    ■ 第五个参数,LPUNKNOWN类型的punkOuter,一个和COM对象接口相关的参数,通常我们设为NULL就可以了。

    这个函数执行成功的话TINPUTVER会返回HRESULT类型的DI_OK,而失败的话根据不同的调用失败原因,会返回DIERR_BETADIRECSION,DIERR_INVALIDPARAM,DIERR_OLDDIRECTINPUTVERSION, DIERR_OUTOFMEMORY中的一个。所以我们可以根据FAILED宏来判断我们IDirectInput8接口对象是否创建成功了。

    下面是一个调用的例子:

    1 // 创建DirectInput设备
    2 
    3 LPDIRECTINPUT8 g_pDirectInput = NULL;
    4 
    5 if(FAILED(DirectInput8Create(hInstance, 0x0800, IID_IDirectInput8,( void**)&g_pDirectInput, NULL)))
    6 
    7 return E_FAIL;

    这步完成之后,咱们的定义的DIRECTINPUT8接口对象g_pDirectInput就有了权利,新官上任了。

    在IDirectInput8接口中包含了很多用于初始化输入设备及获得设备接口的方法。其中,常用的方法为EnumDevices和CreateDevices。前者EnumDevices用于获得输入设备的类型,而后者CreateDevices用于为输入设备创建IDirectInputDevice8接口对象。

    系统中每一个已安装的设备都有一个系统分配的全局唯一标示符(GUID,Global Unique Identification),从英文单词意义上就可以知道,系统中的每个设备都有着独一无二的GUID,这个GUID又唯一的标志了系统中的某某设备。就像我们每个人都有着独一无二的的身份证号码。

    要使用某个设备的话,首先我们就需要知道他的GUID。

    鼠标和键盘作为我们电脑中最为重要的外设,DirectInput对他们做了特殊对待,给了后门,定义了他们的GUID分别为GUID_Keyboard和GUID_SysMouse。而对于其他的输入设备,我们就用上面提到过的EnumDevices方法枚举出这些设备,以得到他们的GUID,我们可以在MSDN中查到这个方法有如下声明:

     1 HRESULTEnumDevices(
     2 
     3 DWORD dwDevType,
     4 
     5 LPDIENUMDEVICESCALLBACKlpCallback,
     6 
     7 LPVOID pvRef,
     8 
     9 DWORD dwFlags
    10 
    11 )

    ■ 第一个参数,DWORD类型的dwDevType,指定我们需要枚举的设备类型。

    可取的值为DI8DEVCLASS_ALL,DI8DEVCLASS_DEVICE,DI8DEVCLASS_GAMECTRL,DI8DEVCLASS_KEYBOARD,DI8DEVCLASS_POINTER中的一个。

    ■ 第二个参数,LPDIENUMDEVICESCALLBACK类型的lpCallback,用于指定一个回调函数的地址,当系统中每找到一个匹配的设备时,就会自动调用这个回调函数。

    ■ 第三个参数,LPVOID类型的pvRef,返回我们当前匹配设备的GUID值。

    ■ 第四个参数,DWORD类型的dwFlags,指定我们枚举设备的方式。取值可以下面的一个或者多个值:DIEDFL_ALLDEVICES,DIEDFL_ATTACHEDONLY,DIEDFL_FORCEFEEDBACK,DIEDFL_INCLUDEALIASES,DIEDFL_INCLUDEHIDDEN,DIEDFL_INCLUDEPHANTOMS。

    取得我们需要使用的设备的GUID后,就可以根据这个GUID来调用IDirectInput8接口的CreateDevice方法,进而来创建设备的IDirectInputDevice8接口对象了。

    我们可以在MSDN中查到IDirectInput8::CreateDevice方法的声明如下:

    1 HRESULTCreateDevice(
    2 
    3 REFGUID rguid,
    4 
    5 LPDIRECTINPUTDEVICE*lplpDirectInputDevice,
    6 
    7 LPUNKNOWN pUnkOuter
    8 
    9 )

    ■ 第一个参数,REFGUID类型的rguid,就是填我们上面讲到的输出设备的GUID。系统中当前使用的键盘对应GUID_SysKeyboard,当前使用的鼠标对应GUID_SysMouse。其他设备的话,就用我们刚刚讲过的EnumDevices获取一下就行了。

    ■ 第二个参数,LPDIRECTINPUTDEVICE类型的*lplpDirectInputDevice,表示我们所创建的输入设备对象的指针地址,可以说调用这个CreateDevice参数就是在初始化这个参数。

    ■ 第三个参数,LPUNKNOWN类型的pUnkOuter,、和COM对象的IUnknown接口相关的一个参数,一般我们不去管它,设为NULL就可以了。

    讲解完了,当然得看一个调用实例。下面的代码中CreateDevice方法的第二个参数我们填的是GUID_SysMouse,所以我们在为系统鼠标创建一个DirectInput设备接口对象:

    1 LPDIRECTINPUTDEVICE8 g_pMouseDevice = NULL;
    2 
    3 if(FAILED (g_pDirectInput->CreateDevice(GUID_SysKeyboard,&g_pKeyboardDevice, NULL)))
    4 
    5 return E_FAIL;

    3.设置数据格式

    数据格式用于表示设备状态信息的存储方式,每种设备都有一种用于读取对应数据的特定数据格式,所以对每种设备都要区别对待。所以要使程序从设备读入数据的话,首先我们需要告诉DirectInput读取这种数据所采用的格式。

    设置数据格式通常我们都是通过IDirectInputDevice8接口的SetDataFormat方法来做到的,这个方法可以把设备的数据格式填充到一个DIDATAFORMAT接口类型的对象。该方法的声明如下:

    HRESULT SetDataFormat(
    
    LPCDIDATAFORMAT lpdf
    
    )

    SetDataFormat方法唯一的变量就是LPCDIDATAFORMAT类型的lpdf,DirectInput已经为我们准备好了一些备选的参数,下面是一个列表:

    数据格式

    精析

    c_dfDIkeyboard

    标准键盘结构,包含256个字符,每个字符对应着每个键

    c_dfDIMouse

    标准鼠标结构,带有3个轴和4个按钮

    c_dfDIMouse2

    扩展鼠标结构,带有3个轴和8个按钮

    c_dfDIJoystick

    标准游戏杆,带有三个定位轴,3个旋转轴,两个滑块,1个POV hat和32个按钮

    c_dfDIJoystick2

    扩展的游戏杆



    依然是一个调用实例,设置鼠标的数据格式:

    g_pMouseDevice->SetDataFormat(&c_dfDIMouse);
     


    4.设置协作级别

    在Windows操作系统中,系统中的每个应用程序都通常会使用多个输入设备,并且同一输入设备也可能被多个应用程序同时使用。因此,需要一种方式来共享和协调应用程序对设备的访问。在DirectInput中,祭出的是协作级别(Cooperative Level)这套处理方式。

    协作级别定义了进程与其他应用程序和操作系统共享设备的方式。设备一旦创建就需要设置它的协作级别,协作级别表示了应用程序对设备的控制权。

    DirectInput的协作级别可以以两套方案来分类:前台、后台模式和共享、独占模式。

    Ⅰ.前台模式与后台模式

    其中,前台模式表示只有当窗口处于激活状态时,才能获得设备的控制权。而当处于非激活状态时,会自动失去设备的控制权;后台模式表示可以在任何状态下获取设备,即使是在窗口处于非激活状态时。后天模式可以被任何应用程序在任何时候使用并获取设备数据。

    Ⅱ.共享模式与独占模式

    共享模式表示多个应用程序可以共同使用该设备,而独占模式表示应用程序是唯一使用该设备的应用程序。这里需要注意一下,独占模式并非意味着其他应用程序不能获取输入设备状态,如果进程同时使用了后台模式与独占模式的话,当其他进程申请了独占模式的话,这个进程就会失去设备的控制权。

    我们平常都是通过IDirectInputDevice8接口的SetCooperativeLevel方法来设置设备的协作级别的,我们可以在MSDN中查到SetCooperativeLevel的声明如下:

    HRESULT SetCooperativeLevel(
    
    HWND hwnd,
    
    DWORD dwFlags
    
    )

    ■ 第一个参数,HWND类型的hwnd,显然就是填想要与当前设备相关联的窗口句柄了,且这个窗口需要属于当前进程的顶级窗口。

    ■ 第二个参数,DWORD类型的dwFlags,描述了当前设备的协作级别类型,也就是填我们上面讲到的前台、后台模式和共享、独占模式等一些模式的标识符,可取一个值到多个值,浅墨把取值在下表中出来了:

    协作级别类型

    精析

    DISCL_BACKGROUND

    后台模式,一般我们让他与DISCL_NONEXCLUSIVE(非独占模式)配合使用

    DISCL_FOREGROUND

    前台模式,一般我们让他与DISCL_EXCLUSIVE(独占模式)配合使用

    DISCL_EXCLUSIVE

    独占模式

    DISCL_NONEXCLUSIVE

    非独占(共享)模式

    DISCL_NOWINKEY

    让键盘上烦人的Windows键失效

              

    注意,后台模式和独占模式不能同时选择,用脚丫子来想都知道他们两个组合起来不符合逻辑,既然都是在后台了,还谈什么独占呢?

    下面依旧是一个调用实例,将鼠标设备的协作级别设为前台、独占模式:

     g_pMouseDevice->SetCooperativeLevel(hwnd,DISCL_FOREGROUND |DISCL_EXCLUSIVE);
     

    5.设置特殊属性

    设备的特殊属性包含设备的数据模式、缓冲区大小、以及设备的最小最大范围等等。DirectInput为我们提供了SetProperty方法来设置设备的特殊属性,我们可以在MSDN中查到这个方法有如下原型:

    HRESULT SetProperty(
    
            REFGUID rguidProp,
    
            LPCDIPROPHEADER pdiph
    
    )

    这个方法平常用得不算多,因为篇幅原因暂且先不详细讲了,需要用的时候大家去查一下文档就可以了。

    6.获取和轮询设备


    首先是一个常识,在访问和使用任何输入设备之前,首先必须获得该输入设备的控制权。权力这东西,人人都喜欢,对其趋之若鹜,在我们的计算机中也不例外。其他的程序随时都可能勾心斗角,争夺并抢走对输入设备的控制权。所以我们在使用之前,往往都要重新获取一下设备的控制权,以确保权力在我们手中。

    在DirectInput中,权力的敲门砖为IDirectInput8接口的Acquire方法,我们可以在MSDN中查到这个“权力权杖”有如下的原型:

    HRESULT Acquire()
     

    我们可以发现他简简单单清清白白,没有参数,返回值为HRESULT。调用起来当然是非常简单:

    g_pMouseDevice->Acquire();
     

    为了大家看起来简明扼要,咱们这里没有用if和FAILD宏给他括起来,进行错误处理。

    另外需要注意的是,在获得输入设备的控制权之前,必须先调用IDirectInputDevice8接口的SetDataFormat或者SetActionMap方法来设置一下数据格式,不然我们调用Acquire方法的话,将直接给我们返回DIERR_INVALIDPARAM错误的。

    另外需要讲到的是轮询。

    轮询可以准备在合适的情况下读取设备数据。因为数据可能具有临界时间的。这个轮询的原型也是非常非常的简单:

    HRESULT Poll()

    轮询用起来当然也是非常简单的:

    g_pMouseDevice ->Poll();

     

    7.读取设备信息

    在Direct3D应用程序中,拿到对输入设备的控制权之后,就可调用IDirectInputDevice8接口的GetDeviceState方法来读取设备的数据。而为了存储设备的数据信息,在调用该方法时,须传递一个数据缓冲区给GetDeviceState方法,这个GetDeviceState方法的原型我们可以在MSDN中查到是如下:

    HRESULT GetDeviceState(
    
    DWORD cbData,
    
    LPVOID lpvData
    
    )

    ■ 第一个参数,DWORD类型的cbData,指定了我们缓冲区的大小(具体是哪个缓冲区在第二个参数中)。

    ■ 第二个参数,LPVOID类型的lpvData,表示一个获取当前设备状态的结构体的地址值。

    他的数据格式和我们之前调用的IDirectInputDevice8::SetDataFormat方法有着前后呼应的密切联系。下面我们通过一个表格来看看是如何联系的:

    SetDataFormat中指定的数据格式

    GetDeviceState中对应的缓冲区结构体

    c_dfDIMouse

    DIMOUSESTATE结构体

    c_dfDIMouse2

    DIMOUSESTATE2结构体

    c_dfDIKeyboard

    大小为256个字节的数组

    c_dfDIJoystick

    DIJOYSTATE结构体

    c_dfDIJoystick2

    DIJOYSTATE2结构体


    比如,我们先调用了SetDataFormat设置了设备的数据格式为c_dfDIMouse:

    g_pMouseDevice->SetDataFormat(&c_dfDIMouse);
     

    那么我们在读取设备信息的时候调用GetDeviceState就需要把第二个参数填与dfDIMouse对应的DIMOUSESTATE结构体的一个实例:

    DIMOUSESTATE dimouse
    
    g_pMouseDevice-> GetDeviceState( sizeof(dimouse),(LPVOID)&dimouse);

    对此,我们可以抽象出一个函数,专门对付疑难杂症,应对各种类型的设备的数据读取,而且还考虑到了设备如果丢失掉了,在合适的时间自动重新获取该设备:

     1 //*****************************************************************************************
     2 
     3 // Name: Device_Read();
     4 
     5 // Desc: 智能读取设备的输入数据
     6 
     7 //*****************************************************************************************
     8 
     9 BOOL Device_Read(IDirectInputDevice8*pDIDevice, void* pBuffer, longlSize)
    10 
    11 {
    12 
    13 HRESULThr;
    14 
    15 while( true)
    16 
    17 {
    18 
    19 pDIDevice->Poll(); // 轮询设备
    20 
    21 pDIDevice->Acquire(); // 获取设备的控制权
    22 
    23 if(SUCCEEDED(hr = pDIDevice->GetDeviceState(lSize, pBuffer))) break;
    24 
    25 if(hr !=DIERR_INPUTLOST || hr != DIERR_NOTACQUIRED) return FALSE;
    26 
    27 if(FAILED(pDIDevice->Acquire())) return FALSE;
    28 
    29 }
    30 
    31 returnTRUE;
    32 
    33 }

    到这一步之后,就是调用一下Device_Read来读取数据了。调用之后,我们的键位数据其实就存在了g_pKeyStateBuffer之中,我们接下来要做的就是用if语句对g_pKeyStateBuffer数组中对应的键位进行试探,看看这个键是否被按下了。如果按下,就进行相关的处理就可以了,比如:

    1 Device_Read(g_pKeyboardDevice,(LPVOID)g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
    2 
    3 
    4 
    5 if (g_pKeyStateBuffer[DIK_A] & 0x80)
    6 
    7 fPosX -= 0.005f;

    当然,在最后,使用完输入设备后,必须调用IDirectInputDevice8接口的Unacquire方法释放设备的控制权,所谓的杯酒释兵权,且需要接着调用Release方法释放掉设备接口对象。

    g_pMouseDevice->Unacquire();
    
    g_pMouseDevice->Release();

     

    四、精炼:DirectInput使用五步曲



    上面讲解了洋洋洒洒七千字,信息量有些大,为了突出下重点,落实到一个字“用”上,让大家有的放矢,快速掌握DirectInput的使用方法。浅墨在这里依旧是来一个使用几步曲的归纳,主要以代码为载体,把上面讲得知识归纳一下。这回的DirectInput同样是五步曲。需要说明下的是,下面的代码是关于处理键盘消息的,而对于鼠标设备,需要改的地方非常少,也就是在第一步调用CreateDevice方法时GUID填GUID_SysKeyboard,然后在第二步SetDataFormat中填c_dfDIKeyboard就可以了(相关知识上面我们有详细讲到)。对于其他设备。依然是改这两个地方,其他设备的GUID用EnumDevices枚举一下就知道了,废话也不多说,下面就开始DirectInput使用五步曲的讲解:

    这五步曲分别是:

    一、创键DirectInput接口和设备,简称创设备

    二、设置数据格式和协作级别,简称设格式

    三、获取设备控制权,简称拿权力

    四、获取按键情况并做响应,简称取按键

    五、释放控制权和接口对象,简称释对象

    DirectInput使用五步曲载体代码:

      1 //首先是全局变量的定义
      2 
      3 LPDIRECTINPUTDEVICE8 g_pKeyboardDevice = NULL;
      4 
      5 char g_pKeyStateBuffer[ 256] ={ 0};
      6 
      7 //--------------------------------------------------------------------------------------—----------------
      8 
      9 //【DirectInput使用五步曲之一】,创键DirectInput接口和设备,简称创设备
     10 
     11 //--------------------------------------------------------------------------------------—----------------
     12 
     13 
     14 
     15 //创建DirectInput设备
     16 
     17 DirectInput8Create(hInstance, 0x0800, IID_IDirectInput8,( void**)&g_pDirectInput, NULL);
     18 
     19 g_pDirectInput->CreateDevice(GUID_SysKeyboard,&g_pKeyboardDevice, NULL);
     20 
     21 //--------------------------------------------------------------------------------------—----------------
     22 
     23 //【DirectInput使用五步曲之二】,设置数据格式和协作级别,简称设格式
     24 
     25 //--------------------------------------------------------------------------------------—----------------
     26 
     27 //设置数据格式和协作级别
     28 
     29 g_pKeyboardDevice->SetDataFormat(&c_dfDIKeyboard);
     30 
     31 g_pKeyboardDevice->SetCooperativeLevel(hwnd,DISCL_FOREGROUND |DISCL_NONEXCLUSIVE);
     32 
     33 
     34 
     35 //--------------------------------------------------------------------------------------—----------------
     36 
     37 //【DirectInput使用五步曲之三】,.获取设备控制权,简称拿权力
     38 
     39 //--------------------------------------------------------------------------------------—----------------
     40 
     41 g_pKeyboardDevice->Acquire();
     42 
     43 
     44 
     45 //--------------------------------------------------------------------------------------—----------------
     46 
     47 //【DirectInput使用五步曲之四】,.获取按键情况并做响应,简称取按键
     48 
     49 //--------------------------------------------------------------------------------------—----------------
     50 
     51 
     52 
     53 
     54 
     55 // 读取键盘输入
     56 
     57 ::ZeroMemory(g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
     58 
     59 Device_Read(g_pKeyboardDevice,(LPVOID)g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
     60 
     61 //定义的全局函数
     62 
     63 BOOL Device_Read(IDirectInputDevice8*pDIDevice, void* pBuffer, longlSize)
     64 
     65 {
     66 
     67 HRESULThr;
     68 
     69 while( true)
     70 
     71 {
     72 
     73 pDIDevice->Poll(); // 轮询设备
     74 
     75 pDIDevice->Acquire(); // 获取设备的控制权
     76 
     77 if(SUCCEEDED(hr = pDIDevice->GetDeviceState(lSize, pBuffer))) break;
     78 
     79 if(hr !=DIERR_INPUTLOST || hr != DIERR_NOTACQUIRED) return FALSE;
     80 
     81 if(FAILED(pDIDevice->Acquire())) return FALSE;
     82 
     83 }
     84 
     85 returnTRUE;
     86 
     87 }
     88 
     89 //然后就是用if判断并做响应了,如下面一句代码
     90 
     91 if (g_pKeyStateBuffer[DIK_A] & 0x80)fPosX -= 0.005f;
     92 
     93 
     94 
     95 //--------------------------------------------------------------------------------------—----------------
     96 
     97 //【DirectInput使用五步曲之五】,.释放控制权和接口对象,简称释对象
     98 
     99 //--------------------------------------------------------------------------------------—----------------
    100 
    101 g_pKeyboardDevice->Unacquire();
    102 
    103 SAFE_RELEASE(g_pKeyboardDevice)

    所以,上述DirectInput使用五步曲精炼总结起来就十五个字:

    创设备,设格式,拿权力,取按键,释对象

    五、DirectInput键盘按键键值总结



    与一般的Windows应用程序相比,DirectInput处理键盘事件的方式是有很多独特之处的。首先,在我们写的游戏程序中,键盘主要并不是用于文字输入的,而是用于控制3D世界中人物,对象的运动,或者视角的变换等等。且在游戏程序中我们常常只需要知道具体是哪个键被按下,而忽略了该键所对应的字符。所以我们只需读取已按下键的扫描码就可以了。

    另外,为了提高程序运行的效率,DirectInput并非使用Windows中的消息机制来读取键盘的状态,而是直接读取硬件的状态获取按键的扫描码的。

    我们在按照流程创建好和打理好DirectInput之后,就能在程序中不断获取从键盘输入的那些键盘数据。而在程序中,我们需要定义一个大小为256字节的数组,其中的每一个字节都存储一个按键的状态,这样就可以保存256个按键的状态信息了。微软在DirectInput中为每个键都设置了一个对应的宏,这些宏都是以DIK_为前缀的。例如C键就定义为DIK_C,主键盘数字键8就对应DIK_8等等,下面就是浅墨对DirectInput键码做的一个总结表格,查起来非常方便:

    DirectInput键码

    说明

    DIK_0~ DIK_9

    主键盘上数字键0~9

    DIK_A~ DIK_Z

    字母键A~Z

    DIK_F1~ DIK_F15

    功能键F1~F15

    DIK_NUMPAD0~ DIK_NUMPAD9

    小键盘0~9

    DIK_ESCAPE,DIK_TAB, DIK_BACK

    Esc键,Tab键,退格键

    DIK_RETURN,DIK_SPACE,DIK_NUMBERENTER

    回车键、空格键、小键盘回车键,

    DIK_LSHIFT, DIK_RSHIFT

    左右Shift键

    DIK_LMENU, DIK_RMENU

    左右菜单键

    DIK_LALT, DIK_RALT

    左右Alt键

    DIK_LCONTROL, DIK_RCONTROL

    左右Ctrl键

    DIK_UPARROW, DIK_DOWNARROW

    DIK_LEFTARROW, DIK_RIGHTARROW

    上下左右方向键

    DIK_HOME, DIK_DELETE, DIK_INSERT

    Home键,Delete键,Insert键

    DIK_PRIOR, DIK_NEXT, DIK_END

    PageUp键,PageDown键,End键

    比如我们要检测左Alt键是否按下,按下的话就做出响应,就可以在上表中找到左Alt键的键码为DIK_LALT,然后就是一句if(  ){ }语句:

    if (g_pKeyStateBuffer[DIK_A] & 0x80)      fPosX -= 0.005f;
     


    六、DirectInput鼠标按键键值总结



    在通常的Windows应用程序中,系统检测鼠标的移动并通过消息处理函数将鼠标的移动作为消息报告给用户,然而这样做的效率非常低下,因为传递给消息处理函数的每个消息首先都要走消息队列这条“官道”,需要慢悠悠地在消息队列中排队,排队完全满足不了我们对游戏即时处理消息的要求。而在Direct3D中,咱们就可以屌丝逆袭走后门了,我们可以直接同鼠标的驱动程序进行交互,而不用走消息队列这条慢悠悠的“官道”。

    另外,我们有两种方式来跟踪鼠标的移动为:绝对模式和相对模式。在绝对模式下,鼠标是基于某个固定点的,这个点通常是屏幕左上角,而此时返回的鼠标坐标是鼠标指针所处位置在屏幕坐标系中的坐标。

    而另外一种模式,也就是相对模式下,鼠标坐标则是根据上一个已知位置到当前位置所发生的移动量来得到鼠标的坐标值的。在相对模式下得到的鼠标坐标是一个相对位置,而非绝对位置,大家需要注意。

    好了,回到正题上来。在DirectInput中,鼠标的移动信息我们通常都是通过一个名叫DIMOUSESTATE结构体来记录的,我们可以在MSDN中查到这个结构体定义如下:

     1 typedef struct DIMOUSESTATE {
     2 
     3 LONG lX;
     4 
     5 LONG lY;
     6 
     7 LONG lZ;
     8 
     9 BYTE rgbButtons[ 4];
    10 
    11 } DIMOUSESTATE, *LPDIMOUSESTATE;

    这个结构体中,lX,lY,lZ分别记录了X轴,Y轴和Z轴(鼠标滚轮的相对移动量,鼠标没移动的话,他们的值就是0.)。而结构体中的第四个参数rgbButtons[4]记录了四个按钮的状态信息,其中rgbButtons[0]代表鼠标左键,rgbButtons[1]对应鼠标右键。如果需要处理支持更多按钮的鼠标的话,就去用DIMOUSESTATE2结构体吧。

    下面我们来看看实例:

     1 DIMOUSESTATE g_diMouseState = { 0};
     2 
     3 ::ZeroMemory(&g_diMouseState, sizeof(g_diMouseState));
     4 
     5 Device_Read(g_pMouseDevice,(LPVOID)&g_diMouseState, sizeof(g_diMouseState));
     6 
     7 
     8 
     9 //按住鼠标左键并拖动,为平移操作
    10 
    11 staticFLOAT fPosX = 0.0f, fPosY = 30.0f, fPosZ = 0.0f;
    12 
    13 if(g_diMouseState.rgbButtons[ 0] & 0x80)
    14 
    15 {
    16 
    17 fPosX+=g_diMouseState.lX * 0.08f;
    18 
    19 fPosY+=g_diMouseState.lY * -0.08f;
    20 
    21 }

    七、详细注释的源代码欣赏



    首先需要说明的是,本篇文章配套的源代码中用到了我们目前还未讲到的一点技术,就是X模型的载入。源代码中X模型的载入相关的代码大家如果看不懂没关系,请锁定浅墨的博客,后面一定会有相关技术精彩的讲解的。 

    然后这篇文章中的demo我们对细节部分做了升级,新加了三个功能,他们分别是:

    1.在窗口左上角智能读取运行的机器使用的显卡名称。

    2.在窗口左下角给出了帮助信息。

    3. 在窗口左上角给出了模型当前的三维坐标。

    下面我们分别来对这三个新功能进行讲解:

    1.在窗口左上角智能读取运行的机器使用的显卡名称。

    这个其实很简单,借助一个GetAdapterIdentifier方法就可以了。这个方法可以获取获取显卡的厂商类型等信息。原型如下

    HRESULT GetAdapterIdentifier(
    
    [in] UINT Adapter,
    
    [in] DWORD Flags,
    
    [out] D3DADAPTER_IDENTIFIER9*pIdentifier
    
    );

    注意到第三个参数类型是一个D3DADAPTER_IDENTIFIER9结构体,这个结构体的第三个参数Description就保存着显卡的名称的char类型的字符串。思路也就是围绕着这个GetAdapterIdentifier方法来的,用GetAdapterIdentifier方法取得显卡的名称的char类型的字符串,然后转换成wchar_t类型并在显卡名称之前拼接上“当前显卡型号:”字样,然后把结果存在全局的字符串数组g_strAdapterName中,最后在Render函数中用TextOut写出来就可以了。另外注意一点,因为IDirect3D9::GetAdapterIdentifier是IDirect3D9中的方法,而在我们的代码中IDirect3D9接口对象仅局部存在于Direct3D_Init( )方法中,所以我们绝大部分实现代码是在这个Direct3D_Init( )方法中完成的。具体做法咱们直接看代码,这可是每行都详细注释的代码:

          

    首先是一个全局变量:

    wchar_t                              g_strAdapterName[60]={0};    //包含显卡名称的字符数组
     

     然后就是Direct3D_Init( )方法中的功能实现代码:

    //获取显卡信息到g_strAdapterName中,并在显卡名称之前加上“当前显卡型号:”字符串
    
    wchar_tTempName[ 60]= L"当前显卡型号:"; //定义一个临时字符串,且方便了把"当前显卡型号:"字符串引入我们的目的字符串中
    
    D3DADAPTER_IDENTIFIER9Adapter; //定义一个D3DADAPTER_IDENTIFIER9结构体,用于存储显卡信息
    
    pD3D->GetAdapterIdentifier( 0, 0,&Adapter); //调用GetAdapterIdentifier,获取显卡信息
    
    int len =MultiByteToWideChar(CP_ACP, 0,Adapter.Description, -1, NULL, 0); //显卡名称现在已经在Adapter.Description中了,但是其为char类型,我们要将其转为wchar_t类型
    
    MultiByteToWideChar(CP_ACP, 0,Adapter.Description, -1, g_strAdapterName,len); //这步操作完成后,g_strAdapterName中就为当前我们的显卡类型名的wchar_t型字符串了
    
    wcscat_s(TempName,g_strAdapterName); //把当前我们的显卡名加到“当前显卡型号:”字符串后面,结果存在TempName中
    
    wcscpy_s(g_strAdapterName,TempName); //把TempName中的结果拷贝到全局变量g_strAdapterName中,大功告成~

    最后就是在Direct3D_Render函数中调用一下DrawText显示出来了:

              

    //显示显卡类型名
    
    g_pTextAdaperName->DrawText( NULL,g_strAdapterName, -1,&formatRect,
    
    DT_TOP| DT_LEFT, D3DXCOLOR( 1.0f, 0.5f, 0.0f, 1.0f));

    2.在窗口左下角给出帮助信息。

    其实非常简单,就是定义一些LPD3DXFONT接口对象,然后在Objects_Init()函数中用D3DXCreateFont创建不同的字体,最后在Direct3D_Render全DrawText出来就行了。

    3. 在窗口左上角给出了模型当前的三维坐标。

    其实也非常简单,就是用swprintf_s把世界矩阵g_matWorld的几个分量格式化到一个静态的wchar_t类型的字符串中,然后DrawText出来就可以了。

    实现代码如下:

            

    staticwchar_tstrInfo[ 256] = { 0};
    
    swprintf_s(strInfo, -1, L"模型坐标: (%.2f,%.2f, %.2f)",g_matWorld._41, g_matWorld._42, g_matWorld._43);
    
    g_pTextHelper->DrawText( NULL,strInfo, -1, &formatRect, DT_SINGLELINE| DT_NOCLIP | DT_LEFT,D3DCOLOR_RGBA( 135, 239, 136, 255));

    还有一点,因为考虑到咱们的Direct3D_Render()函数中的代码随着讲解的不断深入,代码越来越多,越来越杂,越来越乱。所以我们给他配了一个搭档Direct3D_Update(),跟即时绘制没有直接联系但是需要即时调用的,如按键后的坐标的更改,按键后填充模式的更改等等相关的代码,都放在Direct3D_Update()中了,这样就给Direct3D_Render()绘制函数减了负,看起来更加清晰。

    因为也是即时调用,所以Direct3D_Update()在消息循环中与Direct3D_Render()平起平坐了:

     1 //消息循环过程
     2 
     3 MSGmsg = { 0 }; //初始化msg
     4 
     5 while(msg.message !=WM_QUIT ) //使用while循环
     6 
     7 {
     8 
     9 if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。
    10 
    11 {
    12 
    13 TranslateMessage(&msg ); //将虚拟键消息转换为字符消息
    14 
    15 DispatchMessage(&msg ); //该函数分发一个消息给窗口程序。
    16 
    17 }
    18 
    19 else
    20 
    21 {
    22 
    23 Direct3D_Update(hwnd); //调用更新函数,进行画面的更新
    24 
    25 Direct3D_Render(hwnd); //调用渲染函数,进行画面的渲染
    26 
    27 }
    28 
    29 }

    最后一点,DirectInput使用五步曲的第四步,即获取按键状态并进行响应就是在Direct3D_Update中实现的:

    1.  
       
       
        1 void    Direct3D_Update( HWND hwnd)
        2 
        3 {
        4 
        5 // 获取键盘消息并给予设置相应的填充模式
        6 
        7 if (g_pKeyStateBuffer[DIK_1] & 0x80) // 若数字键1被按下,进行实体填充
        8 
        9 g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
       10 
       11 if (g_pKeyStateBuffer[DIK_2] & 0x80) // 若数字键2被按下,进行线框填充
       12 
       13 g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
       14 
       15 
       16 
       17 
       18 
       19 // 读取鼠标输入
       20 
       21 ::ZeroMemory(&g_diMouseState, sizeof(g_diMouseState));
       22 
       23 Device_Read(g_pMouseDevice, (LPVOID)&g_diMouseState, sizeof(g_diMouseState));
       24 
       25 
       26 
       27 
       28 
       29 // 读取键盘输入
       30 
       31 ::ZeroMemory(g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
       32 
       33 Device_Read(g_pKeyboardDevice, (LPVOID)g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
       34 
       35 
       36 
       37 
       38 
       39 
       40 
       41 
       42 
       43 // 按住鼠标左键并拖动,为平移操作
       44 
       45 static FLOAT fPosX = 0.0f, fPosY = 30.0f, fPosZ = 0.0f;
       46 
       47 if (g_diMouseState.rgbButtons[ 0] & 0x80)
       48 
       49 {
       50 
       51 fPosX += g_diMouseState.lX * 0.08f;
       52 
       53 fPosY += g_diMouseState.lY * -0.08f;
       54 
       55 }
       56 
       57 
       58 
       59 
       60 
       61 //鼠标滚轮,为观察点收缩操作
       62 
       63 fPosZ += g_diMouseState.lZ * 0.02f;
       64 
       65 
       66 
       67 
       68 
       69 // 平移物体
       70 
       71 if (g_pKeyStateBuffer[DIK_A] & 0x80) fPosX -= 0.005f;
       72 
       73 if (g_pKeyStateBuffer[DIK_D] & 0x80) fPosX += 0.005f;
       74 
       75 if (g_pKeyStateBuffer[DIK_W] & 0x80) fPosY += 0.005f;
       76 
       77 if (g_pKeyStateBuffer[DIK_S] & 0x80) fPosY -= 0.005f;
       78 
       79 
       80 
       81 
       82 
       83 
       84 
       85 
       86 
       87 D3DXMatrixTranslation(&g_matWorld, fPosX, fPosY, fPosZ);
       88 
       89 
       90 
       91 
       92 
       93 
       94 
       95 
       96 
       97 // 按住鼠标右键并拖动,为旋转操作
       98 
       99 static float fAngleX = 0.15f, fAngleY = -( float)D3DX_PI ;
      100 
      101 if (g_diMouseState.rgbButtons[ 1] & 0x80)
      102 
      103 {
      104 
      105 fAngleX += g_diMouseState.lY * -0.01f;
      106 
      107 fAngleY += g_diMouseState.lX * -0.01f;
      108 
      109 }
      110 
      111 // 旋转物体
      112 
      113 if (g_pKeyStateBuffer[DIK_UP] & 0x80) fAngleX += 0.005f;
      114 
      115 if (g_pKeyStateBuffer[DIK_DOWN] & 0x80) fAngleX -= 0.005f;
      116 
      117 if (g_pKeyStateBuffer[DIK_LEFT] & 0x80) fAngleY -= 0.005f;
      118 
      119 if (g_pKeyStateBuffer[DIK_RIGHT] & 0x80) fAngleY += 0.005f;
      120 
      121 
      122 
      123 
      124 
      125 
      126 
      127 
      128 
      129 D3DXMATRIX Rx, Ry;
      130 
      131 D3DXMatrixRotationX(&Rx, fAngleX);
      132 
      133 D3DXMatrixRotationY(&Ry, fAngleY);
      134 
      135 
      136 
      137 
      138 
      139 g_matWorld = Rx * Ry * g_matWorld;
      140 
      141 g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matWorld);
      142 
      143 Matrix_Set();
      144 
      145 }

            

    嗯,讲解完成,下面我们就贴出完整的详细注释的demo的源代码:

      1 //*****************************************************************************************
      2 
      3 //
      4 
      5 //【Visual C++】游戏开发笔记系列配套源码 四十二 浅墨DirectX教程之十 游戏输入控制利器 : DirectInput专场
      6 
      7 //    VS2010版
      8 
      9 // 2012年 1月27日 Create by 浅墨
     10 
     11 //图标及图片素材: 《仙剑奇侠传五前传》 瑕
     12 
     13 //此刻心情:既然选择了远方,便只顾风雨兼程
     14 
     15 //
     16 
     17 //*****************************************************************************************
     18 
     19 
     20 
     21 
     22 
     23 //*****************************************************************************************
     24 
     25 // Desc: 宏定义部分
     26 
     27 //*****************************************************************************************
     28 
     29 #define SCREEN_WIDTH    800    //为窗口宽度定义的宏,以方便在此处修改窗口宽度
     30 
     31 #define SCREEN_HEIGHT    600    //为窗口高度定义的宏,以方便在此处修改窗口高度
     32 
     33 #define WINDOW_TITLE    _T("【Visual C++游戏开发笔记】博文配套demo之四十二 浅墨DirectX教程之十 游戏输入控制利器 : DirectInput专场") //为窗口标题定义的宏
     34 
     35 #define DIRECTINPUT_VERSION 0x0800 //指定DirectInput版本,防止DIRECTINPUT_VERSION undefined警告
     36 
     37 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } //自定义一个SAFE_RELEASE()宏,便于COM资源的释放
     38 
     39 #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
     40 
     41 
     42 
     43 
     44 
     45 //*****************************************************************************************
     46 
     47 // Desc: 头文件定义部分
     48 
     49 //*****************************************************************************************
     50 
     51 #include <d3d9.h>
     52 
     53 #include <d3dx9.h>
     54 
     55 #include <tchar.h>
     56 
     57 #include <time.h>
     58 
     59 #include <dinput.h> // 使用DirectInput必须包含的头文件,注意这里没有8
     60 
     61 
     62 
     63 
     64 
     65 
     66 
     67 
     68 
     69 //*****************************************************************************************
     70 
     71 // Desc: 库文件定义部分
     72 
     73 //*****************************************************************************************
     74 
     75 #pragma comment(lib,"d3d9.lib")
     76 
     77 #pragma comment(lib,"d3dx9.lib")
     78 
     79 #pragma comment(lib, "dinput8.lib") // 使用DirectInput必须包含的头文件,注意这里有8
     80 
     81 #pragma comment(lib,"dxguid.lib")
     82 
     83 #pragma comment(lib, "winmm.lib")
     84 
     85 
     86 
     87 
     88 
     89 
     90 
     91 
     92 
     93 
     94 
     95 //*****************************************************************************************
     96 
     97 // Desc: 全局变量声明部分
     98 
     99 //*****************************************************************************************
    100 
    101 LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //Direct3D设备对象
    102 
    103 LPD3DXFONT    g_pTextFPS    = NULL; //字体COM接口
    104 
    105 LPD3DXFONT g_pTextAdaperName = NULL; // 显卡信息的2D文本
    106 
    107 LPD3DXFONT g_pTextHelper = NULL; // 帮助信息的2D文本
    108 
    109 LPD3DXFONT g_pTextInfor = NULL; // 绘制信息的2D文本
    110 
    111 float    g_FPS    = 0.0f; //一个浮点型的变量,代表帧速率
    112 
    113 wchar_t    g_strFPS[ 50]={ 0}; //包含帧速率的字符数组
    114 
    115 wchar_t    g_strAdapterName[ 60]={ 0}; //包含显卡名称的字符数组
    116 
    117 
    118 
    119 LPDIRECTINPUT8 g_pDirectInput = NULL; //
    120 
    121 LPDIRECTINPUTDEVICE8 g_pMouseDevice = NULL;
    122 
    123 DIMOUSESTATE g_diMouseState = { 0};
    124 
    125 LPDIRECTINPUTDEVICE8 g_pKeyboardDevice = NULL;
    126 
    127 char g_pKeyStateBuffer[ 256] = { 0};
    128 
    129 D3DXMATRIX g_matWorld; //世界矩阵
    130 
    131 
    132 
    133 LPD3DXMESH g_pMesh = NULL; // 网格的对象
    134 
    135 D3DMATERIAL9* g_pMaterials = NULL; // 网格的材质信息
    136 
    137 LPDIRECT3DTEXTURE9* g_pTextures = NULL; // 网格的纹理信息
    138 
    139 DWORD g_dwNumMtrls = 0; // 材质的数目
    140 
    141 
    142 
    143 //*****************************************************************************************
    144 
    145 // Desc: 全局函数声明部分
    146 
    147 //*****************************************************************************************
    148 
    149 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
    150 
    151 HRESULT    Direct3D_Init(HWND hwnd,HINSTANCE hInstance);
    152 
    153 HRESULT    Objects_Init();
    154 
    155 void    Direct3D_Render( HWND hwnd);
    156 
    157 void    Direct3D_Update( HWND hwnd);
    158 
    159 void    Direct3D_CleanUp( );
    160 
    161 float    Get_FPS();
    162 
    163 void    Matrix_Set();
    164 
    165 BOOL Device_Read(IDirectInputDevice8 *pDIDevice, void* pBuffer, long lSize) ;
    166 
    167 
    168 
    169 
    170 
    171 //*****************************************************************************************
    172 
    173 // Name: WinMain( )
    174 
    175 // Desc: Windows应用程序入口函数
    176 
    177 //*****************************************************************************************
    178 
    179 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
    180 
    181 {
    182 
    183 
    184 
    185 //开始设计一个完整的窗口类
    186 
    187 WNDCLASSEX wndClass = { 0 };    //用WINDCLASSEX定义了一个窗口类,即用wndClass实例化了WINDCLASSEX,用于之后窗口的各项初始化
    188 
    189 wndClass.cbSize = sizeof( WNDCLASSEX ) ;    //设置结构体的字节数大小
    190 
    191 wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
    192 
    193 wndClass.lpfnWndProc = WndProc;    //设置指向窗口过程函数的指针
    194 
    195 wndClass.cbClsExtra    = 0;
    196 
    197 wndClass.cbWndExtra    = 0;
    198 
    199 wndClass.hInstance = hInstance;    //指定包含窗口过程的程序的实例句柄。
    200 
    201 wndClass.hIcon=(HICON)::LoadImage( NULL,_T( "icon.ico"),IMAGE_ICON, 0, 0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //从全局的::LoadImage函数从本地加载自定义ico图标
    202 
    203 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。
    204 
    205 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个灰色画刷句柄
    206 
    207 wndClass.lpszMenuName = NULL;    //用一个以空终止的字符串,指定菜单资源的名字。
    208 
    209 wndClass.lpszClassName = _T( "ForTheDreamOfGameDevelop");    //用一个以空终止的字符串,指定窗口类的名字。
    210 
    211 
    212 
    213 if( !RegisterClassEx( &wndClass ) )    //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
    214 
    215 return -1;
    216 
    217 
    218 
    219 HWND hwnd = CreateWindow( _T( "ForTheDreamOfGameDevelop"),WINDOW_TITLE,    //喜闻乐见的创建窗口函数CreateWindow
    220 
    221 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, SCREEN_WIDTH,
    222 
    223 SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );
    224 
    225 
    226 
    227 
    228 
    229 //Direct3D资源的初始化,调用失败用messagebox予以显示
    230 
    231 if (!(S_OK==Direct3D_Init (hwnd,hInstance)))
    232 
    233 {
    234 
    235 MessageBox(hwnd, _T( "Direct3D初始化失败~!"), _T( "浅墨的消息窗口"), 0); //使用MessageBox函数,创建一个消息窗口
    236 
    237 }
    238 
    239 
    240 
    241 
    242 
    243 
    244 
    245 MoveWindow(hwnd, 200, 50,SCREEN_WIDTH,SCREEN_HEIGHT, true); //调整窗口显示时的位置,窗口左上角位于屏幕坐标(200,50)处
    246 
    247 ShowWindow( hwnd, nShowCmd ); //调用Win32函数ShowWindow来显示窗口
    248 
    249 UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样
    250 
    251 
    252 
    253 
    254 
    255 //消息循环过程
    256 
    257 MSG msg = { 0 }; //初始化msg
    258 
    259 while( msg.message != WM_QUIT )    //使用while循环
    260 
    261 {
    262 
    263 if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。
    264 
    265 {
    266 
    267 TranslateMessage( &msg );    //将虚拟键消息转换为字符消息
    268 
    269 DispatchMessage( &msg );    //该函数分发一个消息给窗口程序。
    270 
    271 }
    272 
    273 else
    274 
    275 {
    276 
    277 Direct3D_Update(hwnd); //调用更新函数,进行画面的更新
    278 
    279 Direct3D_Render(hwnd);    //调用渲染函数,进行画面的渲染
    280 
    281 }
    282 
    283 }
    284 
    285 
    286 
    287 UnregisterClass(_T( "ForTheDreamOfGameDevelop"), wndClass.hInstance);
    288 
    289 return 0;
    290 
    291 }
    292 
    293 
    294 
    295 
    296 
    297 
    298 
    299 //*****************************************************************************************
    300 
    301 // Name: WndProc()
    302 
    303 // Desc: 对窗口消息进行处理
    304 
    305 //*****************************************************************************************
    306 
    307 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) //窗口过程函数WndProc
    308 
    309 {
    310 
    311 switch( message )    //switch语句开始
    312 
    313 {
    314 
    315 case WM_PAINT:    // 客户区重绘消息
    316 
    317 Direct3D_Render(hwnd); //调用Direct3D_Render函数,进行画面的绘制
    318 
    319 ValidateRect(hwnd, NULL); // 更新客户区的显示
    320 
    321 break;    //跳出该switch语句
    322 
    323 
    324 
    325 case WM_KEYDOWN: // 键盘按下消息
    326 
    327 if (wParam == VK_ESCAPE) // ESC键
    328 
    329 DestroyWindow(hwnd); // 销毁窗口, 并发送一条WM_DESTROY消息
    330 
    331 break;
    332 
    333 case WM_DESTROY:    //窗口销毁消息
    334 
    335 Direct3D_CleanUp(); //调用Direct3D_CleanUp函数,清理COM接口对象
    336 
    337 PostQuitMessage( 0 );    //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
    338 
    339 break;    //跳出该switch语句
    340 
    341 
    342 
    343 default:    //若上述case条件都不符合,则执行该default语句
    344 
    345 return DefWindowProc( hwnd, message, wParam, lParam );    //调用缺省的窗口过程来为应用程序没有处理的窗口消息提供缺省的处理。
    346 
    347 }
    348 
    349 
    350 
    351 return 0;    //正常退出
    352 
    353 }
    354 
    355 
    356 
    357 
    358 
    359 //*****************************************************************************************
    360 
    361 // Name: Direct3D_Init( )
    362 
    363 // Desc: 初始化Direct3D
    364 
    365 // Point:【Direct3D初始化四步曲】
    366 
    367 //    1.初始化四步曲之一,创建Direct3D接口对象
    368 
    369 //    2.初始化四步曲之二,获取硬件设备信息
    370 
    371 //    3.初始化四步曲之三,填充结构体
    372 
    373 //    4.初始化四步曲之四,创建Direct3D设备接口
    374 
    375 //*****************************************************************************************
    376 
    377 
    378 
    379 HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance)
    380 
    381 {
    382 
    383 
    384 
    385 //--------------------------------------------------------------------------------------
    386 
    387 // 【Direct3D初始化四步曲之一,创接口】:创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象
    388 
    389 //--------------------------------------------------------------------------------------
    390 
    391 LPDIRECT3D9 pD3D = NULL; //Direct3D接口对象的创建
    392 
    393 if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商
    394 
    395 return E_FAIL;
    396 
    397 
    398 
    399 //--------------------------------------------------------------------------------------
    400 
    401 // 【Direct3D初始化四步曲之二,取信息】:获取硬件设备信息
    402 
    403 //--------------------------------------------------------------------------------------
    404 
    405 D3DCAPS9 caps; int vp = 0;
    406 
    407 if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
    408 
    409 {
    410 
    411 return E_FAIL;
    412 
    413 }
    414 
    415 if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
    416 
    417 vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //支持硬件顶点运算,我们就采用硬件顶点运算,妥妥的
    418 
    419 else
    420 
    421 vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算
    422 
    423 
    424 
    425 //--------------------------------------------------------------------------------------
    426 
    427 // 【Direct3D初始化四步曲之三,填内容】:填充D3DPRESENT_PARAMETERS结构体
    428 
    429 //--------------------------------------------------------------------------------------
    430 
    431 D3DPRESENT_PARAMETERS d3dpp;
    432 
    433 ZeroMemory(&d3dpp, sizeof(d3dpp));
    434 
    435 d3dpp.BackBufferWidth = SCREEN_WIDTH;
    436 
    437 d3dpp.BackBufferHeight = SCREEN_HEIGHT;
    438 
    439 d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
    440 
    441 d3dpp.BackBufferCount = 2;
    442 
    443 d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
    444 
    445 d3dpp.MultiSampleQuality = 0;
    446 
    447 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    448 
    449 d3dpp.hDeviceWindow = hwnd;
    450 
    451 d3dpp.Windowed = true;
    452 
    453 d3dpp.EnableAutoDepthStencil = true;
    454 
    455 d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
    456 
    457 d3dpp.Flags = 0;
    458 
    459 d3dpp.FullScreen_RefreshRateInHz = 0;
    460 
    461 d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
    462 
    463 
    464 
    465 //--------------------------------------------------------------------------------------
    466 
    467 // 【Direct3D初始化四步曲之四,创设备】:创建Direct3D设备接口
    468 
    469 //--------------------------------------------------------------------------------------
    470 
    471 if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
    472 
    473 hwnd, vp, &d3dpp, &g_pd3dDevice)))
    474 
    475 return E_FAIL;
    476 
    477 
    478 
    479 
    480 
    481 //获取显卡信息到g_strAdapterName中,并在显卡名称之前加上“当前显卡型号:”字符串
    482 
    483 wchar_t TempName[ 60]= L"当前显卡型号:"; //定义一个临时字符串,且方便了把"当前显卡型号:"字符串引入我们的目的字符串中
    484 
    485 D3DADAPTER_IDENTIFIER9 Adapter; //定义一个D3DADAPTER_IDENTIFIER9结构体,用于存储显卡信息
    486 
    487 pD3D->GetAdapterIdentifier( 0, 0,&Adapter); //调用GetAdapterIdentifier,获取显卡信息
    488 
    489 int len = MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, NULL, 0); //显卡名称现在已经在Adapter.Description中了,但是其为char类型,我们要将其转为wchar_t类型
    490 
    491 MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len); //这步操作完成后,g_strAdapterName中就为当前我们的显卡类型名的wchar_t型字符串了
    492 
    493 wcscat_s(TempName,g_strAdapterName); //把当前我们的显卡名加到“当前显卡型号:”字符串后面,结果存在TempName中
    494 
    495 wcscpy_s(g_strAdapterName,TempName); //把TempName中的结果拷贝到全局变量g_strAdapterName中,大功告成~
    496 
    497 
    498 
    499 
    500 
    501 
    502 
    503 //--------------------------------------------------------------------------------------
    504 
    505 // 【DirectInput使用五步曲的前三步】:创设备,设格式,拿权力。在为鼠标设备初始化
    506 
    507 //--------------------------------------------------------------------------------------
    508 
    509 // 创建DirectInput接口和设备
    510 
    511 DirectInput8Create(hInstance, 0x0800, IID_IDirectInput8, ( void**)&g_pDirectInput, NULL);
    512 
    513 g_pDirectInput->CreateDevice(GUID_SysKeyboard, &g_pMouseDevice, NULL);
    514 
    515 
    516 
    517 // 设置数据格式和协作级别
    518 
    519 g_pDirectInput->CreateDevice(GUID_SysMouse, &g_pMouseDevice, NULL);
    520 
    521 g_pMouseDevice->SetDataFormat(&c_dfDIMouse);
    522 
    523 
    524 
    525 //获取设备控制权
    526 
    527 g_pMouseDevice->Acquire();
    528 
    529 
    530 
    531 //--------------------------------------------------------------------------------------
    532 
    533 // 【DirectInput使用五步曲的前三步】:创设备,设格式,拿权力。在为键盘设备初始化
    534 
    535 //--------------------------------------------------------------------------------------
    536 
    537 // 创建DirectInput接口和设备
    538 
    539 DirectInput8Create(hInstance, 0x0800, IID_IDirectInput8, ( void**)&g_pDirectInput, NULL);
    540 
    541 g_pDirectInput->CreateDevice(GUID_SysKeyboard, &g_pKeyboardDevice, NULL);
    542 
    543 
    544 
    545 // 设置数据格式和协作级别
    546 
    547 g_pKeyboardDevice->SetDataFormat(&c_dfDIKeyboard);
    548 
    549 g_pKeyboardDevice->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    550 
    551 
    552 
    553 //获取设备控制权
    554 
    555 g_pKeyboardDevice->Acquire();
    556 
    557 
    558 
    559 
    560 
    561 if(!(S_OK==Objects_Init())) return E_FAIL;
    562 
    563 
    564 
    565 SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命完成,我们将其释放掉
    566 
    567 
    568 
    569 return S_OK;
    570 
    571 }
    572 
    573 
    574 
    575 
    576 
    577 HRESULT Objects_Init()
    578 
    579 {
    580 
    581 //创建字体
    582 
    583 D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET,
    584 
    585 OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T( "Calibri"), &g_pTextFPS);
    586 
    587 D3DXCreateFont(g_pd3dDevice, 20, 0, 1000, 0, false, DEFAULT_CHARSET,
    588 
    589 OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"华文中宋", &g_pTextAdaperName);
    590 
    591 D3DXCreateFont(g_pd3dDevice, 23, 0, 1000, 0, false, DEFAULT_CHARSET,
    592 
    593 OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"微软雅黑", &g_pTextHelper);
    594 
    595 D3DXCreateFont(g_pd3dDevice, 26, 0, 1000, 0, false, DEFAULT_CHARSET,
    596 
    597 OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"黑体", &g_pTextInfor);
    598 
    599 
    600 
    601 // 从X文件中加载网格数据
    602 
    603 LPD3DXBUFFER pAdjBuffer = NULL;
    604 
    605 LPD3DXBUFFER pMtrlBuffer = NULL;
    606 
    607 D3DXLoadMeshFromX( L"loli.x", D3DXMESH_MANAGED, g_pd3dDevice,
    608 
    609 &pAdjBuffer, &pMtrlBuffer, NULL, &g_dwNumMtrls, &g_pMesh);
    610 
    611 
    612 
    613 // 读取材质和纹理数据
    614 
    615 D3DXMATERIAL *pMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();
    616 
    617 g_pMaterials = new D3DMATERIAL9[g_dwNumMtrls];
    618 
    619 g_pTextures = new LPDIRECT3DTEXTURE9[g_dwNumMtrls];
    620 
    621 
    622 
    623 for (DWORD i= 0; i<g_dwNumMtrls; i++)
    624 
    625 {
    626 
    627 g_pMaterials[i] = pMtrls[i].MatD3D;
    628 
    629 g_pMaterials[i].Ambient = g_pMaterials[i].Diffuse;
    630 
    631 g_pTextures[i] = NULL;
    632 
    633 D3DXCreateTextureFromFileA(g_pd3dDevice, pMtrls[i].pTextureFilename, &g_pTextures[i]);
    634 
    635 }
    636 
    637 pAdjBuffer->Release();
    638 
    639 pMtrlBuffer->Release();
    640 
    641 
    642 
    643 
    644 
    645 // 设置渲染状态
    646 
    647 g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); //开启背面消隐
    648 
    649 g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f)); //设置环境光
    650 
    651 
    652 
    653 g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); //设置为线性纹理过滤
    654 
    655 g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    656 
    657 
    658 
    659 
    660 
    661 return S_OK;
    662 
    663 }
    664 
    665 
    666 
    667 
    668 
    669 //*****************************************************************************************
    670 
    671 // Name:Matrix_Set()
    672 
    673 // Desc: 设置世界矩阵
    674 
    675 // Point:【Direct3D四大变换】
    676 
    677 //    1.【四大变换之一】:世界变换矩阵的设置
    678 
    679 //    2.【四大变换之二】:取景变换矩阵的设置
    680 
    681 //    3.【四大变换之三】:投影变换矩阵的设置
    682 
    683 //    4.【四大变换之四】:视口变换的设置
    684 
    685 //*****************************************************************************************
    686 
    687 void Matrix_Set()
    688 
    689 {
    690 
    691 //--------------------------------------------------------------------------------------
    692 
    693 //【四大变换之一】:世界变换矩阵的设置
    694 
    695 //--------------------------------------------------------------------------------------
    696 
    697 
    698 
    699 
    700 
    701 //--------------------------------------------------------------------------------------
    702 
    703 //【四大变换之二】:取景变换矩阵的设置
    704 
    705 //--------------------------------------------------------------------------------------
    706 
    707 D3DXMATRIX matView; //定义一个矩阵
    708 
    709 D3DXVECTOR3 vEye(0.0f, 0.0f, -250.0f); //摄像机的位置
    710 
    711 D3DXVECTOR3 vAt(0.0f, 0.0f, 0.0f); //观察点的位置
    712 
    713 D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f); //向上的向量
    714 
    715 D3DXMatrixLookAtLH(&matView, &vEye, &vAt, &vUp); //计算出取景变换矩阵
    716 
    717 g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //应用取景变换矩阵
    718 
    719 
    720 
    721 //--------------------------------------------------------------------------------------
    722 
    723 //【四大变换之三】:投影变换矩阵的设置
    724 
    725 //--------------------------------------------------------------------------------------
    726 
    727 D3DXMATRIX matProj; //定义一个矩阵
    728 
    729 D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f,( float)(( double)SCREEN_WIDTH/SCREEN_HEIGHT), 1.0f, 1000.0f); //计算投影变换矩阵
    730 
    731 g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); //设置投影变换矩阵
    732 
    733 
    734 
    735 //--------------------------------------------------------------------------------------
    736 
    737 //【四大变换之四】:视口变换的设置
    738 
    739 //--------------------------------------------------------------------------------------
    740 
    741 D3DVIEWPORT9 vp; //实例化一个D3DVIEWPORT9结构体,然后做填空题给各个参数赋值就可以了
    742 
    743 vp.X = 0;    //表示视口相对于窗口的X坐标
    744 
    745 vp.Y = 0;    //视口相对对窗口的Y坐标
    746 
    747 vp.Width = SCREEN_WIDTH;    //视口的宽度
    748 
    749 vp.Height = SCREEN_HEIGHT; //视口的高度
    750 
    751 vp.MinZ = 0.0f; //视口在深度缓存中的最小深度值
    752 
    753 vp.MaxZ = 1.0f;    //视口在深度缓存中的最大深度值
    754 
    755 g_pd3dDevice->SetViewport(&vp); //视口的设置
    756 
    757 
    758 
    759 }
    760 
    761 
    762 
    763 
    764 
    765 void    Direct3D_Update( HWND hwnd)
    766 
    767 {
    768 
    769 // 获取键盘消息并给予设置相应的填充模式
    770 
    771 if (g_pKeyStateBuffer[DIK_1] & 0x80) // 若数字键1被按下,进行实体填充
    772 
    773 g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
    774 
    775 if (g_pKeyStateBuffer[DIK_2] & 0x80) // 若数字键2被按下,进行线框填充
    776 
    777 g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
    778 
    779 
    780 
    781 // 读取鼠标输入
    782 
    783 ::ZeroMemory(&g_diMouseState, sizeof(g_diMouseState));
    784 
    785 Device_Read(g_pMouseDevice, (LPVOID)&g_diMouseState, sizeof(g_diMouseState));
    786 
    787 
    788 
    789 // 读取键盘输入
    790 
    791 ::ZeroMemory(g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
    792 
    793 Device_Read(g_pKeyboardDevice, (LPVOID)g_pKeyStateBuffer, sizeof(g_pKeyStateBuffer));
    794 
    795 
    796 
    797 
    798 
    799 // 按住鼠标左键并拖动,为平移操作
    800 
    801 static FLOAT fPosX = 0.0f, fPosY = 30.0f, fPosZ = 0.0f;
    802 
    803 if (g_diMouseState.rgbButtons[ 0] & 0x80)
    804 
    805 {
    806 
    807 fPosX += g_diMouseState.lX * 0.08f;
    808 
    809 fPosY += g_diMouseState.lY * -0.08f;
    810 
    811 }
    812 
    813 
    814 
    815 //鼠标滚轮,为观察点收缩操作
    816 
    817 fPosZ += g_diMouseState.lZ * 0.02f;
    818 
    819 
    820 
    821 // 平移物体
    822 
    823 if (g_pKeyStateBuffer[DIK_A] & 0x80) fPosX -= 0.005f;
    824 
    825 if (g_pKeyStateBuffer[DIK_D] & 0x80) fPosX += 0.005f;
    826 
    827 if (g_pKeyStateBuffer[DIK_W] & 0x80) fPosY += 0.005f;
    828 
    829 if (g_pKeyStateBuffer[DIK_S] & 0x80) fPosY -= 0.005f;
    830 
    831 
    832 
    833 
    834 
    835 D3DXMatrixTranslation(&g_matWorld, fPosX, fPosY, fPosZ);
    836 
    837 
    838 
    839 
    840 
    841 // 按住鼠标右键并拖动,为旋转操作
    842 
    843 static float fAngleX = 0.15f, fAngleY = -( float)D3DX_PI ;
    844 
    845 if (g_diMouseState.rgbButtons[ 1] & 0x80)
    846 
    847 {
    848 
    849 fAngleX += g_diMouseState.lY * -0.01f;
    850 
    851 fAngleY += g_diMouseState.lX * -0.01f;
    852 
    853 }
    854 
    855 // 旋转物体
    856 
    857 if (g_pKeyStateBuffer[DIK_UP] & 0x80) fAngleX += 0.005f;
    858 
    859 if (g_pKeyStateBuffer[DIK_DOWN] & 0x80) fAngleX -= 0.005f;
    860 
    861 if (g_pKeyStateBuffer[DIK_LEFT] & 0x80) fAngleY -= 0.005f;
    862 
    863 if (g_pKeyStateBuffer[DIK_RIGHT] & 0x80) fAngleY += 0.005f;
    864 
    865 
    866 
    867 
    868 
    869 D3DXMATRIX Rx, Ry;
    870 
    871 D3DXMatrixRotationX(&Rx, fAngleX);
    872 
    873 D3DXMatrixRotationY(&Ry, fAngleY);
    874 
    875 
    876 
    877 g_matWorld = Rx * Ry * g_matWorld;
    878 
    879 g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matWorld);
    880 
    881 Matrix_Set();
    882 
    883 }
    884 
    885 
    886 
    887 
    888 
    889 
    890 
    891 //*****************************************************************************************
    892 
    893 // Name: Direct3D_Render()
    894 
    895 // Desc: 进行图形的渲染操作
    896 
    897 // Point:【Direct3D渲染五步曲】
    898 
    899 //    1.渲染五步曲之一,清屏操作
    900 
    901 //    2.渲染五步曲之二,开始绘制
    902 
    903 //    3.渲染五步曲之三,正式绘制
    904 
    905 //    4.渲染五步曲之四,结束绘制
    906 
    907 //    5.渲染五步曲之五,翻转显示
    908 
    909 //*****************************************************************************************
    910 
    911 
    912 
    913 void Direct3D_Render(HWND hwnd)
    914 
    915 {
    916 
    917 
    918 
    919 //--------------------------------------------------------------------------------------
    920 
    921 // 【Direct3D渲染五步曲之一】:清屏操作
    922 
    923 //--------------------------------------------------------------------------------------
    924 
    925 g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 0, 0, 0), 1.0f, 0);
    926 
    927 
    928 
    929 //定义一个矩形,用于获取主窗口矩形
    930 
    931 RECT formatRect;
    932 
    933 GetClientRect(hwnd, &formatRect);
    934 
    935 
    936 
    937 //--------------------------------------------------------------------------------------
    938 
    939 // 【Direct3D渲染五步曲之二】:开始绘制
    940 
    941 //--------------------------------------------------------------------------------------
    942 
    943 g_pd3dDevice->BeginScene(); // 开始绘制
    944 
    945 
    946 
    947 //--------------------------------------------------------------------------------------
    948 
    949 // 【Direct3D渲染五步曲之三】:正式绘制,利用顶点缓存绘制图形
    950 
    951 //--------------------------------------------------------------------------------------
    952 
    953 
    954 
    955 // 绘制网格
    956 
    957 for (DWORD i = 0; i < g_dwNumMtrls; i++)
    958 
    959 {
    960 
    961 g_pd3dDevice->SetMaterial(&g_pMaterials[i]);
    962 
    963 g_pd3dDevice->SetTexture( 0, g_pTextures[i]);
    964 
    965 g_pMesh->DrawSubset(i);
    966 
    967 }
    968 
    969 
    970 
    971 //在窗口右上角处,显示每秒帧数
    972 
    973 int charCount = swprintf_s(g_strFPS, 20, _T( "FPS:%0.3f"), Get_FPS() );
    974 
    975 g_pTextFPS->DrawText( NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_RGBA( 0, 239, 136, 255));
    976 
    977 
    978 
    979 //显示显卡类型名
    View Code
  • 相关阅读:
    函数7—递归
    函数6—内置函数
    PHP.6-PHP环境搭建(Windows环境下)-LAMP
    PHP.5-DIV+CSS布局网站首页实例
    PHP.4-DIV+CSS标准网页布局准备工作(下)
    PHP.3-DIV+CSS标准网页布局准备工作(上)
    PHP.2-LAMP平台介绍及网站的工作原理
    小白日记36:kali渗透测试之Web渗透-手动漏洞挖掘(二)-突破身份认证,操作系统任意命令执行漏洞
    PHP.1-网站开发概述
    小白日记35:kali渗透测试之Web渗透-手动漏洞挖掘(一)-默认安装引发的漏洞
  • 原文地址:https://www.cnblogs.com/szmtjs10/p/16110344.html
Copyright © 2020-2023  润新知