您好,登錄后才能下訂單哦!
C++教程:DirectX11Frame
前面幾講的內容過于難了一些,當然看不懂沒關系,因為在整個框架里面他們只是作為底層應用來使用,而最終暴露的接口都相當的易于使用,那么,對于拿來主義這就足夠了,因為接下來的內容都注定會是一馬平川。
回顧我們前面說過的內容:一套反射機制(對于這種非主流的GUI庫來說沒有反射靠硬編碼來實現界面配置的話那么結果會是有些吃力不討好),一套事件系統(新的事件機制沒有推送,以前我們使用的事件響應是依賴于boost的signal,不過boost的signal后來被我廢棄了,所以現在的事件響應是一套全新的方案,當然也不算是全新的了,因為大致結構是模仿C#的事件),一套完備的屬性系統(微信里面推送的是當初設計這個屬性的一個版本,它的缺點我們已經說過,至于第二個版本沒有在微信里進行推送,第二個版本不但支持多槽函數進行綁定還支持同步和異步兩種連接方式,當然最重要的還實現了對象有效性的追蹤),一個基礎對象MObject(該對象繼承至屬性和反射,所以凡是MObject的子類都可以實現動態創建對象以及除了使用新的事件機制向外部發送事件外還能夠使用屬性的操作方式和一些需要接收事件的槽函數進行同步)。
好吧,簡單的總結了一些我們前面說過的一些東西后下面我們繼續向前,如同前面我們說過,DirectX本身并沒有窗口的概念,他需要依附在特定的窗口上面才能夠進行可視化的繪制,而標識這個窗口的東西就是HWND。雖然當初這個Frame使用win32來實現的,但后來被應用在Qt里面(公司使用的是Qt)進行項目開發,所以該Frame的設計就是支持跨平臺的(不是指操作系統,而是指在Windows下面的開發平臺),只要能夠獲取到HWND就能夠使用這套Frame。
為了達到上面的效果,我們有必要來設計一個基礎窗口類,它提供了一些必須的接口以及一些共有的實現,這個類就是MAppWindow.
//===============================
#pragma once
#include "MDx11Comm.h"
#include "MObject.h"
class MAppWindow : public MObject
{
public:
//
// 鼠標消息函數
// 鼠標按鍵,鼠標動作,x,y,是否為全局坐標
//
//
typedef std::function MouseFunType;
typedef std::function ADD_INIT; // 附加初始化函數
typedef std::function RECALL_INIT; // 初始化函數
typedef std::function RECALL_DRAW; // 繪圖函數
typedef std::function RECALL_CLEAR; // 清理資源函數
typedef std::function RECALL_RESHAPE; // 更改尺寸處理函數
typedef std::function RECALL_WNDPROC; // 底層消息回調處理函數
typedef std::function RECALL_IDLE; // 空閑處理函數
typedef std::function RECALL_UPDATA; // 動畫更新函數
typedef std::function InputFunType; // 輸入回掉函數
typedef std::function KeyEventFunType; // 鍵盤狀態回調函數
typedef std::function KeyInputCharFunType;// 處理輸入字符函數
public:
MAppWindow();
virtual ~MAppWindow();
//
// 獲取窗口句柄
//
virtual HWND WindHwnd(){ return nullptr; }
//
// 移除窗口邊框
//
virtual void RemoveBorder(){}
//
// 注冊函數
//
void RegisterFunInitD3D(RECALL_INIT initGLfun);
void RegisterDrawScreen(RECALL_DRAW drawFun); // 注冊顯示函數,很重要,沒他,就顯示不出3D畫面
void RegisterReShape(RECALL_RESHAPE reshapeFun); // 移動窗口會被調用
void RegisterWndProc(RECALL_WNDPROC WndProc); // 注冊事件回調函數,該函數將會相應窗口消息
void RegisterAddProcFun(RECALL_WNDPROC WndProc);
void RegisterOnIdleFun(RECALL_IDLE idlefun); // 注冊系統閑時處理函數
void RegisterClearFun(RECALL_CLEAR clearFun); // 注冊清除資源函數
void RegisterUpdataFun(RECALL_UPDATA updatafun) // 注冊更新界面函數
void RegisterMouseEnventFun(MouseFunType fun); // 注冊鼠標事件函數
protected:
MouseFunType mMouseEventFun{ nullptr };
RECALL_INIT mInitD3d;
RECALL_DRAW mDrawScreen;
RECALL_RESHAPE mReshape;
RECALL_CLEAR mClearFun;
RECALL_IDLE mIdleFun;
RECALL_UPDATA mUpdateFun;
};
//==========================================
注冊系列函數我們已經實現,唯一需要用戶自行實現的就只有WindHwnd和RemoveBorder這兩個函數,對于使用Win32或者MFC的同學來說獲取這個HWND實在是太簡單了,對于使用Qt的同學來說這兩個函數的實現依然很簡單,對了,為什么我們要實現RemoveBorder呢?看看我們文章開頭的圖片,整個窗口全都是使用DirectX繪制出來的,包括標題欄和邊框,這樣我們就可以實現我們想要的任意風格了,否則就算你把Client區域做得多華麗但是一看窗口邊框和標題欄就會不自覺的認為這不是一個風格的。
由于我們現在是在win32下,所以這里我們可以使用MAppWindow來作為我們真正的窗口基類。真正的win32窗口類MWindow:
//==================================
#pragma once
#include "MDx11String.h"
#include
#include "MNoCopy.h"
#include
#include "MAppWindow.h"
using namespace MDx11;
class MWindow;
class MEventFun;
//
// 消息回調函數
//
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);
//
// 重寫繼承而來的兩個虛函數
//
void RemoveBorder();
virtual HWND WindHwnd(){ return mHwnd; }
//
// 開啟消息循環
//
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); // 窗口回調函數
//
// 定義幾個事件屬性
//
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(); // 注冊窗口類
bool GenWindow(); // 創建窗口
virtual int Msgloop() const; // 消息循環
virtual void MouseEnterWindow();
virtual void MouseLeavesWindow();
//
// 窗口消息經回調函數轉而進相關的成員函數,方便操作成員數據
//
LRESULT __stdcall BaseWndProc(HWND, UINT, WPARAM, LPARAM);
RECALL_WNDPROC mAddFun; // 消息回調附加函數
private:
MDx11String mTitle;
HWND mHwnd;
unsigned mWidth;
unsigned mHeight;
DWORD mDwExStyle; // 窗口風格
DWORD mDwStyle; // 窗口風格
bool* bIsKeys; //監控鍵狀態
bool bIsInited;
volatile bool bIsDone;
bool bIsFullscreen;
bool bIsActive;
bool bIsAppPaused;
bool bIsMinimized;
bool bIsMaximized;
bool bIsResizing;
WNDCLASS mWndclass;
MDx11String mWndClassName;
//
// 一個計時器,凡是以I開頭的東西都是從com組件中導出來的
//
ITimer* pTimer{ nullptr };
//
// 記錄鼠標是否進入窗口
//
bool bIsEnter{ false };
//
// 保存消息響應函數
//
std::unordered_map mEventFunMap;
};
//=====================================
這個類沒啥多說的,唯一的技巧性操作是將回調函數過程轉發指成員函數身上,要解決這個問題很簡單,正cpp文件中創建一個全局窗口對象,在類的構造函數中將this直接賦給該全局指針,然后在回掉函數中根據HWND來檢查出對應的窗口,從而將事件轉發指相應的成員函數身上(其實如果只是簡單的實現一個窗口大不必要這么麻煩,之所以這么干是考慮到以后該窗口類的擴展性)。
現在只需要下面的代碼窗口就出來了:
//=====================================
MWindow w; //創建一個對象
w.SetTitle("Hello World"); // 設置窗口標題
w.Show(); // 顯示窗口
w.Run(); // 開啟消息循環
//=======================================
ok,今天的就到這里吧,具體實現因為比較簡單就比貼出來了,只需要知道這些類有這些接口以及相關成員就足夠了,接下來我們開始真正的進入DirectX,下一講我們來說說該類的渲染核心——DirectX11的初始化。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。