[DirectX] IMGUI의 클래스 화 1
in Study on WinAPI&DirectX
함수형으로 이루어진 IMGUI코드를 클래스로 관리하는 방식으로 바꿔보자.
ImGuiMgr 클래스의 구성
// 헤더 파일
#pragma once
#include <Engine\CSingleton.h>
#include "ImGui\imgui.h"
#include "ImGui\imgui_impl_dx11.h"
#include "ImGui\imgui_impl_win32.h"
class InspectorUI;
class ImGuiMgr :
public CSingleton<ImGuiMgr>
{
SINGLE(ImGuiMgr);
private:
HWND m_hMainHwnd;
InspectorUI* m_Inspector;
public:
void init(HWND _hWnd);
void progress();
private:
void CreateUI();
void begin();
void tick();
void finaltick();
void render();
};
// cpp 파일
#include "pch.h"
#include "ImGuiMgr.h"
#include <Engine\CDevice.h>
#include <Engine\CLevelMgr.h>
#include <Engine\CKeyMgr.h>
#include <Engine\CGameObject.h>
#include "InspectorUI.h"
ImGuiMgr::ImGuiMgr()
: m_hMainHwnd(nullptr)
, m_Inspector(nullptr)
{
}
ImGuiMgr::~ImGuiMgr()
{
// ImGui Release
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
delete m_Inspector;
}
void ImGuiMgr::init(HWND _hWnd)
{
m_hMainHwnd = _hWnd;
// ImGui 초기화
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
//io.ConfigViewportsNoAutoMerge = true;
//io.ConfigViewportsNoTaskBarIcon = true;
//io.ConfigViewportsNoDefaultParent = true;
//io.ConfigDockingAlwaysTabBar = true;
//io.ConfigDockingTransparentPayload = true;
//io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: Experimental. THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP!
//io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI: Experimental.
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsLight();
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
// Setup Platform/Renderer backends
ImGui_ImplWin32_Init(m_hMainHwnd);
ImGui_ImplDX11_Init(DEVICE, CONTEXT);
CreateUI();
}
void ImGuiMgr::progress()
{
begin();
tick();
finaltick();
render();
}
void ImGuiMgr::begin()
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
}
void ImGuiMgr::tick()
{
m_Inspector->tick();
}
void ImGuiMgr::finaltick()
{
// Demo UI
ImGui::ShowDemoWindow();
// InspectorUI
m_Inspector->finaltick();
if (KEY_TAP(KEY::ENTER))
ImGui::SetWindowFocus(nullptr);
}
void ImGuiMgr::render()
{
// ImGui Rendering
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
// Update and Render additional Platform Windows
ImGuiIO& io = ImGui::GetIO(); (void)io;
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
}
void ImGuiMgr::CreateUI()
{
m_Inspector = new InspectorUI;
}
finaltick()에서 ImGUI가 명령어를 수행하도록 한다.
그리고 CreateUI에서 실제 사용되는 UI를 구성해준다.
이 때, UI들은 key값과 포인터를 이용한 map 자료구조로 관리한다.
UI의 클래스 구성
기존의 ImGUI는 UI하나 당 함수 하나의 형태로 주어져 있었지만, 이번에는 클래스 하나에 UI를 구성하여 UI가 객체가 되는 형태로 작성할 것이다.
부모 UI 클래스 구성
// 헤더파일
#pragma once
#include "ImGuiMgr.h"
class UI
{
private:
const string m_strName; // 타이틀 이름, ID
UI* m_ParentUI; // 부모 UI
vector<UI*> m_vecChildUI; // 자식 UI 목록
public:
virtual void tick() {}
virtual void finaltick();
virtual void render_update() = 0;
public:
const string& GetName() { return m_strName; }
void AddChildUI(UI* _UI)
{
_UI->m_ParentUI = this;
m_vecChildUI.push_back(_UI);
}
public:
UI(const string& _Name);
~UI();
};
// cpp 파일
#include "pch.h"
#include "UI.h"
UI::UI(const string& _Name)
: m_strName(_Name)
, m_ParentUI(nullptr)
{
}
UI::~UI()
{
Safe_Del_Vec(m_vecChildUI);
}
void UI::finaltick()
{
if (nullptr == m_ParentUI)
{
ImGui::Begin(m_strName.c_str());
render_update();
for (size_t i = 0; i < m_vecChildUI.size(); ++i)
{
m_vecChildUI[i]->finaltick();
if(i != m_vecChildUI.size() - 1)
ImGui::Separator();
}
ImGui::End();
}
else
{
ImGui::BeginChild(m_strName.c_str(), ImVec2(0.f, 200.f));
render_update();
for (size_t i = 0; i < m_vecChildUI.size(); ++i)
{
m_vecChildUI[i]->finaltick();
if (i != m_vecChildUI.size() - 1)
ImGui::Separator();
}
ImGui::EndChild();
}
}
- m_vSize : UI의 크기 값을 담당한다.
초기값이 0이 들어가면 부모의 크기를 따라 쓰겠다는 표시이다. - m_ParentUI : 부모 UI가 무엇인지를 저장한다.
- m_vecChildUI : 자식 UI 목록을 vector형태로 저장한다.
(예시) InspectorUI 클래스 구성
// 헤더 파일
#pragma once
#include "UI.h"
class CGameObject;
class TransformUI;
class MeshRenderUI;
class InspectorUI :
public UI
{
private:
CGameObject* m_pTarget;
TransformUI* m_TransformUI;
MeshRenderUI* m_MeshRenderUI;
//ComponentUI* m_arrComUI[(UINT)COMPONENT_TYPE::END];
public:
virtual void tick() override;
virtual void render_update() override;
public:
void SetTargetObject(CGameObject* _Target) { m_pTarget = _Target; }
public:
InspectorUI();
~InspectorUI();
};
// cpp 파일
#include "pch.h"
#include "InspectorUI.h"
#include <Engine\CGameObject.h>
#include <Engine\CTransform.h>
#include <Engine\CLevelMgr.h>
#include <Engine\CKeyMgr.h>
#include "TransformUI.h"
#include "MeshRenderUI.h"
InspectorUI::InspectorUI()
: UI("Inspector")
, m_pTarget(nullptr)
, m_TransformUI(nullptr)
{
m_TransformUI = new TransformUI;
//m_arrComUI[(UINT)COMPONENT_TYPE::TRANSFORM] = new TransformUI;
AddChildUI(m_TransformUI);
m_MeshRenderUI = new MeshRenderUI;
AddChildUI(m_MeshRenderUI);
}
InspectorUI::~InspectorUI()
{
}
void InspectorUI::tick()
{
if (nullptr == m_pTarget)
{
m_pTarget = CLevelMgr::GetInst()->FindObjectByName(L"Player");
m_TransformUI->SetTarget(m_pTarget);
m_MeshRenderUI->SetTarget(m_pTarget);
}
}
void InspectorUI::render_update()
{
}
Inspector UI는 고유한 이름 “Inspector”를 가진다.
그리고 ImGui::Text(“Inspector UI”)로 이름을 표시해준다.
(원하는 다른 이름이 있다면 다르게 표시해줄 수 있다.)
tick()함수에서 Inspector가 할 일을 구현해준다.
Inspector는 담당하는 오브젝트를 알아야한다.
그래서 LevelMgr에서 탐색하는 함수(FindObjectByName)를 만들어준다.
// CLevelMgr.cpp 중에서
CGameObject* CLevelMgr::FindObjectByName(const wstring& _strName)
{
return m_pCurLevel->FindObjectByName(_strName);
}
void CLevelMgr::FindObjectByName(const wstring& _strName, vector<CGameObject*>& _vec)
{
m_pCurLevel->FindObjectByName(_strName, _vec);
}
Transform 정보를 출력하기
Vec3 vPos = m_Target->Transform()->GetRelativePos();
Vec3 vScale = m_Target->Transform()->GetRelativeScale();
Vec3 vRotation = m_Target->Transform()->GetRelativeRot();
vRotation = (vRotation / XM_PI) * 180.f;
ImGui::Text("Position");
ImGui::SameLine();
ImGui::InputFloat3("##Relative Position", vPos);
ImGui::Text("Scale ");
ImGui::SameLine();
ImGui::InputFloat3("##Relative Scale", vScale);
ImGui::Text("Rotation");
ImGui::SameLine();
ImGui::InputFloat3("##Relative Rotation", vRotation);
}
InputFloat3 함수에서 텍스트와 3개의 float를 출력할 수 있다.
그리고 SameLine함수는 같은 줄에다가 출력될 수 있도록 만드는 함수이다.
Transform 정보를 입력하기
반대로 좌표, 회전, 크기 정보를 입력해서 개체의 상태를 조절할 수 있도록 만들어보자.
m_Target->Transform()->SetRelativePos(vPos);
m_Target->Transform()->SetRelativeScale(vScale);
vRotation = (vRotation / 180.f) * XM_PI;
m_Target->Transform()->SetRelativeRot(vRotation);
UI창에서 수동으로 값을 넣고 엔터를 누르면 vPos, vScale, vRotation에 그 값이 들어가게 된다.
즉, 엔터를 눌렀다면 Transform()->SetRelative…함수를 통해서 새로운 값을 넣어주면 되는 것이다.
그리고 추가 구성요소로 인해서 변동사항이 많다. 다음 포스트에 이어서 살펴보자.