/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include "RayTracingVertexFormatExampleComponent.h" #include #include #include #include #include #include #include namespace AtomSampleViewer::ImGuiHelper { template && sizeof(T) == sizeof(int), bool> = true> bool RadioButton(const char* label, T* value, T buttonValue) { return ScriptableImGui::RadioButton(label, reinterpret_cast(value), AZStd::to_underlying(buttonValue)); } } // namespace AtomSampleViewer::ImGuiHelper namespace { enum class FloatType { Float32, Float16, Float8, Unorm8, }; enum class OctahedronType { Oct32, Oct16, Oct8, }; constexpr int GetFloatTypeSize(FloatType floatType) { switch (floatType) { case FloatType::Float32: return 4; case FloatType::Float16: return 2; case FloatType::Float8: case FloatType::Unorm8: return 1; default: AZ_Assert(false, "Failed to get size of FloatType %d", floatType); return 1; } } constexpr int GetOctahedronTypeSize(OctahedronType octahedronType) { switch (octahedronType) { case OctahedronType::Oct32: return 4; case OctahedronType::Oct16: return 2; case OctahedronType::Oct8: return 1; default: AZ_Assert(false, "Failed to get size of OctahedronType %d", octahedronType); return 0; } } constexpr int GetOctahedronComponentBitCount(OctahedronType octahedronType) { switch (octahedronType) { case OctahedronType::Oct32: return 15; case OctahedronType::Oct16: return 7; case OctahedronType::Oct8: return 3; default: AZ_Assert(false, "Failed to get bit count of OctahedronType %d", octahedronType); return 0; } } template void PackFloat(float sourceValue, void* targetAddress); template<> void PackFloat(float sourceValue, void* targetAddress) { std::memcpy(targetAddress, &sourceValue, 4); } template<> void PackFloat(float sourceValue, void* targetAddress) { uint32_t sourceValueInt{ *reinterpret_cast(&sourceValue) }; // https://stackoverflow.com/a/5587983 uint16_t targetValue = (sourceValueInt >> 31) << 5; uint16_t tmp = (sourceValueInt >> 23) & 0xff; tmp = (tmp - 0x70) & ((unsigned int)((int)(0x70 - tmp) >> 4) >> 27); targetValue = (targetValue | tmp) << 10; targetValue |= (sourceValueInt >> 13) & 0x3ff; std::memcpy(targetAddress, &targetValue, 2); } template<> void PackFloat(float sourceValue, void* targetAddress) { uint8_t targetValue{ aznumeric_cast(AZStd::clamp(sourceValue, 0.f, 1.f) * 255.f) }; std::memcpy(targetAddress, &targetValue, 1); } template void ConvertFloatBuffer(AZStd::span sourceBuffer, AZStd::span targetBuffer, int sourceComponentCountOverride) { AZ_Assert( sourceComponentCountOverride == 0 || sourceComponentCountOverride < ComponentCount, "Component count override must be smaller than the format component count"); size_t elementCount{ sourceBuffer.size() / (ComponentCount * sizeof(float)) }; auto sourceIteratorBegin{ reinterpret_cast(sourceBuffer.data()) }; auto sourceIteratorEnd{ sourceIteratorBegin + elementCount * ComponentCount }; uint8_t* targetBufferAddress{ targetBuffer.data() }; int currentComponentIndex{ 0 }; for (auto sourceIterator{ sourceIteratorBegin }; sourceIterator != sourceIteratorEnd; ++sourceIterator) { PackFloat(*sourceIterator, targetBufferAddress); targetBufferAddress += GetFloatTypeSize(ComponentOutputFormat); if (sourceComponentCountOverride != 0 && ++currentComponentIndex == sourceComponentCountOverride) { targetBufferAddress += GetFloatTypeSize(ComponentOutputFormat) * (ComponentCount - sourceComponentCountOverride); currentComponentIndex = 0; } } } AZ::Vector3 EncodeNormalSignedOctahedron(AZ::Vector3 normal) { // http://johnwhite3d.blogspot.com/2017/10/signed-octahedron-normal-encoding.html normal /= AZStd::abs(normal.GetX()) + AZStd::abs(normal.GetY()) + AZStd::abs(normal.GetZ()); AZ::Vector3 encodedNormal; encodedNormal.SetY(normal.GetY() * 0.5f + 0.5f); encodedNormal.SetX(normal.GetX() * 0.5f + encodedNormal.GetY()); encodedNormal.SetY(normal.GetX() * -0.5f + encodedNormal.GetY()); encodedNormal.SetZ(AZStd::clamp(normal.GetZ() * AZStd::numeric_limits::max(), 0.f, 1.f)); return encodedNormal; } void EncodeTangentPackOctahedron(const AZ::PackedVector4f& sourceValue, OctahedronType octahedronType, void* targetAddress) { int xyBits{ GetOctahedronComponentBitCount(octahedronType) }; AZ_Assert(xyBits * 2 + 2 <= 32, "Too many bits for octahedron encoding"); AZ::Vector3 encodedNormal{ EncodeNormalSignedOctahedron( AZ::Vector3{ sourceValue.GetX(), sourceValue.GetY(), sourceValue.GetZ() }) }; uint32_t mask{ (1u << xyBits) - 1 }; uint32_t targetValue{ aznumeric_cast(encodedNormal.GetX() * mask) | aznumeric_cast(encodedNormal.GetY() * mask) << xyBits | aznumeric_cast(encodedNormal.GetZ() > 0) << (xyBits * 2) | aznumeric_cast(sourceValue.GetW() > 0) << (xyBits * 2 + 1) }; std::memcpy(targetAddress, &targetValue, GetOctahedronTypeSize(octahedronType)); } void EncodeNormalPackOctahedron(const AZ::PackedVector3f& sourceValue, OctahedronType octahedronType, void* targetAddress) { EncodeTangentPackOctahedron( AZ::PackedVector4f{ sourceValue.GetX(), sourceValue.GetY(), sourceValue.GetZ(), 0.f }, octahedronType, targetAddress); } template void ConvertNormalBuffer(AZStd::span sourceBuffer, AZStd::span targetBuffer) { size_t elementCount{ sourceBuffer.size() / sizeof(AZ::PackedVector3f) }; auto sourceIteratorBegin{ reinterpret_cast(sourceBuffer.data()) }; auto sourceIteratorEnd{ sourceIteratorBegin + elementCount }; uint8_t* targetBufferAddress{ targetBuffer.data() }; for (auto sourceIterator{ sourceIteratorBegin }; sourceIterator != sourceIteratorEnd; ++sourceIterator) { EncodeNormalPackOctahedron(*sourceIterator, ComponentOutputFormat, targetBufferAddress); targetBufferAddress += GetOctahedronTypeSize(ComponentOutputFormat); } } template void ConvertTangentBuffer(AZStd::span sourceBuffer, AZStd::span targetBuffer) { size_t elementCount{ sourceBuffer.size() / sizeof(AZ::PackedVector4f) }; auto sourceIteratorBegin{ reinterpret_cast(sourceBuffer.data()) }; auto sourceIteratorEnd{ sourceIteratorBegin + elementCount }; uint8_t* targetBufferAddress{ targetBuffer.data() }; for (auto sourceIterator{ sourceIteratorBegin }; sourceIterator != sourceIteratorEnd; ++sourceIterator) { EncodeTangentPackOctahedron(*sourceIterator, ComponentOutputFormat, targetBufferAddress); targetBufferAddress += GetOctahedronTypeSize(ComponentOutputFormat); } } void ConvertIndexBufferData( AZStd::span sourceBuffer, AZStd::span targetBuffer, AZ::RHI::IndexFormat indexFormat) { size_t elementCount{ sourceBuffer.size() / sizeof(uint32_t) }; auto sourceIteratorBegin{ reinterpret_cast(sourceBuffer.data()) }; auto sourceIteratorEnd{ sourceIteratorBegin + elementCount }; uint8_t* targetBufferAddress{ targetBuffer.data() }; for (auto sourceIterator{ sourceIteratorBegin }; sourceIterator != sourceIteratorEnd; ++sourceIterator) { uint32_t sourceValue{ *sourceIterator }; std::memcpy(targetBufferAddress, &sourceValue, AZ::RHI::GetIndexFormatSize(indexFormat)); targetBufferAddress += AZ::RHI::GetIndexFormatSize(indexFormat); } } } // namespace namespace AtomSampleViewer { using namespace AZ; void RayTracingVertexFormatExampleComponent::Reflect(ReflectContext* context) { if (auto* serializeContext{ azrtti_cast(context) }) { serializeContext->Class()->Version(0); } } void RayTracingVertexFormatExampleComponent::Activate() { m_modelBrowser.SetFilter( [](const Data::AssetInfo& assetInfo) { return assetInfo.m_assetType == azrtti_typeid(); }); m_modelBrowser.SetDefaultPinnedAssets( { "Objects/Shaderball_simple.fbx.azmodel", "Objects/bunny.fbx.azmodel", "Objects/suzanne.fbx.azmodel", "Objects/sponza.fbx.azmodel", }, true); m_imguiSidebar.Activate(); m_modelBrowser.Activate(); auto& rayTracingDebugFeatureProcessor{ GetRayTracingDebugFeatureProcessor() }; rayTracingDebugFeatureProcessor.OnRayTracingDebugComponentAdded(); rayTracingDebugFeatureProcessor.GetSettingsInterface()->SetDebugViewMode(Render::RayTracingDebugViewMode::PrimitiveIndex); TickBus::Handler::BusConnect(); EBUS_EVENT_ID(GetCameraEntityId(), Debug::CameraControllerRequestBus, Enable, azrtti_typeid()); EBUS_EVENT(Render::Bootstrap::NotificationBus, OnBootstrapSceneReady, m_scene); } void RayTracingVertexFormatExampleComponent::Deactivate() { m_modelBrowser.Deactivate(); m_imguiSidebar.Deactivate(); GetRayTracingDebugFeatureProcessor().OnRayTracingDebugComponentRemoved(); // GetRayTracingFeatureProcessor().RemoveMesh(m_rayTracingUuid); EBUS_EVENT_ID(GetCameraEntityId(), Debug::CameraControllerRequestBus, Disable); TickBus::Handler::BusDisconnect(); } void RayTracingVertexFormatExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) { DrawSidebar(); } void RayTracingVertexFormatExampleComponent::DrawSidebar() { if (m_imguiSidebar.Begin()) { ImGuiAssetBrowser::WidgetSettings modelBrowserSettings; modelBrowserSettings.m_labels.m_root = "Models"; bool modelChanged{ m_modelBrowser.Tick(modelBrowserSettings) }; ImGui::Separator(); ImGui::Spacing(); ImGui::Text("RT Debug Type:"); using DebugViewMode = Render::RayTracingDebugViewMode; bool debugViewModeUpdated{ false }; debugViewModeUpdated |= ImGuiHelper::RadioButton("Instance Index", &m_debugViewMode, DebugViewMode::InstanceIndex); debugViewModeUpdated |= ImGuiHelper::RadioButton("Instance ID", &m_debugViewMode, DebugViewMode::InstanceID); debugViewModeUpdated |= ImGuiHelper::RadioButton("Primitive Index", &m_debugViewMode, DebugViewMode::PrimitiveIndex); debugViewModeUpdated |= ImGuiHelper::RadioButton("Barycentric Coordinates", &m_debugViewMode, DebugViewMode::Barycentrics); debugViewModeUpdated |= ImGuiHelper::RadioButton("Normals", &m_debugViewMode, DebugViewMode::Normals); debugViewModeUpdated |= ImGuiHelper::RadioButton("UV Coordinates", &m_debugViewMode, DebugViewMode::UVs); if (debugViewModeUpdated) { GetRayTracingDebugFeatureProcessor().GetSettingsInterface()->SetDebugViewMode(m_debugViewMode); } ImGui::Separator(); ImGui::Spacing(); ImGui::Text("Index Format:"); bool indexFormatUpdated{ false }; indexFormatUpdated |= ImGuiHelper::RadioButton("Uint32", &m_indexFormat, RHI::IndexFormat::Uint32); indexFormatUpdated |= ImGuiHelper::RadioButton("Uint16", &m_indexFormat, RHI::IndexFormat::Uint16); ImGui::Spacing(); ImGui::PushID("Position"); ImGui::Text("Position Format:"); bool positionFormatUpdated{ false }; positionFormatUpdated |= ImGuiHelper::RadioButton("R32G32B32_FLOAT", &m_positionFormat, RHI::VertexFormat::R32G32B32_FLOAT); positionFormatUpdated |= ImGuiHelper::RadioButton("R16G16B16A16_FLOAT", &m_positionFormat, RHI::VertexFormat::R16G16B16A16_FLOAT); ImGui::PopID(); ImGui::Spacing(); ImGui::PushID("Normal"); ImGui::Text("Normal Format:"); bool normalFormatUpdated{ false }; normalFormatUpdated |= ImGuiHelper::RadioButton("R32G32B32_FLOAT", &m_normalFormat, RHI::VertexFormat::R32G32B32_FLOAT); normalFormatUpdated |= ImGuiHelper::RadioButton("R16G16B16_FLOAT", &m_normalFormat, RHI::VertexFormat::R16G16B16_FLOAT); normalFormatUpdated |= ImGuiHelper::RadioButton("N32_OCT", &m_normalFormat, RHI::VertexFormat::N32_OCT); normalFormatUpdated |= ImGuiHelper::RadioButton("N16_OCT", &m_normalFormat, RHI::VertexFormat::N16_OCT); normalFormatUpdated |= ImGuiHelper::RadioButton("N8_OCT", &m_normalFormat, RHI::VertexFormat::N8_OCT); ImGui::PopID(); ImGui::Spacing(); ImGui::PushID("UV"); ImGui::Text("UV Format:"); bool uvFormatUpdated{ false }; uvFormatUpdated |= ImGuiHelper::RadioButton("R32G32_FLOAT", &m_uvFormat, RHI::VertexFormat::R32G32_FLOAT); uvFormatUpdated |= ImGuiHelper::RadioButton("R16G16_FLOAT", &m_uvFormat, RHI::VertexFormat::R16G16_FLOAT); uvFormatUpdated |= ImGuiHelper::RadioButton("R8G8_UNORM", &m_uvFormat, RHI::VertexFormat::R8G8_UNORM); ImGui::PopID(); ImGui::Spacing(); ImGui::PushID("Tangent"); ImGui::Text("Tangent Format:"); bool tangentFormatUpdated{ false }; tangentFormatUpdated |= ImGuiHelper::RadioButton("R32G32B32A32_FLOAT", &m_tangentFormat, RHI::VertexFormat::R32G32B32A32_FLOAT); tangentFormatUpdated |= ImGuiHelper::RadioButton("R16G16B16A16_FLOAT", &m_tangentFormat, RHI::VertexFormat::R16G16B16A16_FLOAT); tangentFormatUpdated |= ImGuiHelper::RadioButton("T32_OCT", &m_tangentFormat, RHI::VertexFormat::T32_OCT); tangentFormatUpdated |= ImGuiHelper::RadioButton("T16_OCT", &m_tangentFormat, RHI::VertexFormat::T16_OCT); tangentFormatUpdated |= ImGuiHelper::RadioButton("T8_OCT", &m_tangentFormat, RHI::VertexFormat::T8_OCT); ImGui::PopID(); ImGui::Spacing(); ImGui::PushID("Bitangent"); ImGui::Text("Bitangent Format:"); bool bitangentFormatUpdated{ false }; bitangentFormatUpdated |= ImGuiHelper::RadioButton("R32G32B32_FLOAT", &m_bitangentFormat, RHI::VertexFormat::R32G32B32_FLOAT); bitangentFormatUpdated |= ImGuiHelper::RadioButton("R16G16B16_FLOAT", &m_bitangentFormat, RHI::VertexFormat::R16G16B16_FLOAT); bitangentFormatUpdated |= ImGuiHelper::RadioButton("N32_OCT", &m_bitangentFormat, RHI::VertexFormat::N32_OCT); bitangentFormatUpdated |= ImGuiHelper::RadioButton("N16_OCT", &m_bitangentFormat, RHI::VertexFormat::N16_OCT); bitangentFormatUpdated |= ImGuiHelper::RadioButton("N8_OCT", &m_bitangentFormat, RHI::VertexFormat::N8_OCT); ImGui::PopID(); if (modelChanged || indexFormatUpdated || positionFormatUpdated || uvFormatUpdated || normalFormatUpdated || tangentFormatUpdated || bitangentFormatUpdated) { ModelChanged(); } m_imguiSidebar.End(); } } void RayTracingVertexFormatExampleComponent::ModelChanged() { if (!m_modelBrowser.GetSelectedAssetId().IsValid()) { return; } m_currentModel.Create(m_modelBrowser.GetSelectedAssetId()); m_currentModel.QueueLoad(); m_currentModel.BlockUntilLoadComplete(); Render::RayTracingFeatureProcessorInterface::Mesh rtMesh; rtMesh.m_assetId.m_guid = m_rayTracingUuid; rtMesh.m_instanceMask |= static_cast(RHI::RayTracingAccelerationStructureInstanceInclusionMask::STATIC_MESH); AZStd::vector rtSubMeshes; const auto& meshes{ m_currentModel->GetLodAssets()[0]->GetMeshes() }; for (auto& mesh : meshes) { auto& rtSubMesh{ rtSubMeshes.emplace_back() }; { const auto& indexView{ mesh.GetIndexBufferAssetView() }; auto indexBuffer{ ConvertIndexBuffer( indexView.GetBufferAsset()->GetBuffer(), indexView.GetBufferViewDescriptor(), m_indexFormat) }; uint32_t indexElementSize{ RHI::GetIndexFormatSize(m_indexFormat) }; rtSubMesh.m_indexBufferView = RHI::IndexBufferView{ *indexBuffer->GetRHIBuffer(), 0, mesh.GetIndexCount() * indexElementSize, m_indexFormat }; rtSubMesh.m_indexShaderBufferView = indexBuffer->GetRHIBuffer()->GetBufferView(indexBuffer->GetBufferViewDescriptor()); } { const auto* positionView{ mesh.GetSemanticBufferAssetView(AZ_NAME_LITERAL("POSITION")) }; uint32_t positionElementSize{ RHI::GetVertexFormatSize(m_positionFormat) }; int sourceComponentOverride{ m_positionFormat == RHI::VertexFormat::R16G16B16A16_FLOAT ? 3 : 0 }; auto positionBuffer{ ConvertVertexBuffer( positionView->GetBufferAsset()->GetBuffer(), positionView->GetBufferViewDescriptor(), m_positionFormat, sourceComponentOverride) }; rtSubMesh.m_positionVertexBufferView = RHI::StreamBufferView{ *positionBuffer->GetRHIBuffer(), 0, mesh.GetVertexCount() * positionElementSize, positionElementSize }; rtSubMesh.m_positionShaderBufferView = positionBuffer->GetRHIBuffer()->GetBufferView(positionBuffer->GetBufferViewDescriptor()); rtSubMesh.m_positionFormat = m_positionFormat; } { const auto* normalView{ mesh.GetSemanticBufferAssetView(AZ_NAME_LITERAL("NORMAL")) }; uint32_t normalElementSize{ RHI::GetVertexFormatSize(m_normalFormat) }; auto normalBuffer{ ConvertVertexBuffer( normalView->GetBufferAsset()->GetBuffer(), normalView->GetBufferViewDescriptor(), m_normalFormat) }; rtSubMesh.m_normalVertexBufferView = RHI::StreamBufferView{ *normalBuffer->GetRHIBuffer(), 0, mesh.GetVertexCount() * normalElementSize, normalElementSize }; rtSubMesh.m_normalShaderBufferView = normalBuffer->GetRHIBuffer()->GetBufferView(normalBuffer->GetBufferViewDescriptor()); rtSubMesh.m_normalFormat = m_normalFormat; } if (auto* uvView{ mesh.GetSemanticBufferAssetView(AZ_NAME_LITERAL("UV")) }) { uint32_t uvElementSize{ RHI::GetVertexFormatSize(m_uvFormat) }; auto uvBuffer{ ConvertVertexBuffer(uvView->GetBufferAsset()->GetBuffer(), uvView->GetBufferViewDescriptor(), m_uvFormat) }; rtSubMesh.m_uvVertexBufferView = RHI::StreamBufferView{ *uvBuffer->GetRHIBuffer(), 0, mesh.GetVertexCount() * uvElementSize, uvElementSize }; rtSubMesh.m_uvShaderBufferView = uvBuffer->GetRHIBuffer()->GetBufferView(uvBuffer->GetBufferViewDescriptor()); rtSubMesh.m_uvFormat = m_uvFormat; rtSubMesh.m_bufferFlags |= Render::RayTracingSubMeshBufferFlags::UV; } if (auto* tangentView{ mesh.GetSemanticBufferAssetView(AZ_NAME_LITERAL("TANGENT")) }) { uint32_t tangentElementSize{ RHI::GetVertexFormatSize(m_tangentFormat) }; auto tangentBuffer{ ConvertVertexBuffer( tangentView->GetBufferAsset()->GetBuffer(), tangentView->GetBufferViewDescriptor(), m_tangentFormat) }; rtSubMesh.m_tangentVertexBufferView = RHI::StreamBufferView{ *tangentBuffer->GetRHIBuffer(), 0, mesh.GetVertexCount() * tangentElementSize, tangentElementSize }; rtSubMesh.m_tangentShaderBufferView = tangentBuffer->GetRHIBuffer()->GetBufferView(tangentBuffer->GetBufferViewDescriptor()); rtSubMesh.m_tangentFormat = m_tangentFormat; rtSubMesh.m_bufferFlags |= Render::RayTracingSubMeshBufferFlags::Tangent; } if (auto* bitangentView{ mesh.GetSemanticBufferAssetView(AZ_NAME_LITERAL("BITANGENT")) }) { uint32_t bitangentElementSize{ RHI::GetVertexFormatSize(m_bitangentFormat) }; auto bitangentBuffer{ ConvertVertexBuffer( bitangentView->GetBufferAsset()->GetBuffer(), bitangentView->GetBufferViewDescriptor(), m_bitangentFormat) }; rtSubMesh.m_bitangentVertexBufferView = RHI::StreamBufferView{ *bitangentBuffer->GetRHIBuffer(), 0, mesh.GetVertexCount() * bitangentElementSize, bitangentElementSize }; rtSubMesh.m_bitangentShaderBufferView = bitangentBuffer->GetRHIBuffer()->GetBufferView(bitangentBuffer->GetBufferViewDescriptor()); rtSubMesh.m_bitangentFormat = m_bitangentFormat; rtSubMesh.m_bufferFlags |= Render::RayTracingSubMeshBufferFlags::Bitangent; } } GetRayTracingFeatureProcessor().RemoveMesh(m_rayTracingUuid); GetRayTracingFeatureProcessor().AddMesh(m_rayTracingUuid, rtMesh, rtSubMeshes); } RPI::Ptr RayTracingVertexFormatExampleComponent::ConvertVertexBuffer( AZStd::span sourceBufferData, const RHI::BufferViewDescriptor& sourceBufferDescriptor, RHI::VertexFormat targetFormat, int sourceComponentCountOverride) { auto sourceBufferView{ sourceBufferData.subspan( sourceBufferDescriptor.m_elementOffset * sourceBufferDescriptor.m_elementSize, sourceBufferDescriptor.m_elementCount * sourceBufferDescriptor.m_elementSize) }; uint32_t targetFormatSize{ RHI::GetVertexFormatSize(targetFormat) }; // Use SizeAlignUp to ensure 4-byte-aligned reads in the shader are always valid when the buffer size is not a multiple of 4 uint32_t targetBufferSize{ SizeAlignUp(sourceBufferDescriptor.m_elementCount * targetFormatSize, 4) }; AZStd::vector targetBufferData(targetBufferSize); switch (targetFormat) { case RHI::VertexFormat::R32G32B32A32_FLOAT: ConvertFloatBuffer<4, FloatType::Float32>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::R16G16B16A16_FLOAT: ConvertFloatBuffer<4, FloatType::Float16>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::R32G32B32_FLOAT: ConvertFloatBuffer<3, FloatType::Float32>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::R16G16B16_FLOAT: ConvertFloatBuffer<3, FloatType::Float16>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::R32G32_FLOAT: ConvertFloatBuffer<2, FloatType::Float32>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::R16G16_FLOAT: ConvertFloatBuffer<2, FloatType::Float16>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::R8G8_UNORM: ConvertFloatBuffer<2, FloatType::Unorm8>(sourceBufferView, targetBufferData, sourceComponentCountOverride); break; case RHI::VertexFormat::N32_OCT: ConvertNormalBuffer(sourceBufferView, targetBufferData); break; case RHI::VertexFormat::N16_OCT: ConvertNormalBuffer(sourceBufferView, targetBufferData); break; case RHI::VertexFormat::N8_OCT: ConvertNormalBuffer(sourceBufferView, targetBufferData); break; case RHI::VertexFormat::T32_OCT: ConvertTangentBuffer(sourceBufferView, targetBufferData); break; case RHI::VertexFormat::T16_OCT: ConvertTangentBuffer(sourceBufferView, targetBufferData); break; case RHI::VertexFormat::T8_OCT: ConvertTangentBuffer(sourceBufferView, targetBufferData); break; default: AZ_Assert(false, "Target format %d not supported", targetFormat); } RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::StaticInputAssembly; desc.m_bufferName = "VertexBuffer"; desc.m_byteCount = targetBufferSize; desc.m_elementSize = targetFormatSize; desc.m_bufferData = targetBufferData.data(); return RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); } RPI::Ptr RayTracingVertexFormatExampleComponent::ConvertIndexBuffer( AZStd::span sourceBufferData, const RHI::BufferViewDescriptor& sourceBufferDescriptor, RHI::IndexFormat targetFormat) { auto sourceBufferView{ sourceBufferData.subspan( sourceBufferDescriptor.m_elementOffset * sourceBufferDescriptor.m_elementSize, sourceBufferDescriptor.m_elementCount * sourceBufferDescriptor.m_elementSize) }; uint32_t targetFormatSize{ RHI::GetIndexFormatSize(targetFormat) }; // Use SizeAlignUp to ensure 4-byte-aligned reads in the shader are always valid when the buffer size is not a multiple of 4 uint32_t targetBufferSize{ SizeAlignUp(sourceBufferDescriptor.m_elementCount * targetFormatSize, 4) }; AZStd::vector targetBufferData(targetBufferSize); ConvertIndexBufferData(sourceBufferView, targetBufferData, targetFormat); RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::StaticInputAssembly; desc.m_bufferName = "IndexBuffer"; desc.m_byteCount = targetBufferSize; desc.m_elementSize = targetFormatSize; desc.m_bufferData = targetBufferData.data(); return RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); } Render::RayTracingFeatureProcessorInterface& RayTracingVertexFormatExampleComponent::GetRayTracingFeatureProcessor() { if (!m_rayTracingFeatureProcessor) { auto* scene{ RPI::Scene::GetSceneForEntityContextId(GetEntityContextId()) }; auto featureProcessor{ scene->GetFeatureProcessor() }; AZ_Assert(featureProcessor != nullptr, "RayTracingFeatureProcessor not found"); m_rayTracingFeatureProcessor = featureProcessor; } return *m_rayTracingFeatureProcessor; } Render::RayTracingDebugFeatureProcessorInterface& RayTracingVertexFormatExampleComponent::GetRayTracingDebugFeatureProcessor() { if (!m_rayTracingDebugFeatureProcessor) { auto* scene{ RPI::Scene::GetSceneForEntityContextId(GetEntityContextId()) }; auto featureProcessor{ scene->GetFeatureProcessor() }; AZ_Assert(featureProcessor != nullptr, "RayTracingDebugFeatureProcessor not found"); m_rayTracingDebugFeatureProcessor = featureProcessor; } return *m_rayTracingDebugFeatureProcessor; } } // namespace AtomSampleViewer