C++教程:DirectX11Frame
創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),金臺(tái)企業(yè)網(wǎng)站建設(shè),金臺(tái)品牌網(wǎng)站建設(shè),網(wǎng)站定制,金臺(tái)網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,金臺(tái)網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
前面幾講的內(nèi)容過(guò)于難了一些,當(dāng)然看不懂沒(méi)關(guān)系,因?yàn)樵谡麄€(gè)框架里面他們只是作為底層應(yīng)用來(lái)使用,而最終暴露的接口都相當(dāng)?shù)囊子谑褂?,那么,?duì)于拿來(lái)主義這就足夠了,因?yàn)榻酉聛?lái)的內(nèi)容都注定會(huì)是一馬平川。
回顧我們前面說(shuō)過(guò)的內(nèi)容:一套反射機(jī)制(對(duì)于這種非主流的GUI庫(kù)來(lái)說(shuō)沒(méi)有反射靠硬編碼來(lái)實(shí)現(xiàn)界面配置的話那么結(jié)果會(huì)是有些吃力不討好),一套事件系統(tǒng)(新的事件機(jī)制沒(méi)有推送,以前我們使用的事件響應(yīng)是依賴于boost的signal,不過(guò)boost的signal后來(lái)被我廢棄了,所以現(xiàn)在的事件響應(yīng)是一套全新的方案,當(dāng)然也不算是全新的了,因?yàn)榇笾陆Y(jié)構(gòu)是模仿C#的事件),一套完備的屬性系統(tǒng)(微信里面推送的是當(dāng)初設(shè)計(jì)這個(gè)屬性的一個(gè)版本,它的缺點(diǎn)我們已經(jīng)說(shuō)過(guò),至于第二個(gè)版本沒(méi)有在微信里進(jìn)行推送,第二個(gè)版本不但支持多槽函數(shù)進(jìn)行綁定還支持同步和異步兩種連接方式,當(dāng)然最重要的還實(shí)現(xiàn)了對(duì)象有效性的追蹤),一個(gè)基礎(chǔ)對(duì)象MObject(該對(duì)象繼承至屬性和反射,所以凡是MObject的子類都可以實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象以及除了使用新的事件機(jī)制向外部發(fā)送事件外還能夠使用屬性的操作方式和一些需要接收事件的槽函數(shù)進(jìn)行同步)。
好吧,簡(jiǎn)單的總結(jié)了一些我們前面說(shuō)過(guò)的一些東西后下面我們繼續(xù)向前,如同前面我們說(shuō)過(guò),DirectX本身并沒(méi)有窗口的概念,他需要依附在特定的窗口上面才能夠進(jìn)行可視化的繪制,而標(biāo)識(shí)這個(gè)窗口的東西就是HWND。雖然當(dāng)初這個(gè)Frame使用win32來(lái)實(shí)現(xiàn)的,但后來(lái)被應(yīng)用在Qt里面(公司使用的是Qt)進(jìn)行項(xiàng)目開(kāi)發(fā),所以該Frame的設(shè)計(jì)就是支持跨平臺(tái)的(不是指操作系統(tǒng),而是指在Windows下面的開(kāi)發(fā)平臺(tái)),只要能夠獲取到HWND就能夠使用這套Frame。
為了達(dá)到上面的效果,我們有必要來(lái)設(shè)計(jì)一個(gè)基礎(chǔ)窗口類,它提供了一些必須的接口以及一些共有的實(shí)現(xiàn),這個(gè)類就是MAppWindow.
//===============================
#pragma once
#include "MDx11Comm.h"
#include "MObject.h"
class MAppWindow : public MObject
{
public:
//
// 鼠標(biāo)消息函數(shù)
// 鼠標(biāo)按鍵,鼠標(biāo)動(dòng)作,x,y,是否為全局坐標(biāo)
//
//
typedef std::function MouseFunType;
typedef std::function ADD_INIT; // 附加初始化函數(shù)
typedef std::function RECALL_INIT; // 初始化函數(shù)
typedef std::function RECALL_DRAW; // 繪圖函數(shù)
typedef std::function RECALL_CLEAR; // 清理資源函數(shù)
typedef std::function RECALL_RESHAPE; // 更改尺寸處理函數(shù)
typedef std::function RECALL_WNDPROC; // 底層消息回調(diào)處理函數(shù)
typedef std::function RECALL_IDLE; // 空閑處理函數(shù)
typedef std::function RECALL_UPDATA; // 動(dòng)畫(huà)更新函數(shù)
typedef std::function InputFunType; // 輸入回掉函數(shù)
typedef std::function KeyEventFunType; // 鍵盤(pán)狀態(tài)回調(diào)函數(shù)
typedef std::function KeyInputCharFunType;// 處理輸入字符函數(shù)
public:
MAppWindow();
virtual ~MAppWindow();
//
// 獲取窗口句柄
//
virtual HWND WindHwnd(){ return nullptr; }
//
// 移除窗口邊框
//
virtual void RemoveBorder(){}
//
// 注冊(cè)函數(shù)
//
void RegisterFunInitD3D(RECALL_INIT initGLfun);
void RegisterDrawScreen(RECALL_DRAW drawFun); // 注冊(cè)顯示函數(shù),很重要,沒(méi)他,就顯示不出3D畫(huà)面
void RegisterReShape(RECALL_RESHAPE reshapeFun); // 移動(dòng)窗口會(huì)被調(diào)用
void RegisterWndProc(RECALL_WNDPROC WndProc); // 注冊(cè)事件回調(diào)函數(shù),該函數(shù)將會(huì)相應(yīng)窗口消息
void RegisterAddProcFun(RECALL_WNDPROC WndProc);
void RegisterOnIdleFun(RECALL_IDLE idlefun); // 注冊(cè)系統(tǒng)閑時(shí)處理函數(shù)
void RegisterClearFun(RECALL_CLEAR clearFun); // 注冊(cè)清除資源函數(shù)
void RegisterUpdataFun(RECALL_UPDATA updatafun) // 注冊(cè)更新界面函數(shù)
void RegisterMouseEnventFun(MouseFunType fun); // 注冊(cè)鼠標(biāo)事件函數(shù)
protected:
MouseFunType mMouseEventFun{ nullptr };
RECALL_INIT mInitD3d;
RECALL_DRAW mDrawScreen;
RECALL_RESHAPE mReshape;
RECALL_CLEAR mClearFun;
RECALL_IDLE mIdleFun;
RECALL_UPDATA mUpdateFun;
};
//==========================================
注冊(cè)系列函數(shù)我們已經(jīng)實(shí)現(xiàn),唯一需要用戶自行實(shí)現(xiàn)的就只有WindHwnd和RemoveBorder這兩個(gè)函數(shù),對(duì)于使用Win32或者M(jìn)FC的同學(xué)來(lái)說(shuō)獲取這個(gè)HWND實(shí)在是太簡(jiǎn)單了,對(duì)于使用Qt的同學(xué)來(lái)說(shuō)這兩個(gè)函數(shù)的實(shí)現(xiàn)依然很簡(jiǎn)單,對(duì)了,為什么我們要實(shí)現(xiàn)RemoveBorder呢?看看我們文章開(kāi)頭的圖片,整個(gè)窗口全都是使用DirectX繪制出來(lái)的,包括標(biāo)題欄和邊框,這樣我們就可以實(shí)現(xiàn)我們想要的任意風(fēng)格了,否則就算你把Client區(qū)域做得多華麗但是一看窗口邊框和標(biāo)題欄就會(huì)不自覺(jué)的認(rèn)為這不是一個(gè)風(fēng)格的。
由于我們現(xiàn)在是在win32下,所以這里我們可以使用MAppWindow來(lái)作為我們真正的窗口基類。真正的win32窗口類MWindow:
//==================================
#pragma once
#include "MDx11String.h"
#include
#include "MNoCopy.h"
#include
#include "MAppWindow.h"
using namespace MDx11;
class MWindow;
class MEventFun;
//
// 消息回調(diào)函數(shù)
//
HRESULT __stdcall WndProc(HWND, UINT, WPARAM, LPARAM);
class MWindow : public MAppWindow, public MNoCopy
{
DECLARE_CLASS(MWindow)
public:
explicit MWindow(MWindow* parent = nullptr);
virtual ~MWindow();
public:
unsigned Width() const{ return mWidth; }
unsigned Height() const{ return mHeight; }
void SetTitle(const MDx11String& Title);
//
// 重寫(xiě)繼承而來(lái)的兩個(gè)虛函數(shù)
//
void RemoveBorder();
virtual HWND WindHwnd(){ return mHwnd; }
//
// 開(kāi)啟消息循環(huán)
//
int Run() const;
//
// 顯示窗口
//
void Show();
virtual void SetExStyle(DWORD dwExStyle);
virtual void SetStyle(DWORD dwStyle);
virtual void SetIsFullScreen(bool fullscreen);
virtual void Update(){ InvalidateRect(*this, nullptr, false); }
virtual operator HWND() const{ return mHwnd; }
virtual LRESULT __stdcall MemWndProc(HWND, UINT, WPARAM, LPARAM);
bool IsFullScreen(){ return bIsFullscreen; }
bool IsActive(){ return bIsActive; }
void CalculateFrameStats();
//
// 事件
//
public:
friend LRESULT __stdcall WndProc(HWND, UINT, WPARAM, LPARAM); // 窗口回調(diào)函數(shù)
//
// 定義幾個(gè)事件屬性
//
static MDx11String MouseClickedEvent;
static MDx11String MouseMoveEvent;
static MDx11String MouseEnterEvent;
static MDx11String MouseLeverEvent;
static MDx11String ContentChangedEvent;
static MDx11String SelectedChangedEvent;
void RegisterEventFun(const MDx11String& EventDesc, MEventFun* SlotFun);
protected:
virtual bool InitWindow();
bool RegisterWndClass(); // 注冊(cè)窗口類
bool GenWindow(); // 創(chuàng)建窗口
virtual int Msgloop() const; // 消息循環(huán)
virtual void MouseEnterWindow();
virtual void MouseLeavesWindow();
//
// 窗口消息經(jīng)回調(diào)函數(shù)轉(zhuǎn)而進(jìn)相關(guān)的成員函數(shù),方便操作成員數(shù)據(jù)
//
LRESULT __stdcall BaseWndProc(HWND, UINT, WPARAM, LPARAM);
RECALL_WNDPROC mAddFun; // 消息回調(diào)附加函數(shù)
private:
MDx11String mTitle;
HWND mHwnd;
unsigned mWidth;
unsigned mHeight;
DWORD mDwExStyle; // 窗口風(fēng)格
DWORD mDwStyle; // 窗口風(fēng)格
bool* bIsKeys; //監(jiān)控鍵狀態(tài)
bool bIsInited;
volatile bool bIsDone;
bool bIsFullscreen;
bool bIsActive;
bool bIsAppPaused;
bool bIsMinimized;
bool bIsMaximized;
bool bIsResizing;
WNDCLASS mWndclass;
MDx11String mWndClassName;
//
// 一個(gè)計(jì)時(shí)器,凡是以I開(kāi)頭的東西都是從com組件中導(dǎo)出來(lái)的
//
ITimer* pTimer{ nullptr };
//
// 記錄鼠標(biāo)是否進(jìn)入窗口
//
bool bIsEnter{ false };
//
// 保存消息響應(yīng)函數(shù)
//
std::unordered_map mEventFunMap;
};
//=====================================
這個(gè)類沒(méi)啥多說(shuō)的,唯一的技巧性操作是將回調(diào)函數(shù)過(guò)程轉(zhuǎn)發(fā)指成員函數(shù)身上,要解決這個(gè)問(wèn)題很簡(jiǎn)單,正cpp文件中創(chuàng)建一個(gè)全局窗口對(duì)象,在類的構(gòu)造函數(shù)中將this直接賦給該全局指針,然后在回掉函數(shù)中根據(jù)HWND來(lái)檢查出對(duì)應(yīng)的窗口,從而將事件轉(zhuǎn)發(fā)指相應(yīng)的成員函數(shù)身上(其實(shí)如果只是簡(jiǎn)單的實(shí)現(xiàn)一個(gè)窗口大不必要這么麻煩,之所以這么干是考慮到以后該窗口類的擴(kuò)展性)。
現(xiàn)在只需要下面的代碼窗口就出來(lái)了:
//=====================================
MWindow w; //創(chuàng)建一個(gè)對(duì)象
w.SetTitle("Hello World"); // 設(shè)置窗口標(biāo)題
w.Show(); // 顯示窗口
w.Run(); // 開(kāi)啟消息循環(huán)
//=======================================
ok,今天的就到這里吧,具體實(shí)現(xiàn)因?yàn)楸容^簡(jiǎn)單就比貼出來(lái)了,只需要知道這些類有這些接口以及相關(guān)成員就足夠了,接下來(lái)我們開(kāi)始真正的進(jìn)入DirectX,下一講我們來(lái)說(shuō)說(shuō)該類的渲染核心——DirectX11的初始化。