2026年3月12日木曜日

DirectX11 で gltf 読み込みと表示(右手系)


DirectX11でgltfを読み込んで表示するプログラム。
agbycを参考に作成しました。
・agbycはDirectX12用なので多少の書き直しが必要だった。
・gltfは右手系なので、まずはDirectXを右手系で処理することで読み込み時の変換を不要にしてデバッグを容易にした。
・gltf読み込みのために、cgltfライブラリを使用。
・テクスチャ、マテリアルは読み込んでない。テクスチャは適当に生成。



■gltfの分かりにくかった部分
・静止状態(Node + Mesh)
 Nodeの初期TRSから行列を作り、Meshを配置する。
・動く準備(Skin + Joint)
 Skin(対応表)を介して、どのNodeを「骨」として扱うか特定する。
・動きの注入(Animation + Sampler)
 Samplerから値を取り出し、NodeのTRSをリアルタイムに上書きし続ける。
・描画(Skinning)
上書きされたNodeの行列と、inverseBindMatrices を組み合わせて最終的な形を計算する。



■main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <d3d11.h>
#pragma comment(lib, "d3d11.lib")

#include <d3dcompiler.h> // シェーダーコンパイル用
#pragma comment(lib, "d3dcompiler.lib")

#include <DirectXMath.h>

#include <vector>
#include <map>
#include <array>
#include <string>
#include <fstream>
#include <filesystem>
#include <span>



#define CGLTF_IMPLEMENTATION
#include "cgltf.h"





class DX11Device
{
public:
	DX11Device()
		: m_pDevice( nullptr )
		, m_pContext( nullptr )
		, m_pSwapChain( nullptr )
		, m_pRTV( nullptr )
		, m_pDepthStencilBuffer( nullptr )
		, m_pDSV( nullptr )
		, m_pRasterizerState( nullptr )
	{
	}

	~DX11Device()
	{
		if ( m_pRTV )
		{
			m_pRTV->Release();
			m_pRTV = nullptr;
		}
		if ( m_pSwapChain )
		{
			m_pSwapChain->Release();
			m_pSwapChain = nullptr;
		}
		if ( m_pContext )
		{
			m_pContext->Release();
			m_pContext = nullptr;
		}
		if ( m_pDevice )
		{
			m_pDevice->Release();
			m_pDevice = nullptr;
		}
		if ( m_pDepthStencilBuffer )
		{
			m_pDepthStencilBuffer->Release();
			m_pDepthStencilBuffer = nullptr;
		}
		if ( m_pDSV )
		{
			m_pDSV->Release();
			m_pDSV = nullptr;
		}
		if ( m_pRasterizerState )
		{
			m_pRasterizerState->Release();
			m_pRasterizerState = nullptr;
		}
	}

	bool initialize( HWND hWnd, int width, int height )
	{
		DXGI_SWAP_CHAIN_DESC sd = { 0 };
		sd.BufferCount = 1;
		sd.BufferDesc.Width = width;
		sd.BufferDesc.Height = height;
		sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		sd.BufferDesc.RefreshRate.Numerator = 60;
		sd.BufferDesc.RefreshRate.Denominator = 1;
		sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		sd.OutputWindow = hWnd;
		sd.SampleDesc.Count = 1;
		sd.SampleDesc.Quality = 0;
		sd.Windowed = TRUE;

		D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
		D3D_FEATURE_LEVEL featureLevel;

		HRESULT hr = D3D11CreateDeviceAndSwapChain(
			NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0,
			featureLevels, 1, D3D11_SDK_VERSION, &sd,
			&m_pSwapChain, &m_pDevice, &featureLevel, &m_pContext
		);

		if ( FAILED( hr ) ) return false;

		ID3D11Texture2D* pBackBuffer = nullptr;
		hr = m_pSwapChain->GetBuffer( 0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer );
		if ( FAILED( hr ) ) return false;

		hr = m_pDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRTV );
		pBackBuffer->Release();
		if ( FAILED( hr ) ) return false;


		// 深度バッファテクスチャの設定
		D3D11_TEXTURE2D_DESC descDepth = { 0 };
		descDepth.Width = 800;
		descDepth.Height = 600;
		descDepth.MipLevels = 1;
		descDepth.ArraySize = 1;
		descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
		descDepth.SampleDesc.Count = 1;
		descDepth.SampleDesc.Quality = 0;
		descDepth.Usage = D3D11_USAGE_DEFAULT;
		descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
		descDepth.CPUAccessFlags = 0;
		descDepth.MiscFlags = 0;

		// テクスチャ作成
		hr = m_pDevice->CreateTexture2D( &descDepth, NULL, &m_pDepthStencilBuffer );
		if ( FAILED( hr ) ) return false;

		// 深度ステンシルビュー作成
		D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
		ZeroMemory( &descDSV, sizeof( descDSV ) );
		descDSV.Format = descDepth.Format;
		descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
		descDSV.Texture2D.MipSlice = 0;

		hr = m_pDevice->CreateDepthStencilView( m_pDepthStencilBuffer, &descDSV, &m_pDSV );
		if ( FAILED( hr ) ) return false;


		m_pContext->OMSetRenderTargets( 1, &m_pRTV, m_pDSV );




		D3D11_DEPTH_STENCIL_DESC dsDesc = {};
		dsDesc.DepthEnable = TRUE;
		dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
		dsDesc.DepthFunc = D3D11_COMPARISON_LESS; // 「より手前なら描画する」右手系オプション

		ID3D11DepthStencilState* pDSState;
		m_pDevice->CreateDepthStencilState( &dsDesc, &pDSState );
		m_pContext->OMSetDepthStencilState( pDSState, 1 );




		D3D11_VIEWPORT vp;
		vp.Width = (FLOAT)width;
		vp.Height = (FLOAT)height;
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;
		m_pContext->RSSetViewports( 1, &vp );

		initRasterizer( m_pDevice );

		return true;
	}




	bool initRasterizer( ID3D11Device* pDevice )
	{
		D3D11_RASTERIZER_DESC rd;
		ZeroMemory( &rd, sizeof( rd ) );

		rd.FillMode = D3D11_FILL_SOLID;
		//rd.CullMode = D3D11_CULL_NONE;    // カリング
		rd.CullMode = D3D11_CULL_BACK;
		rd.FrontCounterClockwise = TRUE; // 時計回りが表
		rd.DepthClipEnable = TRUE;

		HRESULT hr = pDevice->CreateRasterizerState( &rd, &m_pRasterizerState );
		return SUCCEEDED( hr );
	}



	void beginRender( float r, float g, float b )
	{
		float clearColor[4] = { r, g, b, 1.0f }; // RGBA
		m_pContext->ClearRenderTargetView( m_pRTV, clearColor );
		m_pContext->ClearDepthStencilView( m_pDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );
		m_pContext->RSSetState( m_pRasterizerState );
	}


	void endRender()
	{
		m_pSwapChain->Present( 1, 0 );
	}




	ID3D11Device* getDevice() { return m_pDevice; }
	ID3D11DeviceContext* getContext() { return m_pContext; }


