| #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); |
| } |
| } |