#include "Viewer.h" #include #include #include using namespace DirectX; namespace { constexpr bool isPow2(uint64_t value) { return (value == 0) ? false : ((value & (value - 1)) == 0); } template constexpr T alignPow2(T value, uint64_t alignment) { assert(isPow2(alignment)); return ((value + static_cast(alignment) - 1) & ~(static_cast(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(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(kRadius * cos(radian)), 0.0f, static_cast(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 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 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 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(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 > 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 >* pStagingResources) { HRESULT hr = S_OK; for (auto& gltfBuffer : pModel_->buffers) { ComPtr 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 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 >* pStagingResources) { HRESULT hr = S_OK; for (auto& gltfImage : pModel_->images) { ComPtr 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 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(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(pBufferData); auto& baseColorFactor = pPBRMetallicRoughness->baseColorFactor; baseColorFactor.x = static_cast(glTFPBRMetallicRoughness.baseColorFactor[0]); baseColorFactor.y = static_cast(glTFPBRMetallicRoughness.baseColorFactor[1]); baseColorFactor.z = static_cast(glTFPBRMetallicRoughness.baseColorFactor[2]); baseColorFactor.w = static_cast(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(glTFPBRMetallicRoughness.metallicFactor); pPBRMetallicRoughness->roughnessFactor = static_cast(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( glTFBufferView.byteLength - glTFAccessor.byteOffset); attribute.vertexBufferView.StrideInBytes = glTFAccessor.ByteStride(glTFBufferView); attributes.emplace_back(attribute); if (attributeName == "POSITION") { primitive.vertexCount = static_cast(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( 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(glTFAccessor.count); } auto buildDefines = [](const std::vector& attributes) { std::vector 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 pError; hr = D3DCompileFromFile(pFilePath, pDefines, D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", pTarget, flags, 0, ppShader, &pError); if (pError) { spdlog::error("{}", static_cast(pError->GetBufferPointer())); } assert(SUCCEEDED(hr)); } return hr; }; auto createRootSignature = [this](D3D12_ROOT_SIGNATURE_DESC* pRootSignatureDesc, ID3D12RootSignature** ppRootSignature) { HRESULT hr = S_OK; ComPtr pSerializeRootSignature; ComPtr pError; { hr = D3D12SerializeRootSignature( pRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, pSerializeRootSignature.GetAddressOf(), pError.GetAddressOf()); if (pError) { spdlog::error("{}", static_cast(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& attributes) { std::vector 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(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 pVS; hr = compileShaderFromFile(DXVIEW_RES_DIR "/primitive.hlsl", pDefinces, "vs_5_1", &pVS); assert(SUCCEEDED(hr)); ComPtr 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(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 pVS; hr = compileShaderFromFile(DXVIEW_RES_DIR "/primitive.hlsl", pDefinces, "vs_5_1", &pVS); assert(SUCCEEDED(hr)); ComPtr 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(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 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(pData), XMMatrixIdentity()); } else { float* pElement = static_cast(pData); for (auto value : glTFNode.matrix) { *pElement = static_cast(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); } }