private:
	ID3D11Device* m_pDevice;
	ID3D11DeviceContext* m_pContext;
	IDXGISwapChain* m_pSwapChain;
	ID3D11RenderTargetView* m_pRTV;

	ID3D11Texture2D* m_pDepthStencilBuffer;
	ID3D11DepthStencilView* m_pDSV;

	ID3D11RasterizerState* m_pRasterizerState;
};




// 適当なテクスチャを作成
class Texture
{
public:
	Texture()
		: m_pSRV( nullptr )
		, m_pSampler( nullptr )
	{
	}
	~Texture()
	{
		if ( m_pSRV )
		{
			m_pSRV->Release();
			m_pSRV = nullptr;
		}
		if ( m_pSampler )
		{
			m_pSampler->Release();
			m_pSampler = nullptr;
		}
	}


	bool createCheckered( ID3D11Device* pDevice )
	{
		if ( !pDevice ) return false;


		const int texW = 256;
		const int texH = 256;
		DWORD pixels[texW * texH];
		for ( int i = 0; i < texW * texH; i++ )
		{
			int x = i % texW;
			int y = i / texW;
			// 32ピクセルごとに色を変える
			if ( ((x / 32) + (y / 32)) % 2 == 0 )
				pixels[i] = 0xFFFFFFFF; // 白 (ABGR)
			else
				pixels[i] = 0xFF00FF00; // 緑 (A=255, R=0, G=255, B=0)
		}


		D3D11_TEXTURE2D_DESC td = { 0 };
		td.Width = texW;
		td.Height = texH;
		td.MipLevels = 1;
		td.ArraySize = 1;
		td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		td.SampleDesc.Count = 1;
		td.Usage = D3D11_USAGE_DEFAULT;
		td.BindFlags = D3D11_BIND_SHADER_RESOURCE;

		D3D11_SUBRESOURCE_DATA initData = { 0 };
		initData.pSysMem = pixels;
		initData.SysMemPitch = texW * 4;

		ID3D11Texture2D* pTex = nullptr;
		HRESULT hr = pDevice->CreateTexture2D( &td, &initData, &pTex );
		if ( FAILED( hr ) ) return false;


		hr = pDevice->CreateShaderResourceView( pTex, NULL, &m_pSRV );
		pTex->Release();
		if ( FAILED( hr ) ) return false;


		D3D11_SAMPLER_DESC sd;
		ZeroMemory( &sd, sizeof( sd ) );
		sd.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		sd.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
		sd.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
		sd.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		sd.ComparisonFunc = D3D11_COMPARISON_NEVER;
		sd.MinLOD = 0;
		sd.MaxLOD = D3D11_FLOAT32_MAX;

		hr = pDevice->CreateSamplerState( &sd, &m_pSampler );
		if ( FAILED( hr ) ) return false;

		return true;
	}


	void bind( ID3D11DeviceContext* pContext )
	{
		if ( !pContext ) return;

		pContext->PSSetShaderResources( 0, 1, &m_pSRV );
		pContext->PSSetSamplers( 0, 1, &m_pSampler );
	}

private:
	ID3D11ShaderResourceView* m_pSRV;
	ID3D11SamplerState* m_pSampler;
};







class Camera
{
public:
	Camera()
		: m_pos( 0, 1, -5 )
		, m_rotation( 0, 0, 0 )
	{
		update();
	}

	void update()
	{
		DirectX::XMMATRIX rotation = DirectX::XMMatrixRotationRollPitchYaw( m_rotation.x, m_rotation.y, 0.0f );
		DirectX::XMVECTOR pos = DirectX::XMLoadFloat3( &m_pos );
		DirectX::XMVECTOR lookAt = DirectX::XMVectorSet( 0.0f, 0.0f, 1.0f, 0.0f );
		lookAt = DirectX::XMVector3TransformCoord( lookAt, rotation );

		DirectX::XMVECTOR target = DirectX::XMVectorAdd( pos, lookAt );
		DirectX::XMVECTOR up = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
		m_viewMatrix = DirectX::XMMatrixLookAtRH( pos, target, up );
	}

	DirectX::XMMATRIX getViewMatrix() const
	{
		return m_viewMatrix;
	}


	void setPos( float x, float y, float z )
	{
		m_pos = DirectX::XMFLOAT3( x, y, z );
		update();
	}

	void move( float x, float y, float z )
	{
		DirectX::XMMATRIX rotation = DirectX::XMMatrixRotationRollPitchYaw( m_rotation.x, m_rotation.y, 0 );
		DirectX::XMVECTOR moveVec = DirectX::XMVectorSet( x, y, z, 0.0f );
		moveVec = DirectX::XMVector3TransformCoord( moveVec, rotation );

		DirectX::XMVECTOR currentPos = DirectX::XMLoadFloat3( &m_pos );
		currentPos = DirectX::XMVectorAdd( currentPos, moveVec );

		DirectX::XMStoreFloat3( &m_pos, currentPos );
		update();
	}

	void rotate( float pitch, float yaw )
	{
		m_rotation.x += pitch;
		m_rotation.y += yaw;

		// 角度制限(真上・真下に行き過ぎないように)
		if ( m_rotation.x > 1.5f ) m_rotation.x = 1.5f;
		if ( m_rotation.x < -1.5f ) m_rotation.x = -1.5f;

		update();
	}

	const DirectX::XMFLOAT3& getPos()const { return m_pos; }
private:
	DirectX::XMFLOAT3 m_pos;
	DirectX::XMFLOAT3 m_rotation;
	DirectX::XMMATRIX m_viewMatrix;
};







class AnimeShader
{
public:
	AnimeShader()
		: m_pVS( nullptr )
		, m_pPS( nullptr )
		, m_pLayout( nullptr )
		, m_pConstantBuffer( nullptr )
		, m_pBoneBuffer( nullptr )
	{
	}

	~AnimeShader()
	{
		if ( m_pVS )
		{
			m_pVS->Release();
			m_pVS = nullptr;
		}
		if ( m_pPS )
		{
			m_pPS->Release();
			m_pPS = nullptr;
		}
		if ( m_pLayout )
		{
			m_pLayout->Release();
			m_pLayout = nullptr;
		}
		if ( m_pConstantBuffer )
		{
			m_pConstantBuffer->Release();
			m_pConstantBuffer = nullptr;
		}
		if ( m_pBoneBuffer )
		{
			m_pBoneBuffer->Release();
			m_pBoneBuffer = nullptr;
		}
	}


