/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/Engine/RendererCore/Meshes/Implementation/LodMeshComponent.cpp
321 строка
9 KB
Jan Krassnigg
Introduced attribute to mark properties as required (#1981)
09 июл 2026, 19:48
Не верифицирован
09 июл 2026, 19:48
111b7a7
Код
Авторство
О чём код?
#include <RendererCore/RendererCorePCH.h> #include <Core/Messages/SetColorMessage.h> #include <Core/WorldSerializer/WorldReader.h> #include <Core/WorldSerializer/WorldWriter.h> #include <RendererCore/Debug/DebugRenderer.h> #include <RendererCore/Meshes/LodMeshComponent.h> #include <RendererCore/Pipeline/RenderDataManager.h> #include <RendererCore/Pipeline/View.h> // clang-format off EZ_BEGIN_STATIC_REFLECTED_TYPE(ezLodMeshLod, ezNoBase, 2, ezRTTIDefaultAllocator<ezLodMeshLod>) { EZ_BEGIN_PROPERTIES { EZ_RESOURCE_MEMBER_PROPERTY("Mesh", m_hMesh)->AddAttributes(new ezAssetBrowserAttribute("CompatibleAsset_Mesh_Static"), new ezRequiredAttribute()), EZ_MEMBER_PROPERTY("Threshold", m_fThreshold) } EZ_END_PROPERTIES; } EZ_END_STATIC_REFLECTED_TYPE; EZ_BEGIN_COMPONENT_TYPE(ezLodMeshComponent, 1, ezComponentMode::Static) { EZ_BEGIN_PROPERTIES { EZ_ACCESSOR_PROPERTY("Color", GetColor, SetColor)->AddAttributes(new ezExposeColorAlphaAttribute()), EZ_ACCESSOR_PROPERTY("CustomData", GetCustomData, SetCustomData)->AddAttributes(new ezDefaultValueAttribute(ezVec4(0, 1, 0, 1))), EZ_ACCESSOR_PROPERTY("SortingDepthOffset", GetSortingDepthOffset, SetSortingDepthOffset), EZ_MEMBER_PROPERTY("BoundsOffset", m_vBoundsOffset), EZ_MEMBER_PROPERTY("BoundsRadius", m_fBoundsRadius)->AddAttributes(new ezDefaultValueAttribute(1.0f), new ezClampValueAttribute(0.01f, 100.0f)), EZ_ACCESSOR_PROPERTY("ShowDebugInfo", GetShowDebugInfo, SetShowDebugInfo), EZ_ACCESSOR_PROPERTY("OverlapRanges", GetOverlapRanges, SetOverlapRanges)->AddAttributes(new ezDefaultValueAttribute(true)), EZ_ARRAY_MEMBER_PROPERTY("Meshes", m_Meshes), } EZ_END_PROPERTIES; EZ_BEGIN_ATTRIBUTES { new ezCategoryAttribute("Rendering"), new ezSphereVisualizerAttribute("BoundsRadius", ezColor::MediumVioletRed, nullptr, ezVisualizerAnchor::Center, ezVec3(1.0f), "BoundsOffset"), new ezTransformManipulatorAttribute("BoundsOffset"), } EZ_END_ATTRIBUTES; EZ_BEGIN_MESSAGEHANDLERS { EZ_MESSAGE_HANDLER(ezMsgExtractRenderData, OnMsgExtractRenderData), EZ_MESSAGE_HANDLER(ezMsgSetColor, OnMsgSetColor), EZ_MESSAGE_HANDLER(ezMsgSetCustomData, OnMsgSetCustomData), } EZ_END_MESSAGEHANDLERS; } EZ_END_COMPONENT_TYPE; // clang-format on struct LodMeshCompFlags { enum Enum { ShowDebugInfo = 0, OverlapRanges = 1, }; }; ezLodMeshComponent::ezLodMeshComponent() = default; ezLodMeshComponent::~ezLodMeshComponent() = default; void ezLodMeshComponent::SetShowDebugInfo(bool bShow) { SetUserFlag(LodMeshCompFlags::ShowDebugInfo, bShow); } bool ezLodMeshComponent::GetShowDebugInfo() const { return GetUserFlag(LodMeshCompFlags::ShowDebugInfo); } void ezLodMeshComponent::SetOverlapRanges(bool bShow) { SetUserFlag(LodMeshCompFlags::OverlapRanges, bShow); } bool ezLodMeshComponent::GetOverlapRanges() const { return GetUserFlag(LodMeshCompFlags::OverlapRanges); } void ezLodMeshComponent::OnDeactivated() { ezRenderDataManager* pRenderDataManager = GetWorld()->GetModule<ezRenderDataManager>(); pRenderDataManager->DeleteInstanceData(m_InstanceDataOffset); SUPER::OnDeactivated(); } void ezLodMeshComponent::SerializeComponent(ezWorldWriter& inout_stream) const { SUPER::SerializeComponent(inout_stream); ezStreamWriter& s = inout_stream.GetStream(); s << m_Meshes.GetCount(); for (const auto& mesh : m_Meshes) { s << mesh.m_hMesh; s << mesh.m_fThreshold; } s << m_Color; s << m_fSortingDepthOffset; s << m_vBoundsOffset; s << m_fBoundsRadius; s << m_vCustomData; } void ezLodMeshComponent::DeserializeComponent(ezWorldReader& inout_stream) { SUPER::DeserializeComponent(inout_stream); const ezUInt32 uiVersion = inout_stream.GetComponentTypeVersion(GetStaticRTTI()); ezStreamReader& s = inout_stream.GetStream(); ezUInt32 uiMeshes = 0; s >> uiMeshes; m_Meshes.SetCount(uiMeshes); for (auto& mesh : m_Meshes) { s >> mesh.m_hMesh; s >> mesh.m_fThreshold; } s >> m_Color; s >> m_fSortingDepthOffset; s >> m_vBoundsOffset; s >> m_fBoundsRadius; if (uiVersion >= 2) { s >> m_vCustomData; } } ezResult ezLodMeshComponent::GetLocalBounds(ezBoundingBoxSphere& out_bounds, bool& out_bAlwaysVisible, ezMsgUpdateLocalBounds& ref_msg) { out_bounds = ezBoundingSphere::MakeFromCenterAndRadius(m_vBoundsOffset, m_fBoundsRadius); out_bAlwaysVisible = false; return EZ_SUCCESS; } void ezLodMeshComponent::OnMsgExtractRenderData(ezMsgExtractRenderData& msg) const { if (m_Meshes.IsEmpty()) return; if (msg.m_pView->GetCameraUsageHint() == ezCameraUsageHint::EditorView || msg.m_pView->GetCameraUsageHint() == ezCameraUsageHint::MainView) { UpdateSelectedLod(*msg.m_pView); } if (m_iCurLod >= (ezInt32)m_Meshes.GetCount()) return; auto hMesh = m_Meshes[m_iCurLod].m_hMesh; if (!hMesh.IsValid()) return; // Force dynamic instance data buffer since the render data is not cached, so we would trash the static instance data buffer every frame. const bool bDynamic = true; auto hInstanceDataBuffer = msg.m_pRenderDataManager->GetOrCreateInstanceDataAndFill(*this, bDynamic, GetOwner()->GetGlobalTransform(), m_InstanceDataOffset, GetUniqueIdForRendering(), m_Color, m_vCustomData); ezResourceLock<ezMeshResource> pMesh(hMesh, ezResourceAcquireMode::AllowLoadingFallback); ezArrayPtr<const ezMeshResourceDescriptor::SubMesh> parts = pMesh->GetSubMeshes(); for (ezUInt32 uiPartIndex = 0; uiPartIndex < parts.GetCount(); ++uiPartIndex) { const ezUInt32 uiMaterialIndex = parts[uiPartIndex].m_uiMaterialIndex; ezMaterialResourceHandle hMaterial; hMaterial = pMesh->GetMaterials()[uiMaterialIndex]; ezMeshRenderData* pRenderData = msg.m_pRenderDataManager->CreateRenderDataForThisFrame<ezMeshRenderData>(GetOwner()); pRenderData->m_fSortingDepthOffset = m_fSortingDepthOffset; pRenderData->SetFallbackGlobalBounds(GetOwner()->GetGlobalBounds()); pRenderData->Fill(m_InstanceDataOffset, hInstanceDataBuffer, hMaterial, hMesh, uiMaterialIndex, uiPartIndex); ezRenderData::Category category = ezMaterialResource::GetRenderDataCategory(hMaterial); msg.AddRenderData(pRenderData, category, ezRenderData::Caching::Never); } } void ezLodMeshComponent::SetColor(const ezColor& color) { m_Color = color; InvalidateCachedRenderData(); } const ezColor& ezLodMeshComponent::GetColor() const { return m_Color; } void ezLodMeshComponent::SetCustomData(const ezVec4& vData) { m_vCustomData = vData; InvalidateCachedRenderData(); } const ezVec4& ezLodMeshComponent::GetCustomData() const { return m_vCustomData; } void ezLodMeshComponent::SetSortingDepthOffset(float fOffset) { m_fSortingDepthOffset = fOffset; InvalidateCachedRenderData(); } float ezLodMeshComponent::GetSortingDepthOffset() const { return m_fSortingDepthOffset; } void ezLodMeshComponent::OnMsgSetColor(ezMsgSetColor& ref_msg) { ref_msg.ModifyColor(m_Color); InvalidateCachedRenderData(); } void ezLodMeshComponent::OnMsgSetCustomData(ezMsgSetCustomData& ref_msg) { m_vCustomData = ref_msg.m_vData; InvalidateCachedRenderData(); } static float CalculateSphereScreenSpaceCoverage(const ezBoundingSphere& sphere, const ezCamera& camera) { if (camera.IsPerspective()) { return ezGraphicsUtils::CalculateSphereScreenCoverage(sphere, camera.GetCenterPosition(), camera.GetFovY(1.0f)); } else { return ezGraphicsUtils::CalculateSphereScreenCoverage(sphere.m_fRadius, camera.GetDimensionY(1.0f)); } } void ezLodMeshComponent::UpdateSelectedLod(const ezView& view) const { const ezInt32 iNumLods = (ezInt32)m_Meshes.GetCount(); const ezVec3 vScale = GetOwner()->GetGlobalScaling(); const float fScale = ezMath::Max(vScale.x, vScale.y, vScale.z); const ezVec3 vCenter = GetOwner()->GetGlobalTransform() * m_vBoundsOffset; const float fCoverage = CalculateSphereScreenSpaceCoverage(ezBoundingSphere::MakeFromCenterAndRadius(vCenter, fScale * m_fBoundsRadius), *view.GetLodCamera()); // clamp the input value, this is to prevent issues while editing the threshold array ezInt32 iNewLod = ezMath::Clamp<ezInt32>(m_iCurLod, 0, iNumLods); float fCoverageP = 1; float fCoverageN = 0; if (iNewLod > 0) { fCoverageP = m_Meshes[iNewLod - 1].m_fThreshold; } if (iNewLod < iNumLods) { fCoverageN = m_Meshes[iNewLod].m_fThreshold; } if (GetOverlapRanges()) { const float fLodRangeOverlap = 0.40f; if (iNewLod + 1 < iNumLods) { float range = (fCoverageN - m_Meshes[iNewLod + 1].m_fThreshold); fCoverageN -= range * fLodRangeOverlap; // overlap into the next range } else { float range = (fCoverageN - 0.0f); fCoverageN -= range * fLodRangeOverlap; // overlap into the next range } } if (fCoverage < fCoverageN) { ++iNewLod; } else if (fCoverage > fCoverageP) { --iNewLod; } iNewLod = ezMath::Clamp(iNewLod, 0, iNumLods); m_iCurLod = iNewLod; if (GetShowDebugInfo()) { ezStringBuilder sb; sb.SetFormat("Coverage: {}\nLOD {}\nRange: {} - {}", ezArgF(fCoverage, 3), iNewLod, ezArgF(fCoverageP, 3), ezArgF(fCoverageN, 3)); ezDebugRenderer::Draw3DText(view.GetHandle(), sb, GetOwner()->GetGlobalPosition(), ezColor::White); } } EZ_STATICLINK_FILE(RendererCore, RendererCore_Meshes_Implementation_LodMeshComponent);