■右手系のgltfを左手系に、読み込み時に直す
ソースコードの[**]というコメントがあるところが左手系への変換処理です
●行列:
DirectX::XMMATRIX s = DirectX::XMMatrixScaling( 1, 1, -1 );
node.globalTransform = s * node.globalTransform * s;
DirectX::XMMATRIX s = DirectX::XMMatrixScaling( 1, 1, -1 );
node.localTransform = s * node.localTransform * s;
DirectX::XMMATRIX s = DirectX::XMMatrixScaling( 1, 1, -1 );
mj.inverseBindMat = s * mj.inverseBindMat * s;
●モデルデータ
modelVertices[i].normal.z *= -1;
modelVertices[i].pos.z *= -1;
modelVertices[i].texcoord.y = 1 - modelVertices[i].texcoord.y;
またカリングが逆になるので
std::swap( modelIndices[i + 1], modelIndices[i + 2] );
で頂点を逆回転にしている
●アニメーションデータ
keyFrame.xyzw.x *= -1;
keyFrame.xyzw.y *= -1;
アニメデータは回転と位置のxyを反転する。
gltfはsamplerを使いまわすことがあるらしく、ちょっと処理がややこしい。
■そのほか地味に引っかかったところ
struct Vertex
{
DirectX::XMFLOAT4 jointWeights = { 1,0,0,0 };
};
ボーンのウェイト初期化時に[0]=1としている。アニメーションのないgltfを読み込んで表示しようとしても表示できなかった(AnimeShader.hlslの都合)。
■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_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 = FALSE; // 反時計回りが表
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::XMMatrixLookAtLH( 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] );
}
}
}
#if 0
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 );
}
}
}
#endif
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 );
// [**]
DirectX::XMMATRIX s = DirectX::XMMatrixScaling( 1, 1, -1 );
node.globalTransform = s * node.globalTransform * s;
}
if ( gltfNode.has_matrix )
{
node.localTransform = DirectX::XMLoadFloat4x4( (DirectX::XMFLOAT4X4*)gltfNode.matrix );
// [**]
DirectX::XMMATRIX s = DirectX::XMMatrixScaling( 1, 1, -1 );
node.localTransform = s * node.localTransform * s;
}
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 );
// [**]
modelVertices[i].normal.z *= -1;
modelVertices[i].pos.z *= -1;
modelVertices[i].texcoord.y = 1 - modelVertices[i].texcoord.y;
}
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 );
// [**]
std::swap( modelIndices[i + 1], modelIndices[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 );
// [**]
DirectX::XMMATRIX s = DirectX::XMMatrixScaling( 1, 1, -1 );
mj.inverseBindMat = s * mj.inverseBindMat * s;
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;
}
}
}
// [**]
for ( auto& animation : inout_mesh.animations )
{
std::vector<bool> samplerProcessed( animation.samplers.size(), false );
for ( auto& channel : animation.channels )
{
size_t samplerIdx = channel.sampler - &animation.samplers[0];
if ( samplerProcessed[samplerIdx] == false )
{
if ( channel.type == Gltf::ChannelType::Translate )
{
samplerProcessed[samplerIdx] = true;
for ( auto& keyFrame : channel.sampler->keyFrames ) keyFrame.xyzw.z *= -1;
}
else if ( channel.type == Gltf::ChannelType::Rotate )
{
samplerProcessed[samplerIdx] = true;
for ( auto& keyFrame : channel.sampler->keyFrames )
{
keyFrame.xyzw.x *= -1;
keyFrame.xyzw.y *= -1;
}
}
}
}
}
}
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::XMMatrixPerspectiveFovLH( DirectX::XM_PIDIV4, 800.0f / 600.0f, 0.01f, 100.0f );
// gltf表示
{
g_modelInst.update( 1.f / 60 );
DirectX::XMMATRIX objectWorld = DirectX::XMMatrixIdentity();
#if 1
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() );
}
}
#endif
//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( "rabbit_uv.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 件のコメント:
コメントを投稿