	// hlslコンパイルと各メモリ確保
	bool initialize( ID3D11Device* pDevice, LPCTSTR filename )
	{
		if ( !pDevice || !filename ) return  false;

		HRESULT hr;
		ID3DBlob* pVSBlob = nullptr;
		ID3DBlob* pErrorBlob = nullptr;


		hr = D3DCompileFromFile(
			filename,   // ファイル名
			NULL, NULL, 
			"VS",
			"vs_5_0",
			0, 0,
			&pVSBlob,
			&pErrorBlob
		);

		// コンパイルエラーのチェック
		if ( FAILED( hr ) ) {
			if ( pErrorBlob ) {
				OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() ); // 出力ウィンドウにエラー表示
				pErrorBlob->Release();
			}
			return false;
		}

		// シェーダーオブジェクトの作成
		hr = pDevice->CreateVertexShader(
			pVSBlob->GetBufferPointer(),
			pVSBlob->GetBufferSize(),
			NULL,
			&m_pVS
		);
		if ( FAILED( hr ) ) { pVSBlob->Release(); return false; }


		D3D11_INPUT_ELEMENT_DESC layout[] = {
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "BLENDWEIGHT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "BLENDINDICES", 0, DXGI_FORMAT_R32G32B32A32_UINT, 0, 48, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};
		UINT numElements = ARRAYSIZE( layout );

		hr = pDevice->CreateInputLayout(
			layout,
			numElements,
			pVSBlob->GetBufferPointer(),
			pVSBlob->GetBufferSize(),
			&m_pLayout
		);
		pVSBlob->Release();
		if ( FAILED( hr ) ) return false;

		// ピクセルシェーダのコンパイル
		ID3DBlob* pPSBlob = nullptr;
		hr = D3DCompileFromFile(
			L"AnimeShader.hlsl",
			NULL, NULL,
			"PS",
			"ps_5_0",
			0, 0,
			&pPSBlob,
			&pErrorBlob
		);

		if ( FAILED( hr ) ) {
			if ( pErrorBlob ) {
				OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
				pErrorBlob->Release();
			}
			return false;
		}

		// ピクセルシェーダーオブジェクト作成
		hr = pDevice->CreatePixelShader(
			pPSBlob->GetBufferPointer(),
			pPSBlob->GetBufferSize(),
			NULL,
			&m_pPS
		);
		pPSBlob->Release();
		if ( FAILED( hr ) ) return false;


		// 定数バッファ作成(WVP)
		{
			D3D11_BUFFER_DESC cbd = { 0 };
			cbd.ByteWidth = sizeof( CBData );
			cbd.Usage = D3D11_USAGE_DEFAULT;
			cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
			cbd.CPUAccessFlags = 0;

			hr = pDevice->CreateBuffer( &cbd, NULL, &m_pConstantBuffer );
			if ( FAILED( hr ) ) return false;
		}


		// 定数バッファ作成(ボーン)
		{
			D3D11_BUFFER_DESC cbd = { 0 };
			cbd.ByteWidth = sizeof( DirectX::XMMATRIX ) * 128;
			cbd.Usage = D3D11_USAGE_DEFAULT;
			cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
			cbd.CPUAccessFlags = 0;

			hr = pDevice->CreateBuffer( &cbd, NULL, &m_pBoneBuffer );
			if ( FAILED( hr ) ) return false;
		}

		return true;
	}


	// シェーダセットと行列更新
	void bind( ID3D11DeviceContext* pContext, DirectX::XMMATRIX world, DirectX::XMMATRIX view, DirectX::XMMATRIX proj, const std::vector<DirectX::XMMATRIX>& bonesMatrices )
	{
		if ( !pContext ) return;

		pContext->IASetInputLayout( m_pLayout );
		pContext->VSSetShader( m_pVS, NULL, 0 );
		pContext->PSSetShader( m_pPS, NULL, 0 );

		CBData cb;
		cb.wvp = DirectX::XMMatrixTranspose( world * view * proj );

		std::array<DirectX::XMMATRIX, 128> tpBones;
		for ( auto& m : tpBones ) m = DirectX::XMMatrixIdentity();

		for ( size_t i = 0; i < tpBones.size(); ++i )
		{
			if ( i >= bonesMatrices.size() ) break;
			tpBones[i] = DirectX::XMMatrixTranspose( bonesMatrices[i] );
		}

		pContext->UpdateSubresource( m_pConstantBuffer, 0, NULL, &cb, 0, 0 );
		pContext->UpdateSubresource( m_pBoneBuffer, 0, NULL, tpBones.data(), 0, 0 );

		// シェーダーの定数バッファスロットにセット
		pContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffer );
		pContext->VSSetConstantBuffers( 1, 1, &m_pBoneBuffer );
	}


private:
	struct CBData { DirectX::XMMATRIX wvp; }; // 定数バッファ用

	ID3D11VertexShader* m_pVS;
	ID3D11PixelShader* m_pPS;
	ID3D11InputLayout* m_pLayout;
	ID3D11Buffer* m_pConstantBuffer;
	ID3D11Buffer* m_pBoneBuffer;
};




namespace Gltf
{

	struct Transform
	{
		DirectX::XMFLOAT3 scale = { 1,1,1 };
		DirectX::XMFLOAT4 rotate = { 0,0,0,1 };
		DirectX::XMFLOAT3 translation = { 0,0,0 };

		Transform() = default;
		Transform( DirectX::XMMATRIX mat )
		{
			DirectX::XMVECTOR s, r, t;
			DirectX::XMMatrixDecompose( &s, &r, &t, mat );
			DirectX::XMStoreFloat3( &scale, s );
			DirectX::XMStoreFloat4( &rotate, r );
			DirectX::XMStoreFloat3( &translation, t );
		}
		DirectX::XMMATRIX toMat() const
		{
			DirectX::XMVECTOR s = DirectX::XMLoadFloat3( &scale );
			DirectX::XMVECTOR r = DirectX::XMLoadFloat4( &rotate );
			DirectX::XMVECTOR t = DirectX::XMLoadFloat3( &translation );
			DirectX::XMVECTOR zero = DirectX::XMVectorZero();
			return DirectX::XMMatrixAffineTransformation( s, zero, r, t );
		}

		Transform mix( const Transform& a, const Transform& b, float t )
		{
			Transform ret;
			DirectX::XMStoreFloat3( &ret.translation, DirectX::XMVectorLerp( DirectX::XMLoadFloat3( &a.translation ), DirectX::XMLoadFloat3( &b.translation ), t ) );
			DirectX::XMStoreFloat4( &ret.rotate, DirectX::XMQuaternionSlerp( DirectX::XMLoadFloat4( &a.rotate ), DirectX::XMLoadFloat4( &b.rotate ), t ) );
			DirectX::XMStoreFloat3( &ret.scale, DirectX::XMVectorLerp( DirectX::XMLoadFloat3( &a.scale ), DirectX::XMLoadFloat3( &b.scale ), t ) );
			return ret;
		}
	};


