NuklearをC++、DirectX11環境で使う

# はじめに
GUIはimguiが有名だけれど、他の物を使って個性出したい！！という気持ちからnuklearを導入してGUIを目指す。
調べてもC++、DirectX11環境で導入している情報がないので備忘録としてここに残します。あっても英語しか見つからない...

目指す画面
<img width="750" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3889028/3f146a2c-dfb8-6fa2-6f9a-e832aa074997.png">


## 環境
・VisualStudio2022：17.11.2
・C++Ver20

## 準備

1. [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear/tree/master "Nuklear GitHub")から **nuklear.h** とdemoのd3d11の中にいる **nuklear_d3d11.h** 、 **nuklear_d3d11_pixel_shader.h** 、 **nuklear_d3d11_vertex_shader.h** をダウンロードする。
2. ダウンロードしたものをプロジェクトに追加する。
3. nuklear_d3d11.hで使われている関数がC言語仕様なのでC++で使えるように変更する。 変更しないと定義が無いというエラーが大量にあると思います。
```diff_c
//例として一部
NK_API void
nk_d3d11_render(ID3D11DeviceContext *context, enum nk_anti_aliasing AA)
{
    //...    
    ID3D11DeviceContext_IASetInputLayout(context, d3d11.input_layout);
    //↑を
    Renderer::GetDeviceContext()->IASetInputLayout(d3d11.input_layout);
    //↑こんな感じに変更
}
```
他にもラスタライザステートとかプロジェクトに合わせて変更します。ここは各々のプロジェクトに合わせてください。

 ## 導入して動かしてみる
 GUIクラスを作ってそこで初期化なりしていこうと思います。
 インクルードとデファインが多いけどdemoを参考にしながら作ります。
 ```diff_c
 //GUI.h---------------------------------------------------
#pragma once
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#include "nuklear.h"
#include "nuklear_d3d11.h"

class GUI {
	static struct nk_context* _ctx;

public:
	GUI() {}
	~GUI() {
		nk_d3d11_shutdown();
	}

	static void InitNuklear();
	static void UninitNuklear();

	static void Render();
};

 //GUI.cpp-------------------------------------------------
#include "GUI.h"
#define NK_IMPLEMENTATION
#define NK_D3D11_IMPLEMENTATION
#include "nuklear.h"
#include "nuklear_d3d11.h"

#include <malloc.h>
#include "Renderer.h"
#include "Input.h"

nk_context* GUI::_ctx;

void GUI::InitNuklear() {
	struct nk_d3d11_device* nuklearDevice;
	nuklearDevice = (struct nk_d3d11_device*)malloc(sizeof(struct nk_d3d11_device*));
	_ctx = nk_d3d11_init(Renderer::GetDevice(), SCREEN_WIDTH, SCREEN_HEIGHT, (512 * 1024), (128 * 1024));

	// フォントの初期化
	struct nk_font_atlas* atlas;
	nk_d3d11_font_stash_begin(&atlas);
	//フォントを使用する場合はここに書く
	/*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../extra_font/DroidSans.ttf", 14, 0);*/

	nk_d3d11_font_stash_end();
	/*nk_style_set_font(ctx, &droid->handle)*/;
}

void GUI::UninitNuklear() {
	nk_d3d11_shutdown();
}

void GUI::Render() {
	nk_input_begin(_ctx);

	nk_input_end(_ctx);

	// Nuklear の描画を開始
	if(nk_begin(_ctx, "Test", nk_rect(10, 10, 400, 300), NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE | NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) {
		//ウィンドウ内にテキスト表示
		nk_layout_row_static(_ctx, 40, 150, 1);
		nk_label(_ctx, "Hello, Nuklear!", NK_TEXT_LEFT);

		nk_layout_row_static(_ctx, 80, 150, 1);
		if (nk_button_label(_ctx, "Button")) {
			//ボタンが押されたときの処理をここに書く
		}
	}
	nk_end(_ctx);

	Renderer::GUIBegin();

	// デバイスコンテキストを使って描画
	nk_d3d11_render(NK_ANTI_ALIASING_OFF);
}
 ```
 シーンやらを管理しているManager.cppでInitNuklearを呼んで初期化してUninitNuklearで終了処理をしています。
 フォントは何も設定していないのでデフォルトのフォントが使われます。

 Renderは実際にnuklearのウィンドウを描画している部分です。
 nk_input_beginとnk_input_endは入力の初期化ですが、今はとりあえず描画したいので何もしていません。入力を受け取れるようにすればクリックしたりドラッグしたりできるようになります。


 Render::GUIBegin()の中身は
 ```diff_c
 m_DeviceContext->OMSetRenderTargets(1, &m_RenderTargetView, NULL);
 ```
 これです。
 これがあるとnuklearが正しく描画されます。
  **でもゲームを描画するときに戻して！**
```diff_c
void Renderer::Begin() {
	//背景色
	float clearColor[4] = { 0.564f, 0.933f, 0.564f, 1.0f };
	m_DeviceContext->ClearRenderTargetView(m_RenderTargetView, clearColor);
	m_DeviceContext->ClearDepthStencilView(m_DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
	m_DeviceContext->VSSetConstantBuffers(0, 1, &m_WorldBuffer);//これ
	m_DeviceContext->OMSetRenderTargets(1, &m_RenderTargetView, m_DepthStencilView);//これ
}
```
ゲームの描画の前でこれを呼び出しています。
コンスタントバッファもnuklearで書き換えているので戻しています。
これらがないとGUIは描画されてもオブジェクトがぐちゃぐちゃになったりポリゴンの裏面が描画されたりしてしまいます。

## おしまい
以上C++、DirectX11環境でnuklearを使う方法でした。
自分も導入したばかりで使い勝手はまだわかっていないけれどこれをきっかけにnuklearが流行ってほしい。
nuklearはこんなこともできるよ！みたいな情報があれば教えてください(、._. )、