mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-08-05 12:58:53 +00:00
1293 lines
46 KiB
C++
1293 lines
46 KiB
C++
#include "Viewer.h"
|
|
|
|
#include <DirectXColors.h>
|
|
#include <d3dcompiler.h>
|
|
#include <spdlog/spdlog.h>
|
|
|
|
using namespace DirectX;
|
|
|
|
namespace {
|
|
|
|
constexpr bool isPow2(uint64_t value) {
|
|
return (value == 0) ? false : ((value & (value - 1)) == 0);
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr T alignPow2(T value, uint64_t alignment) {
|
|
assert(isPow2(alignment));
|
|
return ((value + static_cast<T>(alignment) - 1) &
|
|
~(static_cast<T>(alignment) - 1));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Viewer::Viewer(HWND window, tinygltf::Model* pModel)
|
|
: pModel_(pModel), directFenceValue_(0), copyFenceValue_(0) {
|
|
initDirectX(window);
|
|
buildRenderTargets();
|
|
buildResources();
|
|
|
|
// TODO:
|
|
HRESULT hr = S_OK;
|
|
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Width =
|
|
alignPow2(sizeof(PBRMetallicRoughness),
|
|
D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
|
|
IID_PPV_ARGS(&pCameraBuffer_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
void Viewer::update(double deltaTime) {
|
|
if (pDirectFence_->GetCompletedValue() < directFenceValue_) {
|
|
auto event = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
|
|
pDirectFence_->SetEventOnCompletion(directFenceValue_, event);
|
|
WaitForSingleObject(event, INFINITE);
|
|
CloseHandle(event);
|
|
}
|
|
|
|
// TODO:
|
|
void* pData;
|
|
pCameraBuffer_->Map(0, nullptr, &pData);
|
|
|
|
auto* pCameraData = static_cast<Camera*>(pData);
|
|
|
|
constexpr auto kRadius = 3.0;
|
|
static auto degree = 0.0;
|
|
degree += 10.0 * deltaTime;
|
|
auto radian = degree * XM_PI / 180.0;
|
|
|
|
XMMATRIX P =
|
|
XMMatrixPerspectiveFovRH(90.0f * XM_PI / 180.0f, 1.0f, 0.01f, 100.0f);
|
|
XMStoreFloat4x4(&pCameraData->P, XMMatrixTranspose(P));
|
|
|
|
XMMATRIX V = XMMatrixLookAtRH(
|
|
XMVectorSet(static_cast<float>(kRadius * cos(radian)), 0.0f,
|
|
static_cast<float>(kRadius * sin(radian)), 1.0f),
|
|
XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f));
|
|
XMStoreFloat4x4(&pCameraData->V, XMMatrixTranspose(V));
|
|
|
|
XMMATRIX VP = XMMatrixMultiply(V, P);
|
|
XMStoreFloat4x4(&pCameraData->VP, XMMatrixTranspose(VP));
|
|
pCameraBuffer_->Unmap(0, nullptr);
|
|
}
|
|
|
|
void Viewer::render(double deltaTime) {
|
|
const auto kSwapChainBackBufferIndex =
|
|
pSwapChain_->GetCurrentBackBufferIndex();
|
|
|
|
auto pDirectCommandAllocator =
|
|
pDirectCommandAllocators_[kSwapChainBackBufferIndex].Get();
|
|
|
|
pDirectCommandAllocator->Reset();
|
|
pDirectCommandList_->Reset(pDirectCommandAllocator, nullptr);
|
|
|
|
D3D12_VIEWPORT viewport = {};
|
|
viewport.Width = 512.0;
|
|
viewport.Height = 512.0;
|
|
viewport.MaxDepth = 1.0f;
|
|
|
|
pDirectCommandList_->RSSetViewports(1, &viewport);
|
|
|
|
D3D12_RECT scissorRect = {};
|
|
scissorRect.right = 512;
|
|
scissorRect.bottom = 512;
|
|
|
|
pDirectCommandList_->RSSetScissorRects(1, &scissorRect);
|
|
|
|
auto& renderTargets = renderTargets_[kSwapChainBackBufferIndex];
|
|
|
|
{
|
|
auto& renderTarget = renderTargets[RENDER_PASS_TYPE_PRESENT];
|
|
auto pTexture = renderTarget.pTexture.Get();
|
|
auto rtvDescriptor = renderTarget.viewDescriptor;
|
|
|
|
{
|
|
D3D12_RESOURCE_BARRIER resourceBarrier = {};
|
|
resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
resourceBarrier.Transition.pResource = pTexture;
|
|
resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
|
resourceBarrier.Transition.StateAfter =
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
|
|
pDirectCommandList_->ResourceBarrier(1, &resourceBarrier);
|
|
}
|
|
|
|
pDirectCommandList_->OMSetRenderTargets(1, &rtvDescriptor, false, nullptr);
|
|
pDirectCommandList_->ClearRenderTargetView(rtvDescriptor, Colors::Black, 0,
|
|
nullptr);
|
|
|
|
auto& scene = pModel_->scenes[pModel_->defaultScene];
|
|
for (auto nodeIndex : scene.nodes) {
|
|
drawNode(nodeIndex);
|
|
}
|
|
|
|
{
|
|
D3D12_RESOURCE_BARRIER resourceBarrier = {};
|
|
resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
resourceBarrier.Transition.pResource = pTexture;
|
|
resourceBarrier.Transition.StateBefore =
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
|
|
|
pDirectCommandList_->ResourceBarrier(1, &resourceBarrier);
|
|
}
|
|
}
|
|
|
|
pDirectCommandList_->Close();
|
|
|
|
ID3D12CommandList* pCommandLists[] = {pDirectCommandList_.Get()};
|
|
pDirectCommandQueue_->ExecuteCommandLists(_countof(pCommandLists),
|
|
pCommandLists);
|
|
pDirectCommandQueue_->Signal(pDirectFence_.Get(), ++directFenceValue_);
|
|
pSwapChain_->Present(0, 0);
|
|
}
|
|
|
|
void Viewer::initDirectX(HWND window) {
|
|
HRESULT hr = S_OK;
|
|
|
|
{
|
|
UINT flags = 0;
|
|
|
|
#if _DEBUG
|
|
ComPtr<ID3D12Debug> pDebug;
|
|
hr = D3D12GetDebugInterface(IID_PPV_ARGS(&pDebug));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
if (pDebug) {
|
|
flags |= DXGI_CREATE_FACTORY_DEBUG;
|
|
pDebug->EnableDebugLayer();
|
|
}
|
|
#endif
|
|
|
|
hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(&pFactory_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
UINT i = 0;
|
|
while (DXGI_ERROR_NOT_FOUND != pFactory_->EnumAdapters1(i, &pAdapter_)) {
|
|
DXGI_ADAPTER_DESC1 adapterDesc;
|
|
pAdapter_->GetDesc1(&adapterDesc);
|
|
|
|
if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
|
|
continue;
|
|
}
|
|
|
|
hr = D3D12CreateDevice(pAdapter_.Get(), D3D_FEATURE_LEVEL_11_0,
|
|
IID_PPV_ARGS(&pDevice_));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
if (pDevice_) {
|
|
break;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
{
|
|
D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {};
|
|
commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
|
commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateCommandQueue(&commandQueueDesc,
|
|
IID_PPV_ARGS(&pDirectCommandQueue_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
hr = pDevice_->CreateFence(directFenceValue_, D3D12_FENCE_FLAG_NONE,
|
|
IID_PPV_ARGS(&pDirectFence_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {};
|
|
commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
|
|
commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateCommandQueue(&commandQueueDesc,
|
|
IID_PPV_ARGS(&pCopyCommandQueue_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
hr = pDevice_->CreateFence(copyFenceValue_, D3D12_FENCE_FLAG_NONE,
|
|
IID_PPV_ARGS(&pCopyFence_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
ComPtr<IDXGIOutput> pOutput;
|
|
hr = pAdapter_->EnumOutputs(0, &pOutput);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
UINT modeCount = 1;
|
|
DXGI_MODE_DESC modeDesc;
|
|
hr = pOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &modeCount,
|
|
&modeDesc);
|
|
assert(SUCCEEDED(hr) || hr == DXGI_ERROR_MORE_DATA);
|
|
|
|
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
|
|
swapChainDesc.BufferDesc = modeDesc;
|
|
|
|
// TODO:
|
|
swapChainDesc.BufferDesc.Width = 512;
|
|
swapChainDesc.BufferDesc.Height = 512;
|
|
|
|
swapChainDesc.SampleDesc = {1, 0};
|
|
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapChainDesc.BufferCount = DXVIEW_SWAP_CHAIN_BUFFER_COUNT;
|
|
swapChainDesc.OutputWindow = window;
|
|
swapChainDesc.Windowed = true;
|
|
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
|
|
|
ComPtr<IDXGISwapChain> pSwapChain;
|
|
hr = pFactory_->CreateSwapChain(pDirectCommandQueue_.Get(), &swapChainDesc,
|
|
&pSwapChain);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
hr = pSwapChain.As(&pSwapChain_);
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
for (auto i = 0; i != DXVIEW_SWAP_CHAIN_BUFFER_COUNT; ++i) {
|
|
hr = pSwapChain_->GetBuffer(i, IID_PPV_ARGS(&pSwapChainBuffers_[i]));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
for (auto i = 0; i != DXVIEW_SWAP_CHAIN_BUFFER_COUNT; ++i) {
|
|
hr = pDevice_->CreateCommandAllocator(
|
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
IID_PPV_ARGS(&pDirectCommandAllocators_[i]));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
hr = pDevice_->CreateCommandList(
|
|
0, D3D12_COMMAND_LIST_TYPE_DIRECT, pDirectCommandAllocators_[0].Get(),
|
|
nullptr, IID_PPV_ARGS(&pDirectCommandList_));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
hr = pDirectCommandList_->Close();
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
hr = pDevice_->CreateCommandAllocator(
|
|
D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&pCopyCommandAllocator_));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
hr = pDevice_->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY,
|
|
pCopyCommandAllocator_.Get(), nullptr,
|
|
IID_PPV_ARGS(&pCopyCommandList_));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
hr = pCopyCommandList_->Close();
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
for (auto i = 0; i != D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; ++i) {
|
|
auto descriptorHeapType = static_cast<D3D12_DESCRIPTOR_HEAP_TYPE>(i);
|
|
descriptorIncrementSize_[i] =
|
|
pDevice_->GetDescriptorHandleIncrementSize(descriptorHeapType);
|
|
}
|
|
}
|
|
|
|
void Viewer::buildRenderTargets() {
|
|
HRESULT hr = S_OK;
|
|
|
|
for (auto i = 0; i != DXVIEW_SWAP_CHAIN_BUFFER_COUNT; ++i) {
|
|
auto& pRTVDescriptorHeap = pRTVDescriptorHeaps_[i];
|
|
|
|
{
|
|
D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
|
|
descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
|
descriptorHeapDesc.NumDescriptors = 1;
|
|
descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateDescriptorHeap(&descriptorHeapDesc,
|
|
IID_PPV_ARGS(&pRTVDescriptorHeap));
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
auto& renderTargets = renderTargets_[i];
|
|
auto RTVDescriptor =
|
|
pRTVDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
|
|
|
|
for (auto j = 0; j != RENDER_PASS_TYPE_COUNT; ++j) {
|
|
RenderTarget renderTarget = {};
|
|
|
|
if (j != RENDER_PASS_TYPE_PRESENT) {
|
|
assert(false);
|
|
} else {
|
|
renderTarget.pTexture = pSwapChainBuffers_[i];
|
|
}
|
|
|
|
renderTarget.viewDescriptor = RTVDescriptor;
|
|
|
|
pDevice_->CreateRenderTargetView(pSwapChainBuffers_[i].Get(), nullptr,
|
|
RTVDescriptor);
|
|
RTVDescriptor.ptr +=
|
|
descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_RTV];
|
|
|
|
renderTargets.push_back(renderTarget);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Viewer::buildResources() {
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = pCopyCommandList_->Reset(pCopyCommandAllocator_.Get(), nullptr);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
std::vector<ComPtr<ID3D12Resource> > stagingResources;
|
|
stagingResources.reserve(256);
|
|
|
|
buildBuffers(&stagingResources);
|
|
buildImages(&stagingResources);
|
|
|
|
hr = pCopyCommandList_->Close();
|
|
assert(SUCCEEDED(hr));
|
|
|
|
ID3D12CommandList* pCommandLists[] = {pCopyCommandList_.Get()};
|
|
pCopyCommandQueue_->ExecuteCommandLists(_countof(pCommandLists),
|
|
pCommandLists);
|
|
pCopyCommandQueue_->Signal(pCopyFence_.Get(), ++copyFenceValue_);
|
|
|
|
buildSamplerDescs();
|
|
buildMaterials();
|
|
buildMeshes();
|
|
buildNodes();
|
|
|
|
if (pCopyFence_->GetCompletedValue() < copyFenceValue_) {
|
|
auto event = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
|
|
pCopyFence_->SetEventOnCompletion(copyFenceValue_, event);
|
|
WaitForSingleObject(event, INFINITE);
|
|
CloseHandle(event);
|
|
}
|
|
}
|
|
|
|
void Viewer::buildBuffers(
|
|
std::vector<ComPtr<ID3D12Resource> >* pStagingResources) {
|
|
HRESULT hr = S_OK;
|
|
|
|
for (auto& gltfBuffer : pModel_->buffers) {
|
|
ComPtr<ID3D12Resource> pDstBuffer;
|
|
{
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Alignment = 0;
|
|
resourceDesc.Width = gltfBuffer.data.size();
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pDstBuffer));
|
|
assert(SUCCEEDED(hr));
|
|
pBuffers_.push_back(pDstBuffer);
|
|
}
|
|
|
|
ComPtr<ID3D12Resource> pSrcBuffer;
|
|
{
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Alignment = 0;
|
|
resourceDesc.Width = gltfBuffer.data.size();
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
|
|
IID_PPV_ARGS(&pSrcBuffer));
|
|
assert(SUCCEEDED(hr));
|
|
pStagingResources->push_back(pSrcBuffer);
|
|
|
|
void* pData;
|
|
hr = pSrcBuffer->Map(0, nullptr, &pData);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
memcpy(pData, &gltfBuffer.data[0], gltfBuffer.data.size());
|
|
}
|
|
|
|
pCopyCommandList_->CopyBufferRegion(pDstBuffer.Get(), 0, pSrcBuffer.Get(),
|
|
0, gltfBuffer.data.size());
|
|
}
|
|
}
|
|
|
|
void Viewer::buildImages(
|
|
std::vector<ComPtr<ID3D12Resource> >* pStagingResources) {
|
|
HRESULT hr = S_OK;
|
|
|
|
for (auto& gltfImage : pModel_->images) {
|
|
ComPtr<ID3D12Resource> pDstTexture;
|
|
{
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
resourceDesc.Alignment = 0;
|
|
resourceDesc.Width = gltfImage.width;
|
|
resourceDesc.Height = gltfImage.height;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
|
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pDstTexture));
|
|
assert(SUCCEEDED(hr));
|
|
pTextures_.push_back(pDstTexture);
|
|
}
|
|
|
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
|
UINT rowCount;
|
|
UINT64 rowSize;
|
|
UINT64 size;
|
|
pDevice_->GetCopyableFootprints(&pDstTexture->GetDesc(), 0, 1, 0,
|
|
&footprint, &rowCount, &rowSize, &size);
|
|
|
|
ComPtr<ID3D12Resource> pSrcBuffer;
|
|
{
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Alignment = 0;
|
|
resourceDesc.Width = size;
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
|
|
IID_PPV_ARGS(&pSrcBuffer));
|
|
assert(SUCCEEDED(hr));
|
|
pStagingResources->push_back(pSrcBuffer);
|
|
|
|
void* pData;
|
|
hr = pSrcBuffer->Map(0, nullptr, &pData);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
for (auto i = 0; i != rowCount; ++i) {
|
|
memcpy(static_cast<uint8_t*>(pData) + rowSize * i,
|
|
&gltfImage.image[0] + gltfImage.width * gltfImage.component * i,
|
|
gltfImage.width * gltfImage.component);
|
|
}
|
|
}
|
|
|
|
D3D12_TEXTURE_COPY_LOCATION dstCopyLocation = {};
|
|
dstCopyLocation.pResource = pDstTexture.Get();
|
|
dstCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
dstCopyLocation.SubresourceIndex = 0;
|
|
|
|
D3D12_TEXTURE_COPY_LOCATION srcCopyLocation = {};
|
|
srcCopyLocation.pResource = pSrcBuffer.Get();
|
|
srcCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
srcCopyLocation.PlacedFootprint = footprint;
|
|
|
|
pCopyCommandList_->CopyTextureRegion(&dstCopyLocation, 0, 0, 0,
|
|
&srcCopyLocation, nullptr);
|
|
}
|
|
}
|
|
|
|
void Viewer::buildSamplerDescs() {
|
|
for (auto& glTFSampler : pModel_->samplers) {
|
|
D3D12_SAMPLER_DESC samplerDesc = {};
|
|
switch (glTFSampler.minFilter) {
|
|
case TINYGLTF_TEXTURE_FILTER_NEAREST:
|
|
if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST)
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
|
break;
|
|
case TINYGLTF_TEXTURE_FILTER_LINEAR:
|
|
if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST)
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
break;
|
|
case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST:
|
|
if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST)
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
|
|
break;
|
|
case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST:
|
|
if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST)
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
break;
|
|
case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR:
|
|
if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST)
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR;
|
|
break;
|
|
case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR:
|
|
if (glTFSampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST)
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
|
break;
|
|
default:
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
break;
|
|
}
|
|
|
|
auto toTextureAddressMode = [](int wrap) {
|
|
switch (wrap) {
|
|
case TINYGLTF_TEXTURE_WRAP_REPEAT:
|
|
return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
|
case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE:
|
|
return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
case TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT:
|
|
return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
|
|
default:
|
|
assert(false);
|
|
return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
|
}
|
|
};
|
|
|
|
samplerDesc.AddressU = toTextureAddressMode(glTFSampler.wrapS);
|
|
samplerDesc.AddressV = toTextureAddressMode(glTFSampler.wrapT);
|
|
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
|
samplerDesc.MaxLOD = 256;
|
|
|
|
samplerDescs_.push_back(samplerDesc);
|
|
}
|
|
}
|
|
|
|
void Viewer::buildMaterials() {
|
|
HRESULT hr = S_OK;
|
|
|
|
// Build materials.
|
|
for (auto& glTFMaterial : pModel_->materials) {
|
|
Material material = {};
|
|
|
|
// Set a material name.
|
|
material.name = glTFMaterial.name;
|
|
|
|
// Set a blend desc.
|
|
auto& blendDesc = material.blendDesc;
|
|
if (glTFMaterial.alphaMode == "BLEND") {
|
|
blendDesc.RenderTarget[0].BlendEnable = true;
|
|
blendDesc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
|
blendDesc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
|
blendDesc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
|
|
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
|
|
blendDesc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO;
|
|
blendDesc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
|
blendDesc.RenderTarget[0].LogicOp = D3D12_LOGIC_OP_NOOP;
|
|
} else if (glTFMaterial.alphaMode == "MASK") {
|
|
assert(false);
|
|
}
|
|
|
|
blendDesc.RenderTarget[0].RenderTargetWriteMask =
|
|
D3D12_COLOR_WRITE_ENABLE_ALL;
|
|
|
|
// Set a rasterizer desc.
|
|
auto& rasterizerDesc = material.rasterizerDesc;
|
|
rasterizerDesc.FillMode = D3D12_FILL_MODE_SOLID;
|
|
|
|
if (glTFMaterial.doubleSided) {
|
|
rasterizerDesc.CullMode = D3D12_CULL_MODE_NONE;
|
|
} else {
|
|
rasterizerDesc.CullMode = D3D12_CULL_MODE_BACK;
|
|
}
|
|
|
|
rasterizerDesc.FrontCounterClockwise = true;
|
|
rasterizerDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
|
rasterizerDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
|
rasterizerDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
|
rasterizerDesc.ForcedSampleCount = 0;
|
|
rasterizerDesc.ConservativeRaster =
|
|
D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
|
|
|
auto& pBuffer = material.pBuffer;
|
|
auto& pBufferData = material.pBufferData;
|
|
{
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Width =
|
|
alignPow2(sizeof(PBRMetallicRoughness),
|
|
D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pBuffer));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
hr = pBuffer->Map(0, nullptr, &pBufferData);
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
auto& pSRVDescriptorHeap = material.pSRVDescriptorHeap;
|
|
{
|
|
D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
|
|
|
|
descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
|
descriptorHeapDesc.NumDescriptors = 5;
|
|
descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
|
|
|
hr = pDevice_->CreateDescriptorHeap(&descriptorHeapDesc,
|
|
IID_PPV_ARGS(&pSRVDescriptorHeap));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
auto& pSamplerDescriptorHeap = material.pSamplerDescriptorHeap;
|
|
{
|
|
D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
|
|
|
|
descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
|
|
descriptorHeapDesc.NumDescriptors = 5;
|
|
descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
|
|
|
hr = pDevice_->CreateDescriptorHeap(
|
|
&descriptorHeapDesc, IID_PPV_ARGS(&pSamplerDescriptorHeap));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
auto& glTFPBRMetallicRoughness = glTFMaterial.pbrMetallicRoughness;
|
|
{
|
|
auto pPBRMetallicRoughness =
|
|
static_cast<PBRMetallicRoughness*>(pBufferData);
|
|
|
|
auto& baseColorFactor = pPBRMetallicRoughness->baseColorFactor;
|
|
|
|
baseColorFactor.x =
|
|
static_cast<float>(glTFPBRMetallicRoughness.baseColorFactor[0]);
|
|
baseColorFactor.y =
|
|
static_cast<float>(glTFPBRMetallicRoughness.baseColorFactor[1]);
|
|
baseColorFactor.z =
|
|
static_cast<float>(glTFPBRMetallicRoughness.baseColorFactor[2]);
|
|
baseColorFactor.w =
|
|
static_cast<float>(glTFPBRMetallicRoughness.baseColorFactor[3]);
|
|
|
|
auto& glTFBaseColorTexture = glTFPBRMetallicRoughness.baseColorTexture;
|
|
auto& baseColorTexture = pPBRMetallicRoughness->baseColorTexture;
|
|
if (glTFBaseColorTexture.index >= 0) {
|
|
baseColorTexture.textureIndex = 0;
|
|
baseColorTexture.samplerIndex = 0;
|
|
} else {
|
|
baseColorTexture.textureIndex = -1;
|
|
baseColorTexture.samplerIndex = -1;
|
|
}
|
|
|
|
pPBRMetallicRoughness->metallicFactor =
|
|
static_cast<float>(glTFPBRMetallicRoughness.metallicFactor);
|
|
pPBRMetallicRoughness->roughnessFactor =
|
|
static_cast<float>(glTFPBRMetallicRoughness.roughnessFactor);
|
|
|
|
auto& glTFMetallicRoughnessTexture =
|
|
glTFPBRMetallicRoughness.metallicRoughnessTexture;
|
|
auto& metallicRoughnessTexture =
|
|
pPBRMetallicRoughness->metallicRoughnessTexture;
|
|
if (glTFMetallicRoughnessTexture.index >= 0) {
|
|
metallicRoughnessTexture.textureIndex = 1;
|
|
metallicRoughnessTexture.samplerIndex = 1;
|
|
} else {
|
|
metallicRoughnessTexture.textureIndex = -1;
|
|
metallicRoughnessTexture.samplerIndex = -1;
|
|
}
|
|
}
|
|
|
|
auto srvDescriptor =
|
|
pSRVDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
|
|
auto samplerDescriptor =
|
|
pSamplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
|
|
|
|
auto& glTFBaseColorTexture = glTFPBRMetallicRoughness.baseColorTexture;
|
|
if (glTFBaseColorTexture.index >= 0) {
|
|
auto& glTFTexture = pModel_->textures[glTFBaseColorTexture.index];
|
|
|
|
auto pTexture = pTextures_[glTFTexture.source].Get();
|
|
pDevice_->CreateShaderResourceView(pTexture, nullptr, srvDescriptor);
|
|
|
|
auto& samplerDesc = samplerDescs_[glTFTexture.sampler];
|
|
pDevice_->CreateSampler(&samplerDesc, samplerDescriptor);
|
|
}
|
|
|
|
srvDescriptor.ptr +=
|
|
descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV];
|
|
samplerDescriptor.ptr +=
|
|
descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER];
|
|
|
|
auto& glTFMetallicRoughnessTexture =
|
|
glTFMaterial.pbrMetallicRoughness.metallicRoughnessTexture;
|
|
if (glTFMetallicRoughnessTexture.index >= 0) {
|
|
auto& glTFTexture = pModel_->textures[glTFMetallicRoughnessTexture.index];
|
|
auto pTexture = pTextures_[glTFTexture.source].Get();
|
|
|
|
pDevice_->CreateShaderResourceView(pTexture, nullptr, srvDescriptor);
|
|
|
|
auto& samplerDesc = samplerDescs_[glTFTexture.sampler];
|
|
pDevice_->CreateSampler(&samplerDesc, samplerDescriptor);
|
|
}
|
|
materials_.push_back(material);
|
|
}
|
|
}
|
|
|
|
void Viewer::buildMeshes() {
|
|
HRESULT hr = S_OK;
|
|
|
|
for (auto& glTFMesh : pModel_->meshes) {
|
|
Mesh mesh = {};
|
|
mesh.name = glTFMesh.name;
|
|
|
|
auto& primitives = mesh.primitives;
|
|
for (auto& glTFPrimitive : glTFMesh.primitives) {
|
|
Primitive primitive = {};
|
|
|
|
auto& attributes = primitive.attributes;
|
|
for (auto& [attributeName, accessorIndex] : glTFPrimitive.attributes) {
|
|
const auto& glTFAccessor = pModel_->accessors[accessorIndex];
|
|
const auto& glTFBufferView =
|
|
pModel_->bufferViews[glTFAccessor.bufferView];
|
|
const auto& glTFBuffer = pModel_->buffers[glTFBufferView.buffer];
|
|
|
|
Attribute attribute = {};
|
|
attribute.name = attributeName;
|
|
|
|
switch (glTFAccessor.type) {
|
|
case TINYGLTF_TYPE_VEC2:
|
|
attribute.format = DXGI_FORMAT_R32G32_FLOAT;
|
|
break;
|
|
case TINYGLTF_TYPE_VEC3:
|
|
attribute.format = DXGI_FORMAT_R32G32B32_FLOAT;
|
|
break;
|
|
case TINYGLTF_TYPE_VEC4:
|
|
attribute.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
break;
|
|
}
|
|
|
|
attribute.vertexBufferView.BufferLocation =
|
|
pBuffers_[glTFBufferView.buffer]->GetGPUVirtualAddress() +
|
|
glTFBufferView.byteOffset + glTFAccessor.byteOffset;
|
|
attribute.vertexBufferView.SizeInBytes = static_cast<UINT>(
|
|
glTFBufferView.byteLength - glTFAccessor.byteOffset);
|
|
attribute.vertexBufferView.StrideInBytes =
|
|
glTFAccessor.ByteStride(glTFBufferView);
|
|
|
|
attributes.emplace_back(attribute);
|
|
|
|
if (attributeName == "POSITION") {
|
|
primitive.vertexCount = static_cast<uint32_t>(glTFAccessor.count);
|
|
}
|
|
}
|
|
|
|
auto& primitiveTopology = primitive.primitiveTopology;
|
|
switch (glTFPrimitive.mode) {
|
|
case TINYGLTF_MODE_POINTS:
|
|
primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
|
|
break;
|
|
case TINYGLTF_MODE_LINE:
|
|
primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
|
|
break;
|
|
case TINYGLTF_MODE_LINE_STRIP:
|
|
primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
|
|
break;
|
|
case TINYGLTF_MODE_TRIANGLES:
|
|
primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
|
break;
|
|
case TINYGLTF_MODE_TRIANGLE_STRIP:
|
|
primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
if (glTFPrimitive.indices >= 0) {
|
|
const auto& glTFAccessor = pModel_->accessors[glTFPrimitive.indices];
|
|
const auto& glTFBufferView =
|
|
pModel_->bufferViews[glTFAccessor.bufferView];
|
|
const auto& glTFBuffer = pModel_->buffers[glTFBufferView.buffer];
|
|
|
|
auto& indexBufferView = primitive.indexBufferView;
|
|
indexBufferView.BufferLocation =
|
|
pBuffers_[glTFBufferView.buffer]->GetGPUVirtualAddress() +
|
|
glTFBufferView.byteOffset + glTFAccessor.byteOffset;
|
|
indexBufferView.SizeInBytes = static_cast<UINT>(
|
|
glTFBufferView.byteLength - glTFAccessor.byteOffset);
|
|
|
|
switch (glTFAccessor.componentType) {
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
indexBufferView.Format = DXGI_FORMAT_R8_UINT;
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
indexBufferView.Format = DXGI_FORMAT_R16_UINT;
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
|
indexBufferView.Format = DXGI_FORMAT_R32_UINT;
|
|
break;
|
|
}
|
|
|
|
auto& indexCount = primitive.indexCount;
|
|
indexCount = static_cast<uint32_t>(glTFAccessor.count);
|
|
}
|
|
|
|
auto buildDefines = [](const std::vector<Attribute>& attributes) {
|
|
std::vector<D3D_SHADER_MACRO> defines;
|
|
|
|
for (auto& attribute : attributes) {
|
|
if (attribute.name == "NORMAL")
|
|
defines.push_back({"HAS_NORMAL", "1"});
|
|
else if (attribute.name == "TANGENT")
|
|
defines.push_back({"HAS_TANGENT", "1"});
|
|
else if (attribute.name == "TEXCOORD_0")
|
|
defines.push_back({"HAS_TEXCOORD_0", "1"});
|
|
}
|
|
|
|
defines.push_back({nullptr, nullptr});
|
|
|
|
return defines;
|
|
};
|
|
|
|
auto compileShaderFromFile = [](LPCWSTR pFilePath,
|
|
D3D_SHADER_MACRO* pDefines,
|
|
LPCSTR pTarget, ID3DBlob** ppShader) {
|
|
HRESULT hr = S_OK;
|
|
|
|
{
|
|
UINT flags = 0;
|
|
|
|
#if _DEBUG
|
|
flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
|
#endif
|
|
|
|
ComPtr<ID3DBlob> pError;
|
|
hr = D3DCompileFromFile(pFilePath, pDefines,
|
|
D3D_COMPILE_STANDARD_FILE_INCLUDE, "main",
|
|
pTarget, flags, 0, ppShader, &pError);
|
|
|
|
if (pError) {
|
|
spdlog::error("{}", static_cast<char*>(pError->GetBufferPointer()));
|
|
}
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
return hr;
|
|
};
|
|
|
|
auto createRootSignature =
|
|
[this](D3D12_ROOT_SIGNATURE_DESC* pRootSignatureDesc,
|
|
ID3D12RootSignature** ppRootSignature) {
|
|
HRESULT hr = S_OK;
|
|
|
|
ComPtr<ID3DBlob> pSerializeRootSignature;
|
|
ComPtr<ID3DBlob> pError;
|
|
|
|
{
|
|
hr = D3D12SerializeRootSignature(
|
|
pRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1,
|
|
pSerializeRootSignature.GetAddressOf(),
|
|
pError.GetAddressOf());
|
|
|
|
if (pError) {
|
|
spdlog::error("{}",
|
|
static_cast<char*>(pError->GetBufferPointer()));
|
|
}
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pDevice_->CreateRootSignature(
|
|
0, pSerializeRootSignature->GetBufferPointer(),
|
|
pSerializeRootSignature->GetBufferSize(),
|
|
IID_PPV_ARGS(ppRootSignature));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
return hr;
|
|
};
|
|
|
|
auto buildInputElementDescs =
|
|
[](const std::vector<Attribute>& attributes) {
|
|
std::vector<D3D12_INPUT_ELEMENT_DESC> inputElementDescs;
|
|
|
|
for (auto& attribute : attributes) {
|
|
D3D12_INPUT_ELEMENT_DESC inputElementDesc = {};
|
|
inputElementDesc.SemanticName = &attribute.name[0];
|
|
inputElementDesc.Format = attribute.format;
|
|
|
|
// TODO: Need to parse semantic name and index from attribute name.
|
|
if (attribute.name == "TEXCOORD_0") {
|
|
inputElementDesc.SemanticName = "TEXCOORD_";
|
|
inputElementDesc.SemanticIndex = 0;
|
|
}
|
|
|
|
inputElementDesc.InputSlot =
|
|
static_cast<UINT>(inputElementDescs.size());
|
|
inputElementDesc.AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
|
|
inputElementDesc.InputSlotClass =
|
|
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
|
|
|
|
inputElementDescs.push_back(inputElementDesc);
|
|
}
|
|
|
|
return inputElementDescs;
|
|
};
|
|
|
|
if (glTFPrimitive.material >= 0) {
|
|
primitive.pMaterial = &materials_[glTFPrimitive.material];
|
|
|
|
auto& pRootSignature = primitive.pRootSignature;
|
|
{
|
|
D3D12_DESCRIPTOR_RANGE SRVDescriptorRange = {};
|
|
|
|
SRVDescriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
SRVDescriptorRange.NumDescriptors = 5;
|
|
|
|
D3D12_DESCRIPTOR_RANGE SamplerDescriptorRange = {};
|
|
|
|
SamplerDescriptorRange.RangeType =
|
|
D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
|
SamplerDescriptorRange.NumDescriptors = 5;
|
|
|
|
D3D12_ROOT_PARAMETER rootParams[5] = {
|
|
{D3D12_ROOT_PARAMETER_TYPE_CBV,
|
|
{0, 0},
|
|
D3D12_SHADER_VISIBILITY_VERTEX},
|
|
{D3D12_ROOT_PARAMETER_TYPE_CBV,
|
|
{1, 0},
|
|
D3D12_SHADER_VISIBILITY_VERTEX},
|
|
{D3D12_ROOT_PARAMETER_TYPE_CBV,
|
|
{2, 0},
|
|
D3D12_SHADER_VISIBILITY_PIXEL},
|
|
{D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
|
|
{1, &SRVDescriptorRange},
|
|
D3D12_SHADER_VISIBILITY_PIXEL},
|
|
{D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
|
|
{1, &SamplerDescriptorRange},
|
|
D3D12_SHADER_VISIBILITY_PIXEL}};
|
|
|
|
// TEMP
|
|
rootParams[0].Descriptor.RegisterSpace = 0;
|
|
rootParams[1].Descriptor.RegisterSpace = 0;
|
|
rootParams[2].Descriptor.RegisterSpace = 0;
|
|
|
|
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
|
|
rootSignatureDesc.NumParameters = 5;
|
|
rootSignatureDesc.pParameters = &rootParams[0];
|
|
rootSignatureDesc.Flags =
|
|
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
|
|
|
hr = createRootSignature(&rootSignatureDesc, &pRootSignature);
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
auto defines = buildDefines(attributes);
|
|
auto pDefinces = defines.empty() ? nullptr : &defines[0];
|
|
|
|
ComPtr<ID3DBlob> pVS;
|
|
hr = compileShaderFromFile(DXVIEW_RES_DIR "/primitive.hlsl",
|
|
pDefinces, "vs_5_1", &pVS);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
ComPtr<ID3DBlob> pPS;
|
|
hr = compileShaderFromFile(DXVIEW_RES_DIR "/lighting.hlsl", pDefinces,
|
|
"ps_5_1", &pPS);
|
|
|
|
auto inputElementDescs = buildInputElementDescs(attributes);
|
|
|
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc = {};
|
|
pipelineStateDesc.pRootSignature = pRootSignature.Get();
|
|
pipelineStateDesc.VS = {pVS->GetBufferPointer(),
|
|
pVS->GetBufferSize()};
|
|
pipelineStateDesc.PS = {pPS->GetBufferPointer(),
|
|
pPS->GetBufferSize()};
|
|
pipelineStateDesc.BlendState = primitive.pMaterial->blendDesc;
|
|
pipelineStateDesc.SampleMask = UINT_MAX;
|
|
pipelineStateDesc.RasterizerState =
|
|
primitive.pMaterial->rasterizerDesc;
|
|
pipelineStateDesc.InputLayout = {
|
|
inputElementDescs.data(),
|
|
static_cast<UINT>(inputElementDescs.size())};
|
|
|
|
switch (primitive.primitiveTopology) {
|
|
case D3D_PRIMITIVE_TOPOLOGY_POINTLIST:
|
|
pipelineStateDesc.PrimitiveTopologyType =
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
|
|
break;
|
|
case D3D_PRIMITIVE_TOPOLOGY_LINELIST:
|
|
case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP:
|
|
pipelineStateDesc.PrimitiveTopologyType =
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
|
|
break;
|
|
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST:
|
|
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP:
|
|
pipelineStateDesc.PrimitiveTopologyType =
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
pipelineStateDesc.NumRenderTargets = 1;
|
|
pipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
pipelineStateDesc.SampleDesc = {1, 0};
|
|
|
|
auto& pPipelineState = primitive.pPipelineState;
|
|
hr = pDevice_->CreateGraphicsPipelineState(
|
|
&pipelineStateDesc, IID_PPV_ARGS(&pPipelineState));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
} else {
|
|
auto& pRootSignature = primitive.pRootSignature;
|
|
{
|
|
D3D12_ROOT_PARAMETER rootParams[2] = {
|
|
{D3D12_ROOT_PARAMETER_TYPE_CBV,
|
|
{0, 0},
|
|
D3D12_SHADER_VISIBILITY_VERTEX},
|
|
{D3D12_ROOT_PARAMETER_TYPE_CBV,
|
|
{1, 0},
|
|
D3D12_SHADER_VISIBILITY_VERTEX}};
|
|
|
|
// TEMP
|
|
rootParams[0].Descriptor.RegisterSpace = 0;
|
|
rootParams[1].Descriptor.RegisterSpace = 0;
|
|
|
|
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
|
|
rootSignatureDesc.NumParameters = _countof(rootParams);
|
|
rootSignatureDesc.pParameters = &rootParams[0];
|
|
rootSignatureDesc.Flags =
|
|
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
|
|
|
hr = createRootSignature(&rootSignatureDesc, &pRootSignature);
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
{
|
|
auto defines = buildDefines(attributes);
|
|
auto pDefinces = defines.empty() ? nullptr : &defines[0];
|
|
|
|
ComPtr<ID3DBlob> pVS;
|
|
hr = compileShaderFromFile(DXVIEW_RES_DIR "/primitive.hlsl",
|
|
pDefinces, "vs_5_1", &pVS);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
ComPtr<ID3DBlob> pPS;
|
|
hr = compileShaderFromFile(DXVIEW_RES_DIR "/gray.hlsl", pDefinces,
|
|
"ps_5_1", &pPS);
|
|
|
|
auto inputElementDescs = buildInputElementDescs(attributes);
|
|
|
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc = {};
|
|
pipelineStateDesc.pRootSignature = pRootSignature.Get();
|
|
pipelineStateDesc.VS = {pVS->GetBufferPointer(),
|
|
pVS->GetBufferSize()};
|
|
pipelineStateDesc.PS = {pPS->GetBufferPointer(),
|
|
pPS->GetBufferSize()};
|
|
pipelineStateDesc.BlendState.RenderTarget[0].RenderTargetWriteMask =
|
|
D3D12_COLOR_WRITE_ENABLE_ALL;
|
|
pipelineStateDesc.SampleMask = UINT_MAX;
|
|
pipelineStateDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
|
|
pipelineStateDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
|
|
pipelineStateDesc.RasterizerState.FrontCounterClockwise = true;
|
|
pipelineStateDesc.InputLayout = {
|
|
inputElementDescs.data(),
|
|
static_cast<UINT>(inputElementDescs.size())};
|
|
|
|
switch (primitive.primitiveTopology) {
|
|
case D3D_PRIMITIVE_TOPOLOGY_POINTLIST:
|
|
pipelineStateDesc.PrimitiveTopologyType =
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
|
|
break;
|
|
case D3D_PRIMITIVE_TOPOLOGY_LINELIST:
|
|
case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP:
|
|
pipelineStateDesc.PrimitiveTopologyType =
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
|
|
break;
|
|
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST:
|
|
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP:
|
|
pipelineStateDesc.PrimitiveTopologyType =
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
pipelineStateDesc.NumRenderTargets = 1;
|
|
pipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
pipelineStateDesc.SampleDesc = {1, 0};
|
|
|
|
auto& pPipelineState = primitive.pPipelineState;
|
|
hr = pDevice_->CreateGraphicsPipelineState(
|
|
&pipelineStateDesc, IID_PPV_ARGS(&pPipelineState));
|
|
assert(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
primitives.push_back(primitive);
|
|
}
|
|
meshes_.push_back(mesh);
|
|
}
|
|
}
|
|
|
|
void Viewer::buildNodes() {
|
|
HRESULT hr = S_OK;
|
|
|
|
for (auto& glTFNode : pModel_->nodes) {
|
|
D3D12_HEAP_PROPERTIES heapProperties = {};
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
|
|
D3D12_RESOURCE_DESC resourceDesc = {};
|
|
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
resourceDesc.Width =
|
|
alignPow2(sizeof(PBRMetallicRoughness),
|
|
D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
|
resourceDesc.Height = 1;
|
|
resourceDesc.DepthOrArraySize = 1;
|
|
resourceDesc.MipLevels = 1;
|
|
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
|
resourceDesc.SampleDesc = {1, 0};
|
|
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
|
|
ComPtr<ID3D12Resource> pBuffer;
|
|
hr = pDevice_->CreateCommittedResource(
|
|
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pBuffer));
|
|
assert(SUCCEEDED(hr));
|
|
|
|
void* pData;
|
|
hr = pBuffer->Map(0, nullptr, &pData);
|
|
assert(SUCCEEDED(hr));
|
|
|
|
if (glTFNode.matrix.empty()) {
|
|
XMStoreFloat4x4(static_cast<DirectX::XMFLOAT4X4*>(pData),
|
|
XMMatrixIdentity());
|
|
} else {
|
|
float* pElement = static_cast<float*>(pData);
|
|
for (auto value : glTFNode.matrix) {
|
|
*pElement = static_cast<float>(value);
|
|
++pElement;
|
|
}
|
|
}
|
|
|
|
pNodeBuffers_.push_back(pBuffer);
|
|
}
|
|
}
|
|
|
|
void Viewer::drawNode(uint64_t nodeIndex) {
|
|
const auto& glTFNode = pModel_->nodes[nodeIndex];
|
|
|
|
if (glTFNode.mesh >= 0) {
|
|
const auto& mesh = meshes_[glTFNode.mesh];
|
|
|
|
for (auto& primitive : mesh.primitives) {
|
|
pDirectCommandList_->SetGraphicsRootSignature(
|
|
primitive.pRootSignature.Get());
|
|
pDirectCommandList_->SetPipelineState(primitive.pPipelineState.Get());
|
|
pDirectCommandList_->IASetPrimitiveTopology(primitive.primitiveTopology);
|
|
|
|
for (auto i = 0; i != primitive.attributes.size(); ++i) {
|
|
pDirectCommandList_->IASetVertexBuffers(
|
|
i, 1, &primitive.attributes[i].vertexBufferView);
|
|
}
|
|
|
|
ID3D12DescriptorHeap* pDescriptorHeaps[] = {
|
|
primitive.pMaterial->pSRVDescriptorHeap.Get(),
|
|
primitive.pMaterial->pSamplerDescriptorHeap.Get()};
|
|
pDirectCommandList_->SetDescriptorHeaps(_countof(pDescriptorHeaps),
|
|
pDescriptorHeaps);
|
|
|
|
pDirectCommandList_->SetGraphicsRootConstantBufferView(
|
|
0, pCameraBuffer_->GetGPUVirtualAddress());
|
|
pDirectCommandList_->SetGraphicsRootConstantBufferView(
|
|
1, pNodeBuffers_[nodeIndex]->GetGPUVirtualAddress());
|
|
pDirectCommandList_->SetGraphicsRootConstantBufferView(
|
|
2, primitive.pMaterial->pBuffer->GetGPUVirtualAddress());
|
|
pDirectCommandList_->SetGraphicsRootDescriptorTable(
|
|
3, primitive.pMaterial->pSRVDescriptorHeap
|
|
->GetGPUDescriptorHandleForHeapStart());
|
|
pDirectCommandList_->SetGraphicsRootDescriptorTable(
|
|
4, primitive.pMaterial->pSamplerDescriptorHeap
|
|
->GetGPUDescriptorHandleForHeapStart());
|
|
|
|
if (primitive.indexCount) {
|
|
pDirectCommandList_->IASetIndexBuffer(&primitive.indexBufferView);
|
|
pDirectCommandList_->DrawIndexedInstanced(primitive.indexCount, 1, 0, 0,
|
|
0);
|
|
} else {
|
|
pDirectCommandList_->DrawInstanced(primitive.vertexCount, 1, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto childNodeIndex : glTFNode.children) {
|
|
drawNode(childNodeIndex);
|
|
}
|
|
}
|