	struct Vertex
	{
		DirectX::XMFLOAT3 pos;
		DirectX::XMFLOAT3 normal;
		DirectX::XMFLOAT2 texcoord;
		DirectX::XMFLOAT4 jointWeights = { 1,0,0,0 };
		DirectX::XMUINT4 jointIndex = { 0,0,0,0 };
	};


	struct Primitive
	{
		ID3D11Buffer* vertexBuffer = nullptr;
		ID3D11Buffer* indexBuffer = nullptr;
		UINT indexCount;

		bool init( ID3D11Device* pDevice, std::vector<Vertex>& vertices, std::vector<uint32_t>& indices )
		{
			if ( !pDevice || vertices.empty() || indices.empty() ) return false;

			const size_t meshIdx = 0;

			// 頂点バッファ作成
			D3D11_BUFFER_DESC bd = { 0 };
			bd.Usage = D3D11_USAGE_DEFAULT;
			bd.ByteWidth = sizeof( Vertex ) * vertices.size();
			bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;

			D3D11_SUBRESOURCE_DATA initData = { 0 };
			initData.pSysMem = vertices.data();
			HRESULT hr = pDevice->CreateBuffer( &bd, &initData, &vertexBuffer );
			if ( FAILED( hr ) ) return false;

			indexCount = indices.size();
			bd.ByteWidth = sizeof( uint32_t ) * indexCount;
			bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
			initData.pSysMem = indices.data();
			hr = pDevice->CreateBuffer( &bd, &initData, &indexBuffer );
			if ( FAILED( hr ) ) return false;

			return true;
		}

		void release()
		{
			if ( vertexBuffer )
			{
				vertexBuffer->Release();
				vertexBuffer = nullptr;
			}
			if ( indexBuffer )
			{
				indexBuffer->Release();
				indexBuffer = nullptr;
			}
			indexCount = 0;
		}

		// 描画命令
		void draw( ID3D11DeviceContext* pContext )
		{
			if ( !pContext ) return;

			UINT stride = sizeof( Vertex );
			UINT offset = 0;
			pContext->IASetVertexBuffers( 0, 1, &vertexBuffer, &stride, &offset );

			pContext->IASetIndexBuffer( indexBuffer, DXGI_FORMAT_R32_UINT, 0 );
			pContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

			pContext->DrawIndexed( indexCount, 0, 0 );
		}
	};

	struct Mesh
	{
		std::string name;
		std::vector<Primitive> primitives;

	};

	struct Node;
	struct Skin
	{
		struct Joint
		{
			Node* node;
			DirectX::XMMATRIX inverseBindMat;
		};
		std::vector<Joint> joints;
	};

	struct Node
	{
		std::string name;
		Node* parent;
		std::vector<Node*> children;
		DirectX::XMMATRIX globalTransform;
		DirectX::XMMATRIX localTransform;
		Transform baseTransform;
		Mesh* mesh;
		Skin* skin;
		int index = -1;	//<GltfModel::nodesの自データがvector何番目にあるか
	};



	enum class Interpolation { Linear, Step, CubicSpline };

	struct KeyFrame
	{
		float time = 0;
		DirectX::XMFLOAT4 xyzw = { 0,0,0,0 };
	};

	struct Sampler
	{
		Interpolation interpolation;
		std::vector<KeyFrame> keyFrames;
	};


	enum class ChannelType { Translate, Rotate, Scale };

	struct Channel
	{
		Node* node;
		Sampler* sampler;
		ChannelType type;
	};

	struct Animation
	{
		std::string name;
		std::vector<Channel> channels;
		std::vector<Sampler> samplers;
		float timeLength;
	};
};


struct GltfModel
{
	std::vector<Gltf::Mesh> meshes;
	std::vector<Gltf::Node> nodes;
	std::vector<Gltf::Node*> rootNodes;
	std::vector<Gltf::Node*> meshNodes;
	std::vector<Gltf::Skin> skins;
	Gltf::Node* skeletonRootNode;
	std::vector<Gltf::Animation> animations;

	~GltfModel()
	{
		for ( auto& mesh : meshes )
		{
			for ( auto& prim : mesh.primitives )
			{
				prim.release();
			}
		}
	}
};



class GltfInstance
{
private:
	struct InstanceSkin
	{
		std::vector<DirectX::XMMATRIX> bonesTransformMats;
	};





	// 再帰的にグローバル行列を計算する
	void updateNodeGlobalTransforms( GltfModel* model, Gltf::Node* node, const DirectX::XMMATRIX& parentMat, const std::vector<Gltf::Transform>& nodeLocalTransforms, std::vector<DirectX::XMMATRIX>& nodeGlobalTransformMats )
	{
		//int nodeIndex = node - &model->nodes[0];
		int nodeIndex = node->index;
		DirectX::XMMATRIX mat = DirectX::XMMatrixMultiply( nodeLocalTransforms[nodeIndex].toMat(), parentMat );
		nodeGlobalTransformMats[nodeIndex] = mat;
		for ( Gltf::Node* childNode : node->children )
		{
			updateNodeGlobalTransforms( model, childNode, mat, nodeLocalTransforms, nodeGlobalTransformMats );
		}
	}


	static DirectX::XMFLOAT3 lerp( const DirectX::XMFLOAT4& frame0, const DirectX::XMFLOAT4& frame1, float percentage )
	{
		auto vec = DirectX::XMVectorLerp( DirectX::XMLoadFloat4( &frame0 ), DirectX::XMLoadFloat4( &frame1 ), percentage );
		DirectX::XMFLOAT3 t;
		DirectX::XMStoreFloat3( &t, vec );
		return t;
	}

	static DirectX::XMFLOAT4 slerp( const DirectX::XMFLOAT4& frame0, const DirectX::XMFLOAT4& frame1, float percentage )
	{
		auto vec = DirectX::XMQuaternionSlerp( DirectX::XMLoadFloat4( &frame0 ), DirectX::XMLoadFloat4( &frame1 ), percentage );
		DirectX::XMFLOAT4 t;
		DirectX::XMStoreFloat4( &t, vec );
		return t;
	}


public:	
	GltfInstance() = default;
	

	void update( float deltaTime )
	{
		if ( !animation ) return;

		// 時間の進行とループ処理
		animationTime += deltaTime;
		if ( animationTime > animation->timeLength )
		{
			animationTime -= animation->timeLength;
		}

		// 各ノードをベース状態に戻す
		for ( size_t i = 0; i < model->nodes.size(); ++i )
		{
			nodesLocalTransforms[i] = model->nodes[i].baseTransform;
		}

		// キーフレーム補間によるローカル姿勢の上書き
		for ( Gltf::Channel& channel : animation->channels )
		{
			auto frame0 = channel.sampler->keyFrames[0].xyzw;
			auto frame1 = channel.sampler->keyFrames[1].xyzw;
			float percentage = 0;
			for ( size_t frameIndex = 1; frameIndex < channel.sampler->keyFrames.size(); ++frameIndex )
			{
				Gltf::KeyFrame& keyFrame = channel.sampler->keyFrames[frameIndex];
				if ( animationTime < keyFrame.time )
				{
					Gltf::KeyFrame& keyFramePrevious = channel.sampler->keyFrames[frameIndex - 1];
					frame0 = keyFramePrevious.xyzw;
					frame1 = keyFrame.xyzw;
					percentage = ((float)animationTime - keyFramePrevious.time) / (keyFrame.time - keyFramePrevious.time);
					break;
				}
			}

			
			int nodeIndex = channel.node->index;

			if ( channel.type == Gltf::ChannelType::Translate )
			{
				if ( channel.sampler->interpolation == Gltf::Interpolation::Linear )
				{
					nodesLocalTransforms[nodeIndex].translation = lerp( frame0, frame1, percentage );
				}
				else if ( channel.sampler->interpolation == Gltf::Interpolation::Step )
				{
					nodesLocalTransforms[nodeIndex].translation = percentage < 1 ? DirectX::XMFLOAT3( frame0.x, frame0.y, frame0.z ) : DirectX::XMFLOAT3( frame1.x, frame1.y, frame1.z );
				}
			}
			else if ( channel.type == Gltf::ChannelType::Rotate )
			{
				if ( channel.sampler->interpolation == Gltf::Interpolation::Linear )
				{
					nodesLocalTransforms[nodeIndex].rotate = slerp( frame0, frame1, percentage );
				}
				else if ( channel.sampler->interpolation == Gltf::Interpolation::Step )
				{
					nodesLocalTransforms[nodeIndex].rotate = percentage < 1 ? frame0 : frame1;
				}
			}
			else if ( channel.type == Gltf::ChannelType::Scale )
			{
				if ( channel.sampler->interpolation == Gltf::Interpolation::Linear )
				{
					nodesLocalTransforms[nodeIndex].scale = lerp( frame0, frame1, percentage );
				}
				else if ( channel.sampler->interpolation == Gltf::Interpolation::Step )
				{
					nodesLocalTransforms[nodeIndex].scale = percentage < 1 ? DirectX::XMFLOAT3( frame0.x, frame0.y, frame0.z ) : DirectX::XMFLOAT3( frame1.x, frame1.y, frame1.z );
				}
			}
		}

		// ルートノードから階層をたどってグローバル行列を計算
		for ( Gltf::Node* rootNode : model->rootNodes )
		{
			updateNodeGlobalTransforms( model, rootNode, DirectX::XMMatrixIdentity(), nodesLocalTransforms, nodesGlobalTransformMats );
		}

		// スキン(ボーン)行列の更新
		for ( size_t skinIndex = 0; skinIndex < model->skins.size(); ++skinIndex )
		{
			Gltf::Skin& skin = model->skins[skinIndex];
			InstanceSkin& instanceSkin = skins[skinIndex];
			for ( size_t jointIndex = 0; jointIndex < skin.joints.size(); ++jointIndex )
			{
				//int nodeIndex = skin.joints[jointIndex].node - &model->nodes[0];
				int nodeIndex = skin.joints[jointIndex].node->index;
				instanceSkin.bonesTransformMats[jointIndex] = DirectX::XMMatrixMultiply( skin.joints[jointIndex].inverseBindMat, nodesGlobalTransformMats[nodeIndex] );
			}
		}
	}


/*
	void draw( ID3D11DeviceContext* pContext, AnimeShader& shader, const DirectX::XMMATRIX& world, const DirectX::XMMATRIX& view, const DirectX::XMMATRIX& proj )
	{
		DirectX::XMMATRIX objectWorld = DirectX::XMMatrixIdentity();
		
		for ( auto* node : model->meshNodes )
		{
			if ( !node->mesh ) continue;

			DirectX::XMMATRIX finalWorld;
			std::vector<DirectX::XMMATRIX>* pBoneMatrices = nullptr;
			static std::vector<DirectX::XMMATRIX> identityBones( 1, DirectX::XMMatrixIdentity() );

			if ( node->skin )
			{
				finalWorld = objectWorld;

				size_t skinIndex = node->skin - &model->skins[0];
				pBoneMatrices = &skins[skinIndex].bonesTransformMats;
			}
			else
			{

				finalWorld = DirectX::XMMatrixMultiply( node->globalTransform, objectWorld );
				pBoneMatrices = &identityBones;
			}

			shader.bind( pContext, finalWorld, view, proj, *pBoneMatrices );

			// このノードのメッシュを描画
			for ( auto& prim : node->mesh->primitives )
			{
				prim.draw( pContext );
			}
		}
	}
*/
	void initialize( GltfModel* targetModel )
	{
		model = targetModel;
		animationTime = 0;

		if ( targetModel && targetModel->animations.size() > 0 )
		{
			animation = &targetModel->animations[0];
			nodesLocalTransforms.resize( targetModel->nodes.size() );
			nodesGlobalTransformMats.resize( targetModel->nodes.size() );
			skins.resize( targetModel->skins.size() );
			for ( size_t skinIndex = 0; skinIndex < targetModel->skins.size(); ++skinIndex )
			{
				skins[skinIndex].bonesTransformMats.resize( targetModel->skins[skinIndex].joints.size() );
			}
		}
	}


public:
	GltfModel* model = nullptr;
	Gltf::Animation* animation = nullptr;
	float animationTime = 0;
	std::vector<Gltf::Transform> nodesLocalTransforms;
	std::vector<DirectX::XMMATRIX> nodesGlobalTransformMats;
	std::vector<InstanceSkin> skins;
};



class GLTFLoader
{
private:
	static void loadNodes( cgltf_data* gltfData, GltfModel& inout_mesh )
	{
		for ( size_t nodeIndex = 0; nodeIndex < gltfData->nodes_count; ++nodeIndex )
		{
			cgltf_node& gltfNode = gltfData->nodes[nodeIndex];
			Gltf::Node& node = inout_mesh.nodes[nodeIndex];
			node.index = &node - &inout_mesh.nodes[0];

			if ( gltfNode.name ) node.name = gltfNode.name;

			if ( gltfNode.parent )
			{
				size_t parentNodeIndex = gltfNode.parent - gltfData->nodes;
				node.parent = &inout_mesh.nodes[parentNodeIndex];
			}
			else
			{
				node.parent = nullptr;
			}

			for ( cgltf_node* child : std::span( gltfNode.children, gltfNode.children_count ) )
			{
				size_t childNodeIndex = child - gltfData->nodes;
				node.children.push_back( &inout_mesh.nodes[childNodeIndex] );
			}

			{
				DirectX::XMFLOAT4X4 nodeTransform = {};
				cgltf_node_transform_world( &gltfNode, (float*)&nodeTransform );
				node.globalTransform = DirectX::XMLoadFloat4x4( &nodeTransform );

			}

			if ( gltfNode.has_matrix )
			{
				node.localTransform = DirectX::XMLoadFloat4x4( (DirectX::XMFLOAT4X4*)gltfNode.matrix );
			}
			else
			{
				
				if ( gltfNode.has_scale )
				{
					node.baseTransform.scale.x = gltfNode.scale[0];
					node.baseTransform.scale.y = gltfNode.scale[1];
					node.baseTransform.scale.z = gltfNode.scale[2];
				}
				if ( gltfNode.has_rotation )
				{
					node.baseTransform.rotate.x = gltfNode.rotation[0];
					node.baseTransform.rotate.y = gltfNode.rotation[1];
					node.baseTransform.rotate.z = gltfNode.rotation[2];
					node.baseTransform.rotate.w = gltfNode.rotation[3];
				}
				if ( gltfNode.has_translation )
				{
					node.baseTransform.translation.x = gltfNode.translation[0];
					node.baseTransform.translation.y = gltfNode.translation[1];
					node.baseTransform.translation.z = gltfNode.translation[2];
				}
				node.localTransform = node.baseTransform.toMat();
			}

			if ( gltfNode.mesh )
			{
				size_t meshIndex = gltfNode.mesh - gltfData->meshes;
				node.mesh = &inout_mesh.meshes[meshIndex];
				inout_mesh.meshNodes.push_back( &node );
			}
			else
			{
				node.mesh = nullptr;
			}

			if ( gltfNode.skin )
			{
				size_t skinIndex = gltfNode.skin - gltfData->skins;
				node.skin = &inout_mesh.skins[skinIndex];
			}
			else
			{
				node.skin = nullptr;
			}
		}

		for ( cgltf_node* gltfNode : std::span( gltfData->scene[0].nodes, gltfData->scenes[0].nodes_count ) )
		{
			inout_mesh.rootNodes.push_back( &inout_mesh.nodes[gltfNode - gltfData->nodes] );
		}
	}

	static void loadMeshes( cgltf_data* gltfData, ID3D11Device* pDevice, GltfModel& inout_mesh )
	{
		for ( size_t meshIndex = 0; meshIndex < gltfData->meshes_count; ++meshIndex )
		{
			cgltf_mesh& gltfMesh = gltfData->meshes[meshIndex];
			Gltf::Mesh& mesh = inout_mesh.meshes[meshIndex];
			if ( gltfMesh.name ) mesh.name = gltfMesh.name;

			mesh.primitives.reserve( gltfMesh.primitives_count );

			std::vector<Gltf::Vertex> modelVertices;
			std::vector<uint32_t> modelIndices;
			for ( cgltf_primitive& gltfPrimitive : std::span( gltfMesh.primitives, gltfMesh.primitives_count ) )
			{
				cgltf_accessor* indices = gltfPrimitive.indices;
				cgltf_accessor* positions = nullptr;
				cgltf_accessor* normals = nullptr;
				cgltf_accessor* uvs = nullptr;
				cgltf_accessor* jointIndices = nullptr;
				cgltf_accessor* jointWeights = nullptr;
				
				for ( cgltf_attribute& gltfAttribute : std::span( gltfPrimitive.attributes, gltfPrimitive.attributes_count ) )
				{
					if ( gltfAttribute.type == cgltf_attribute_type_position )
					{
						positions = gltfAttribute.data;
					}
					else if ( gltfAttribute.type == cgltf_attribute_type_normal )
					{
						normals = gltfAttribute.data;
					}
					else if ( gltfAttribute.type == cgltf_attribute_type_texcoord && 0==strcmp( gltfAttribute.name, "TEXCOORD_0" ) )
					{
						uvs = gltfAttribute.data;
					}
					else if ( gltfAttribute.type == cgltf_attribute_type_joints )
					{
						jointIndices = gltfAttribute.data;
					}
					else if ( gltfAttribute.type == cgltf_attribute_type_weights )
					{
						jointWeights = gltfAttribute.data;
					}
				}

				modelVertices.resize( positions->count );
				for ( size_t i = 0; i < positions->count; ++i )
				{
					cgltf_accessor_read_float( positions, i, &modelVertices[i].pos.x, 3 );
					cgltf_accessor_read_float( normals, i, &modelVertices[i].normal.x, 3 );
					cgltf_accessor_read_float( uvs, i, &modelVertices[i].texcoord.x, 2 );
					if ( jointIndices ) cgltf_accessor_read_uint( jointIndices, i, &modelVertices[i].jointIndex.x, 4 );
					if ( jointWeights ) cgltf_accessor_read_float( jointWeights, i, &modelVertices[i].jointWeights.x, 4 );
				}

				if ( indices )
				{
					modelIndices.resize( indices->count );
					for ( size_t i = 0; i < indices->count; i+=3 )
					{
						modelIndices[i + 0] = cgltf_accessor_read_index( indices, i );
						modelIndices[i + 1] = cgltf_accessor_read_index( indices, i + 1 );
						modelIndices[i + 2] = cgltf_accessor_read_index( indices, i + 2 );
					}
				}


				Gltf::Primitive& modelPrimitive = mesh.primitives.emplace_back();
				modelPrimitive.init( pDevice, modelVertices, modelIndices );
			}
		}
	}

	static void loadSkins( cgltf_data* gltfData, GltfModel& inout_mesh )
	{
		for ( size_t skinIndex = 0; skinIndex < gltfData->skins_count; ++skinIndex )
		{
			cgltf_skin& gltfSkin = gltfData->skins[skinIndex];
			Gltf::Skin& skin = inout_mesh.skins[skinIndex];

			skin.joints.reserve( gltfSkin.joints_count );
			for ( size_t jointIndex = 0; jointIndex < gltfSkin.joints_count; ++jointIndex )
			{
				cgltf_node* jointNode = gltfSkin.joints[jointIndex];
				Gltf::Node* node = &inout_mesh.nodes[jointNode - gltfData->nodes];

				DirectX::XMFLOAT4X4 inv;
				cgltf_accessor_read_float( gltfSkin.inverse_bind_matrices, jointIndex, (float*)&inv, 16 );
				
				Gltf::Skin::Joint mj;
				mj.node = node;
				mj.inverseBindMat = DirectX::XMLoadFloat4x4( &inv );
				skin.joints.push_back( mj );
			}
		}

		// skeletonRootNode設定
		uint32_t minParentCount = UINT_MAX;
		for ( Gltf::Skin& skin : inout_mesh.skins )
		{
			for ( Gltf::Skin::Joint& joint : skin.joints )
			{
				Gltf::Node* jointNode = joint.node;
				Gltf::Node* parentNode = jointNode->parent;
				uint32_t parentCount = 0;
				while ( parentNode )
				{
					parentNode = parentNode->parent;
					parentCount++;
				}

				if ( parentCount < minParentCount )
				{
					minParentCount = parentCount;
					inout_mesh.skeletonRootNode = jointNode;
				}
			}
		}
	}

	static void loadAnimations( cgltf_data* gltfData, GltfModel& inout_mesh )
	{
		for ( size_t animationIndex = 0; animationIndex < gltfData->animations_count; ++animationIndex )
		{
			cgltf_animation& gltfAnimation = gltfData->animations[animationIndex];
			Gltf::Animation& animation = inout_mesh.animations[animationIndex];
			if ( gltfAnimation.name ) animation.name = gltfAnimation.name;



			animation.samplers.reserve( gltfAnimation.samplers_count );
			for ( cgltf_animation_sampler& gltfSampler : std::span( gltfAnimation.samplers, gltfAnimation.samplers_count ) )
			{
				// time
				{
					float timeLength = 0;
					cgltf_accessor_read_float( gltfSampler.input, gltfSampler.input->count - 1, &timeLength, 1 );
					if ( animation.timeLength < timeLength )
					{
						animation.timeLength = timeLength;
					}
				}

				Gltf::Sampler& sampler = animation.samplers.emplace_back();
				if ( gltfSampler.interpolation == cgltf_interpolation_type_linear )
				{
					sampler.interpolation = Gltf::Interpolation::Linear;
				}
				else if ( gltfSampler.interpolation == cgltf_interpolation_type_step )
				{
					sampler.interpolation = Gltf::Interpolation::Step;
				}
				else if ( gltfSampler.interpolation == cgltf_interpolation_type_cubic_spline )
				{
					sampler.interpolation = Gltf::Interpolation::CubicSpline;
					// 未実装
				}


				sampler.keyFrames.reserve( gltfSampler.input->count );
				for ( size_t frameIndex = 0; frameIndex < gltfSampler.input->count; ++frameIndex )
				{
					Gltf::KeyFrame& keyFrame = sampler.keyFrames.emplace_back();
					cgltf_accessor_read_float( gltfSampler.input, frameIndex, &keyFrame.time, 1 );

					if ( gltfSampler.output->type == cgltf_type_vec3 )
					{
						cgltf_accessor_read_float( gltfSampler.output, frameIndex, (float*)&keyFrame.xyzw.x, 3 );
					}
					else if ( gltfSampler.output->type == cgltf_type_vec4 )
					{
						cgltf_accessor_read_float( gltfSampler.output, frameIndex, (float*)&keyFrame.xyzw.x, 4 );
					}
				}

			}


			animation.channels.reserve( gltfAnimation.channels_count );
			for ( cgltf_animation_channel& gltfChannel : std::span( gltfAnimation.channels, gltfAnimation.channels_count ) )
			{
				Gltf::Channel& channel = animation.channels.emplace_back();
				size_t nodeIndex = (size_t)(gltfChannel.target_node - gltfData->nodes);
				size_t samplerIndex = (size_t)(gltfChannel.sampler - gltfAnimation.samplers);

				channel.node = &inout_mesh.nodes[nodeIndex];
				channel.sampler = &animation.samplers[samplerIndex];
				if ( gltfChannel.target_path == cgltf_animation_path_type_translation )
				{
					channel.type = Gltf::ChannelType::Translate;
				}
				else if ( gltfChannel.target_path == cgltf_animation_path_type_rotation )
				{
					channel.type = Gltf::ChannelType::Rotate;
				}
				else if ( gltfChannel.target_path == cgltf_animation_path_type_scale )
				{
					channel.type = Gltf::ChannelType::Scale;
				}
			}
			
		}
	}


	

public:
	static std::unique_ptr<GltfModel> load( const std::vector<char>& fileBuf, ID3D11Device* pDevice )
	{
		cgltf_options gltfOptions = {};
		cgltf_data* gltfData = nullptr;
		cgltf_result result = cgltf_parse( &gltfOptions, fileBuf.data(), fileBuf.size(), &gltfData );
		if ( result != cgltf_result_success ) return nullptr;

		result = cgltf_load_buffers( &gltfOptions, gltfData, nullptr );
		if ( result != cgltf_result_success )
		{
			cgltf_free( gltfData );
			return nullptr;
		}

		std::unique_ptr<GltfModel> model = std::make_unique<GltfModel>();
		model->nodes.resize( gltfData->nodes_count );
		model->meshes.resize( gltfData->meshes_count );
		model->skins.resize( gltfData->skins_count );
		model->animations.resize( gltfData->animations_count );

		loadNodes( gltfData, *model );
		loadMeshes( gltfData, pDevice, *model );
		loadSkins( gltfData, *model );
		loadAnimations( gltfData, *model );

		cgltf_free( gltfData );
		return model;
	}
};





DX11Device g_device;

std::unique_ptr<GltfModel> g_model;
GltfInstance g_modelInst;


Texture g_texture;

AnimeShader g_animeShader;
Camera g_camera;



// メインの動作処理
void update( HWND hWnd )
{
	static bool s_useCursor = true;

	// マウス視点操作(ONの時だけ)
	if ( s_useCursor )
	{
		POINT cursorPos;
		GetCursorPos( &cursorPos );

		// ウィンドウの中心座標を計算
		RECT rect;
		GetWindowRect( hWnd, &rect );
		int centerX = rect.left + (rect.right - rect.left) / 2;
		int centerY = rect.top + (rect.bottom - rect.top) / 2;

		// 中心からのズレ(移動量)を計算
		float deltaX = (float)(cursorPos.x - centerX);
		float deltaY = (float)(cursorPos.y - centerY);

		// カメラを回転させる
		float mouseSensitivity = 0.002f;
		if ( deltaX != 0 || deltaY != 0 ) {
			// Y方向のズレ → Pitch(上下)、X方向のズレ → Yaw(左右)
			g_camera.rotate( deltaY * mouseSensitivity, -deltaX * mouseSensitivity );
		}

		// カーソルを強制的に中心に戻す
		SetCursorPos( centerX, centerY );
	}



	float speed = 0.05f;
	float rotSpeed = 0.02f;


	float moveX = 0.0f;
	float moveZ = 0.0f;
	float moveY = 0.0f;

	if ( GetAsyncKeyState( 'W' ) & 0x8000 ) moveZ += speed; // 前
	if ( GetAsyncKeyState( 'S' ) & 0x8000 ) moveZ -= speed; // 後
	if ( GetAsyncKeyState( 'D' ) & 0x8000 ) moveX -= speed; // 右
	if ( GetAsyncKeyState( 'A' ) & 0x8000 ) moveX += speed; // 左
	if ( GetAsyncKeyState( VK_SPACE ) & 0x8000 ) moveY += speed; // 上昇
	if ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) moveY -= speed; // 下降

	if ( moveX != 0 || moveY != 0 || moveZ != 0 )
		g_camera.move( moveX, moveY, moveZ );


	float rotPitch = 0.0f;
	float rotYaw = 0.0f;

	if ( GetAsyncKeyState( VK_UP ) & 0x8000 ) rotPitch -= rotSpeed;
	if ( GetAsyncKeyState( VK_DOWN ) & 0x8000 ) rotPitch += rotSpeed;
	if ( GetAsyncKeyState( VK_RIGHT ) & 0x8000 ) rotYaw += rotSpeed;
	if ( GetAsyncKeyState( VK_LEFT ) & 0x8000 ) rotYaw -= rotSpeed;

	if ( GetAsyncKeyState( 'E' ) & 0x01 ) s_useCursor = !s_useCursor;

	if ( rotPitch != 0 || rotYaw != 0 )
		g_camera.rotate( rotPitch, rotYaw );



	if ( GetAsyncKeyState( VK_ESCAPE ) & 0x8000 )
	{
		PostMessage( hWnd, WM_CLOSE, 0, 0 );
	}

	// カメラ位置をウィンドウタイトルに表示しておく
	{
		std::wstring str;
		str = L"pos: ";
		str += std::to_wstring( g_camera.getPos().x ) + L", " +
			std::to_wstring( g_camera.getPos().y ) + L", " +
			std::to_wstring( g_camera.getPos().z );
		SetWindowText( hWnd, str.c_str() );
	}
}



void draw()
{
	// bind
	g_texture.bind( g_device.getContext() );


	DirectX::XMMATRIX view = g_camera.getViewMatrix();
	DirectX::XMMATRIX proj = DirectX::XMMatrixPerspectiveFovRH( DirectX::XM_PIDIV4, 800.0f / 600.0f, 0.01f, 100.0f );



	// gltf表示
	{
		g_modelInst.update( 1.f / 60 );


		DirectX::XMMATRIX objectWorld = DirectX::XMMatrixIdentity();


		for ( auto* node : g_model->meshNodes )
		{
			if ( !node->mesh ) continue;

			DirectX::XMMATRIX finalWorld;
			std::vector<DirectX::XMMATRIX>* pBoneMatrices = nullptr;
			static std::vector<DirectX::XMMATRIX> identityBones( 1, DirectX::XMMatrixIdentity() );

			if ( node->skin )
			{
				finalWorld = objectWorld;

				size_t skinIndex = node->skin - &g_model->skins[0];
				pBoneMatrices = &g_modelInst.skins[skinIndex].bonesTransformMats;
			}
			else
			{

				finalWorld = DirectX::XMMatrixMultiply( node->globalTransform, objectWorld );
				pBoneMatrices = &identityBones;
			}

			g_animeShader.bind( g_device.getContext(), finalWorld, view, proj, *pBoneMatrices );

			// このノードのメッシュを描画
			for ( auto& prim : node->mesh->primitives )
			{
				prim.draw( g_device.getContext() );
			}
		}

		//g_modelInst.draw( g_device.getContext(), g_animeShader, objectWorld, view, proj );
	}


}






LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
	switch ( message ) {
	case WM_DESTROY:
		PostQuitMessage( 0 );
		break;
	default:
		return DefWindowProc( hWnd, message, wParam, lParam );
	}
	return 0;
}





int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
	WNDCLASSEX wc = { 0 };
	wc.cbSize = sizeof( WNDCLASSEX );
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszClassName = L"DX11WindowClass";

	if ( !RegisterClassEx( &wc ) ) {
		return -1;
	}


	RECT rc = { 0, 0, 800, 600 }; // クライアント領域のサイズ
	AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );

	HWND hWnd = CreateWindow(
		L"DX11WindowClass",
		L"DirectX11 GLTF",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		rc.right - rc.left, rc.bottom - rc.top,
		NULL, NULL, hInstance, NULL
	);

	if ( !hWnd ) {
		return -1;
	}

	if ( !g_device.initialize( hWnd, 800, 600 ) )
	{
		return -1;
	}

	g_animeShader.initialize( g_device.getDevice(), L"AnimeShader.hlsl" );

	g_texture.createCheckered( g_device.getDevice() );





	// gltf (glb)の読み込み。一度メモリに読むタイプ
	{
		std::ifstream file( "monkey.glb", std::ios::binary );
		file.unsetf( std::ios::skipws );

		file.seekg( 0, std::ios::end );
		auto fileSize = file.tellg();
		file.seekg( 0, std::ios::beg );
		
		std::vector< char > fileBuf;
		fileBuf.reserve( fileSize );
		fileBuf.insert( fileBuf.begin(), std::istream_iterator<char>( file ), (std::istream_iterator<char>()) );

		file.close();



		g_model = GLTFLoader::load( fileBuf, g_device.getDevice() );
		g_modelInst.initialize( g_model.get() );
	}



	ShowWindow( hWnd, nCmdShow );



	MSG msg = { 0 };
	while ( msg.message != WM_QUIT ) {
		if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		else
		{
			g_device.beginRender( 0.0f, 0.2f, 0.4f );

			update( hWnd );
			draw();

			g_device.endRender();
		}
	}



	return (int)msg.wParam;
}




■AnimeShader.hlsl

cbuffer ConstantBuffer : register(b0)
{
    column_major matrix WorldViewProjection;
}

cbuffer BoneBuffer : register(b1)
{
    column_major matrix BoneTransforms[128];
}



Texture2D myTexture : register(t0);
SamplerState mySampler : register(s0);

struct VS_INPUT
{
    float3 Pos : POSITION;
    float3 Normal : Normal;
    float2 UV : TEXCOORD;
    float4 Weights : BLENDWEIGHT;
    uint4 Indices : BLENDINDICES;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 UV : TEXCOORD;
    float3 Normal : NORMAL;
    float4 Weights : BLENDWEIGHT;
};

PS_INPUT VS(VS_INPUT input)
{
    PS_INPUT output = (PS_INPUT) 0;
    
    matrix skinMatrix =
        BoneTransforms[input.Indices.x] * input.Weights.x +
        BoneTransforms[input.Indices.y] * input.Weights.y +
        BoneTransforms[input.Indices.z] * input.Weights.z +
        BoneTransforms[input.Indices.w] * input.Weights.w;

    float4 skinnedPos = mul(float4(input.Pos, 1.0f), skinMatrix);
    output.Pos = mul(skinnedPos, WorldViewProjection);
    
    output.UV = input.UV;

    float3 skinnedNormal = mul(input.Normal, (float3x3) skinMatrix);
    output.Normal = normalize(skinnedNormal);
    
    output.Weights = input.Weights;
    return output;
}

float4 PS(PS_INPUT input) : SV_Target
{
    return myTexture.Sample(mySampler, input.UV);
}

0 件のコメント: