From a27a569a3279856c4acec5b46ec03f6632e6dc9d Mon Sep 17 00:00:00 2001 From: Pavle Date: Thu, 20 Aug 2026 17:10:53 +0200 Subject: [PATCH 01/10] Add debug rendering for sound gates and sequences --- Mods/Editor/Src/Components/DebugChannels.cpp | 743 ++++++++++++++++-- Mods/Editor/Src/Editor.h | 43 + ZHMModSDK/Include/Functions.h | 14 + .../Include/Glacier/IBoolConditionListener.h | 6 + ZHMModSDK/Include/Glacier/SColorRGBA.h | 8 +- ZHMModSDK/Include/Glacier/TSharedValue.h | 15 + ZHMModSDK/Include/Glacier/ZCircleGenerator.h | 82 ++ ZHMModSDK/Include/Glacier/ZColor.h | 23 +- ZHMModSDK/Include/Glacier/ZItem.h | 3 +- ZHMModSDK/Include/Glacier/ZMath.h | 10 +- ZHMModSDK/Include/Glacier/ZResource.h | 4 +- ZHMModSDK/Include/Glacier/ZSequence.h | 231 ++++++ ZHMModSDK/Include/Glacier/ZSound.h | 45 ++ ZHMModSDK/Include/IRenderer.h | 7 + ZHMModSDK/Src/Functions.cpp | 21 + .../Rendering/Renderers/DirectXTKRenderer.cpp | 230 +++++- .../Rendering/Renderers/DirectXTKRenderer.h | 36 + 17 files changed, 1417 insertions(+), 104 deletions(-) create mode 100644 ZHMModSDK/Include/Glacier/IBoolConditionListener.h create mode 100644 ZHMModSDK/Include/Glacier/TSharedValue.h create mode 100644 ZHMModSDK/Include/Glacier/ZCircleGenerator.h create mode 100644 ZHMModSDK/Include/Glacier/ZSequence.h create mode 100644 ZHMModSDK/Include/Glacier/ZSound.h diff --git a/Mods/Editor/Src/Components/DebugChannels.cpp b/Mods/Editor/Src/Components/DebugChannels.cpp index ec515ec0c..1b6118f34 100644 --- a/Mods/Editor/Src/Components/DebugChannels.cpp +++ b/Mods/Editor/Src/Components/DebugChannels.cpp @@ -5,6 +5,7 @@ #include "Glacier/ZModule.h" #include "Glacier/Enums.h" #include "Glacier/SColorRGB.h" +#include "Glacier/ZColor.h" #include "Logging.h" @@ -189,10 +190,12 @@ void Editor::InitializeDebugChannels() { m_DebugChannelNameToTypeNames["Engine"].push_back("ZTrailShapeEntity"); m_DebugChannelNameToTypeNames["Engine"].push_back("ZSplineEntity"); m_DebugChannelNameToTypeNames["Engine"].push_back("ZSplineControlPointEntity"); + m_DebugChannelNameToTypeNames["Engine"].push_back("ZSequenceEntity"); m_DebugChannelNameToTypeNames["Sound"].push_back("ZDebugGizmoEntity"); m_DebugChannelNameToTypeNames["Sound"].push_back("ZAudioEmitterSpatialAspect"); m_DebugChannelNameToTypeNames["Sound"].push_back("ZAudioEmitterVolumetricAspect"); + m_DebugChannelNameToTypeNames["Sound"].push_back("ZSoundGateEntity"); m_DebugChannelNameToTypeNames["Animation"].push_back("ZDebugGizmoEntity"); @@ -278,6 +281,8 @@ void Editor::InitializeDebugEntityTypeIDs() { m_DebugEntityTypeIds[DebugEntityTypeName::AudioEmitterSpatialAspect] = s_TypeRegistry->GetTypeID("ZAudioEmitterSpatialAspect"); m_DebugEntityTypeIds[DebugEntityTypeName::AudioEmitterVolumetricAspect] = s_TypeRegistry->GetTypeID("ZAudioEmitterVolumetricAspect"); m_DebugEntityTypeIds[DebugEntityTypeName::ClothWireEntity] = s_TypeRegistry->GetTypeID("ZClothWireEntity"); + m_DebugEntityTypeIds[DebugEntityTypeName::SoundGateEntity] = s_TypeRegistry->GetTypeID("ZSoundGateEntity"); + m_DebugEntityTypeIds[DebugEntityTypeName::SequenceEntity] = s_TypeRegistry->GetTypeID("ZSequenceEntity"); } void Editor::DrawDebugChannelsWindow(bool p_HasFocus) { @@ -402,7 +407,7 @@ void Editor::DrawDebugChannelsWindow(bool p_HasFocus) { ); }; - ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 220.f); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 400.f); ImGui::TableSetupColumn( "Gizmo", ImGuiTableColumnFlags_WidthFixed, @@ -418,12 +423,15 @@ void Editor::DrawDebugChannelsWindow(bool p_HasFocus) { ImGui::TableNextColumn(); s_CenterItem(ImGui::CalcTextSize("Type").x); + ImGui::TextUnformatted("Type"); ImGui::TableNextColumn(); s_CenterItem(ImGui::CalcTextSize("Gizmo").x); + ImGui::TextUnformatted("Gizmo"); ImGui::TableNextColumn(); s_CenterItem(ImGui::CalcTextSize("Shape").x); + ImGui::TextUnformatted("Shape"); for (const auto& [s_TypeName, s_Count] : s_TypeNameToDebugEntityCount) { bool& s_DrawGizmo = s_TypeNameToGizmoState[s_TypeName]; @@ -452,6 +460,17 @@ void Editor::DrawDebugChannelsWindow(bool p_HasFocus) { &s_DrawShape ); ImGui::EndDisabled(); + + if (s_TypeName == "ZSoundGateEntity") { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::Indent(); + ImGui::BeginDisabled(!s_DrawShapes || !s_DrawShape); + ImGui::Checkbox("Show sound occlusion", &m_ShowSoundOcclusion); + ImGui::EndDisabled(); + ImGui::Unindent(); + } } ImGui::EndTable(); @@ -631,6 +650,392 @@ void Editor::DrawShapes(const DebugEntity& p_DebugEntity, IRenderer* p_Renderer) return; } } + + if (p_DebugEntity.m_TypeName == "ZSoundGateEntity") { + DrawShapes( + p_DebugEntity.m_EntityRef.QueryInterface( + m_DebugEntityTypeIds[DebugEntityTypeName::SoundGateEntity] + ), + p_Renderer + ); + } + else if (p_DebugEntity.m_TypeName == "ZSequenceEntity") { + DrawShapes( + p_DebugEntity.m_EntityRef.QueryInterface( + m_DebugEntityTypeIds[DebugEntityTypeName::SequenceEntity] + ), + p_Renderer + ); + } +} + +void Editor::DrawShapes(ZSequenceEntity* p_SequenceEntity, IRenderer* p_Renderer) { + static const SVector4 s_CurveColor = SVector4(1, 1, 0, 1); + + for (const auto& s_Entity : p_SequenceEntity->m_aTracksAndGroups) { + ZEntityGroup* s_Group = s_Entity.QueryInterface(); + + if (!s_Group) { + continue; + } + + for (const auto& s_Entity : s_Group->m_aTracksAndGroups) { + ZTrajectoryTrackBase* s_Track = s_Entity.QueryInterface(); + + if (s_Track) { + DrawTrajectoryTrack(p_Renderer, s_Track, s_CurveColor, s_Group->m_targetEntity); + } + } + } +} + +void Editor::DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Renderer) { + SMatrix s_WorldMatrix = p_SoundGateEntity->GetObjectToWorldMatrix(); + const float4 s_Position = s_WorldMatrix.Trans; + + const float4 s_Size( + p_SoundGateEntity->m_vLocalSize.x, + 0, + p_SoundGateEntity->m_vLocalSize.y, + 1.f + ); + + p_Renderer->DrawBox3D( + s_Position, + s_Size, + s_WorldMatrix, + ZColor::UnpackUnsigned(0x3FFF0000) + ); + + const float4 s_SourceOffset( + 0.f, + -p_SoundGateEntity->m_fThicknessSource, + 0.f, + 0.f + ); + + s_WorldMatrix.Trans = s_WorldMatrix.WVectorTransform(s_SourceOffset); + + p_Renderer->DrawBox3D( + s_WorldMatrix.Trans, + s_Size, + s_WorldMatrix, + ZColor::UnpackUnsigned(0x3F008000) + ); + + const float4 s_DestinationOffset( + 0.f, + p_SoundGateEntity->m_fThicknessDestination, + 0.f, + 0.f + ); + + s_WorldMatrix.Trans = s_WorldMatrix.WVectorTransform(s_DestinationOffset); + + p_Renderer->DrawBox3D( + s_WorldMatrix.Trans, + s_Size, + s_WorldMatrix, + ZColor::UnpackUnsigned(0x3F800080) + ); + + if (m_ShowSoundOcclusion) { + p_Renderer->DrawSphere3D( + s_WorldMatrix, + s_Position, + p_SoundGateEntity->m_fOcclusionMinDistance, + ZColor::UnpackUnsigned(0xFF00CD00) + ); + + p_Renderer->DrawSphere3D( + s_WorldMatrix, + s_Position, + p_SoundGateEntity->m_fOcclusionMaxDistance, + ZColor::UnpackUnsigned(0xFF0000FF) + ); + } +} + +void Editor::DrawTrajectoryTrack( + IRenderer* p_Renderer, + ZTrajectoryTrackBase* p_Track, + const SVector4& p_Color, + ZEntityRef p_TargetEntity +) { + for (const auto& s_ItemEntity : p_Track->m_aItems) { + ZMorphemeTrajectorySource* s_Item = s_ItemEntity.m_entityRef.QueryInterface(); + + if (s_Item) { + DrawTrajectoryItem( + p_Renderer, + s_Item, + p_Track, + p_Color, + p_TargetEntity, + [&](auto* p_Item, auto* p_Track, ZGameTime p_ItemTime, ZEntityRef p_TargetEntity) { + return EvaluateTrajectoryWorldPosition( + p_Item, + p_Track, + p_ItemTime, + p_Item->m_pAnimationResource.GetResource(), + p_TargetEntity + ); + } + ); + + continue; + } + + ZEulerAngleTrajectorySource* s_Item2 = s_ItemEntity.m_entityRef.QueryInterface(); + + if (s_Item2) { + DrawTrajectoryItem( + p_Renderer, + s_Item2, + p_Track, + p_Color, + p_TargetEntity, + [&](auto* p_Item, auto* p_Track, ZGameTime p_ItemTime, ZEntityRef p_TargetEntity) { + return EvaluateTrajectoryWorldPosition(p_Item, p_Track, p_ItemTime, p_TargetEntity); + } + ); + + continue; + } + + ZBezierSplineTrajectorySource* s_Item3 = s_ItemEntity.m_entityRef.QueryInterface(); + + if (s_Item3) { + DrawTrajectoryItem( + p_Renderer, + s_Item3, + p_Track, + p_Color, + p_TargetEntity, + [&](auto* p_Item, auto* p_Track, ZGameTime p_ItemTime, ZEntityRef p_TargetEntity) { + return EvaluateTrajectoryWorldPosition(p_Item, p_Track, p_ItemTime, p_TargetEntity); + } + ); + } + } +} + +template +void Editor::DrawTrajectoryItem( + IRenderer* p_Renderer, + TItem* p_Item, + ZTrajectoryTrackBase* p_Track, + const SVector4& p_Color, + ZEntityRef p_TargetEntity, + EvaluateFn&& p_EvaluateFn +) { + const ZGameTime s_ItemStart = p_Item->m_startTime; + const ZGameTime s_ItemDuration = p_Item->m_duration; + const ZGameTime s_ItemEnd = s_ItemStart + s_ItemDuration; + + ZGameTime s_SampleTime = s_ItemStart; + const ZGameTime s_SampleStep = ZGameTime::FromSeconds(0.02f); + + const SVector3 s_StartPosition = p_EvaluateFn(p_Item, p_Track, s_ItemStart, p_TargetEntity); + SVector3 s_PreviousSamplePosition = p_EvaluateFn(p_Item, p_Track, s_SampleTime, p_TargetEntity); + + while (s_SampleTime < s_ItemEnd) { + s_SampleTime += s_SampleStep; + + SVector3 s_CurrentSamplePosition = p_EvaluateFn(p_Item, p_Track, s_SampleTime, p_TargetEntity); + + p_Renderer->DrawLine3D(s_PreviousSamplePosition, s_CurrentSamplePosition, p_Color, p_Color); + + s_PreviousSamplePosition = s_CurrentSamplePosition; + } + + p_Renderer->DrawSphere3D(SMatrix {}, s_StartPosition, 0.05f, SVector4 { 0.0f, 1.0f, 0.0f, 1.0f }); + p_Renderer->DrawSphere3D(SMatrix {}, s_PreviousSamplePosition, 0.05f, SVector4 { 1.0f, 0.0f, 0.0f, 1.0f }); + + const auto s_CurrentCamera = Functions::GetCurrentCamera->Call(); + + if (!s_CurrentCamera) { + return; + } + + auto s_CameraTransform = s_CurrentCamera->GetObjectToWorldMatrix(); + + /*s_CameraTransform.Trans = s_StartPosition; + + p_Renderer->DrawText3D( + fmt::format("{:016x}", p_Item->m_pEntityType->m_nEntityID).c_str(), + s_CameraTransform, + SVector4(1.f, 1.f, 0.f, 1.f), + 0.2f + ); + + s_CameraTransform.Trans = s_PreviousSamplePosition; + + p_Renderer->DrawText3D( + fmt::format("{:016x}", p_Item->m_pEntityType->m_nEntityID).c_str(), + s_CameraTransform, + SVector4(1.f, 1.f, 0.f, 1.f), + 0.2f + );*/ +} + +SVector3 Editor::EvaluateTrajectoryWorldPosition( + ZMorphemeTrajectorySource* p_Item, + ZTrajectoryTrackBase* p_Track, + ZGameTime p_ItemTime, + ZAnimationResource* p_AnimationResource, + ZEntityRef p_TargetEntity +) { + ZGameTime s_AnimationTime = p_ItemTime - p_Item->m_startTime; + + SMatrix s_TrajectoryTransform; + + Functions::ZMorphemeTrajectorySource_GetTrajectoryAtTime->Call(p_Item, &s_TrajectoryTransform, s_AnimationTime); + + SMatrix s_WorldTransform; + + switch (p_Item->m_eCoordinateSpace) { + case ECoordinateSpace::CSPACE_OBJECT_TO_WORLD: + s_WorldTransform = s_TrajectoryTransform; + break; + case ECoordinateSpace::CSPACE_CHARACTER_RELATIVE: { + SMatrix s_StartWorldTransform; + + p_Track->GetStartWorldTransform(s_StartWorldTransform); + + s_WorldTransform = s_TrajectoryTransform * s_StartWorldTransform; + + break; + } + case ECoordinateSpace::CSPACE_OBJECT_TO_PARENT: { + ZSpatialEntity* s_SpatialEntity = p_TargetEntity.QueryInterface(); + + if (s_SpatialEntity && s_SpatialEntity->m_pTransformParent) { + SMatrix s_ParentWorldMatrix = s_SpatialEntity->m_pTransformParent->GetObjectToWorldMatrix(); + + s_WorldTransform = s_TrajectoryTransform * s_ParentWorldMatrix; + } + + break; + } + case ECoordinateSpace::CSPACE_SEQUENCE_ORIGIN: { + SMatrix s_SequenceOrigin; + + s_WorldTransform = s_TrajectoryTransform * SMatrix(p_AnimationResource->m_sequenceOrigin); + + if (p_Track->m_pSequence.m_pInterfaceRef->m_sequenceOrigin) { + auto s_SequenceOriginEntity = p_Track->m_pSequence.m_pInterfaceRef->m_sequenceOrigin; + s_SequenceOrigin = s_SequenceOriginEntity.m_pInterfaceRef->GetObjectToWorldMatrix(); + } + + s_WorldTransform = s_TrajectoryTransform * s_SequenceOrigin; + + break; + } + } + + return s_WorldTransform.Trans; +} + +SVector3 Editor::EvaluateTrajectoryWorldPosition( + ZEulerAngleTrajectorySource* p_Item, + ZTrajectoryTrackBase* p_Track, + ZGameTime p_ItemTime, + ZEntityRef p_TargetEntity +) { + ZGameTime s_LocalTime = p_ItemTime - p_Item->m_startTime; + ZGameTime s_NormalizedTime = s_LocalTime / p_Item->m_duration; + + SMatrix s_TrajectoryTransform; + + Functions::ZEulerAngleTrajectorySource_GetTrajectoryAtTime->Call(p_Item, &s_TrajectoryTransform, s_NormalizedTime); + + SMatrix s_WorldTransform; + + switch (p_Item->m_eCoordinateSpace) { + case ECoordinateSpace::CSPACE_OBJECT_TO_WORLD: + s_WorldTransform = s_TrajectoryTransform; + break; + case ECoordinateSpace::CSPACE_CHARACTER_RELATIVE: { + SMatrix s_StartWorldTransform; + + p_Track->GetStartWorldTransform(s_StartWorldTransform); + + s_WorldTransform = s_TrajectoryTransform * s_StartWorldTransform; + + break; + } + case ECoordinateSpace::CSPACE_OBJECT_TO_PARENT: { + ZSpatialEntity* s_SpatialEntity = p_TargetEntity.QueryInterface(); + + if (s_SpatialEntity && s_SpatialEntity->m_pTransformParent) { + SMatrix s_ParentWorldMatrix = s_SpatialEntity->m_pTransformParent->GetObjectToWorldMatrix(); + + s_WorldTransform = s_TrajectoryTransform * s_ParentWorldMatrix; + } + + break; + } + case ECoordinateSpace::CSPACE_SEQUENCE_ORIGIN: { + SMatrix s_SequenceOrigin; + + if (p_Track->m_pSequence.m_pInterfaceRef->m_sequenceOrigin) { + auto s_SequenceOriginEntity = p_Track->m_pSequence.m_pInterfaceRef->m_sequenceOrigin; + s_SequenceOrigin = s_SequenceOriginEntity.m_pInterfaceRef->GetObjectToWorldMatrix(); + } + + s_WorldTransform = s_TrajectoryTransform * s_SequenceOrigin; + + break; + } + } + + return s_WorldTransform.Trans; +} + +SVector3 Editor::EvaluateTrajectoryWorldPosition( + ZBezierSplineTrajectorySource* p_Item, + ZTrajectoryTrackBase* p_Track, + ZGameTime p_ItemTime, + ZEntityRef p_TargetEntity +) { + ZGameTime s_LocalTime = p_ItemTime - p_Item->m_startTime; + ZGameTime s_NormalizedTime = s_LocalTime / p_Item->m_duration; + + SMatrix s_TrajectoryTransform; + + Functions::ZBezierSplineTrajectorySource_GetTrajectoryAtTime->Call(p_Item, &s_TrajectoryTransform, s_NormalizedTime); + + SMatrix s_WorldTransform; + + switch (p_Item->m_eCoordinateSpace) { + case ECoordinateSpace::CSPACE_OBJECT_TO_WORLD: + case ECoordinateSpace::CSPACE_SEQUENCE_ORIGIN: + s_WorldTransform = s_TrajectoryTransform; + break; + case ECoordinateSpace::CSPACE_CHARACTER_RELATIVE: { + SMatrix s_StartWorldTransform; + + p_Track->GetStartWorldTransform(s_StartWorldTransform); + + s_WorldTransform = s_TrajectoryTransform * s_StartWorldTransform; + + break; + } + case ECoordinateSpace::CSPACE_OBJECT_TO_PARENT: { + ZSpatialEntity* s_SpatialEntity = p_TargetEntity.QueryInterface(); + + if (s_SpatialEntity && s_SpatialEntity->m_pTransformParent) { + SMatrix s_ParentWorldMatrix = s_SpatialEntity->m_pTransformParent->GetObjectToWorldMatrix(); + + s_WorldTransform = s_TrajectoryTransform * s_ParentWorldMatrix; + } + + break; + } + } + + return s_WorldTransform.Trans; } void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTreeNode) { @@ -672,45 +1077,76 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::GuideLadder] )) { if (EntityIDMatches(s_GuideLadder, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZGuideLadder", DEBUGCHANNEL_GUIDES_LADDERS, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZGuideLadder", + DEBUGCHANNEL_GUIDES_LADDERS, + "m_pHelper" + ); } } else if (auto s_GuideWindow = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::GuideWindow] )) { if (EntityIDMatches(s_GuideWindow, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZGuideWindow", DEBUGCHANNEL_GUIDES_WINDOWS, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZGuideWindow", + DEBUGCHANNEL_GUIDES_WINDOWS, + "m_pHelper" + ); } } else if (auto s_PFBoxEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::PFBoxEntity] )) { if (EntityIDMatches(s_PFBoxEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZPFBoxEntity", DEBUGCHANNEL_GUIDES_PATHFINDER, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZPFBoxEntity", + DEBUGCHANNEL_GUIDES_PATHFINDER, + "m_pHelper" + ); } } else if (auto s_PFObstacleEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::PFObstacleEntity] )) { if (EntityIDMatches(s_PFObstacleEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZPFObstacleEntity", DEBUGCHANNEL_GUIDES_PATHFINDER, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZPFObstacleEntity", + DEBUGCHANNEL_GUIDES_PATHFINDER, + "m_pHelper" + ); } } else if (auto s_PFSeedPoint = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::PFSeedPoint] )) { if (EntityIDMatches(s_PFSeedPoint, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZPFSeedPoint", DEBUGCHANNEL_GUIDES_PATHFINDER, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZPFSeedPoint", + DEBUGCHANNEL_GUIDES_PATHFINDER, + "m_pHelper" + ); } } else if (auto s_DebugGizmoEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::DebugGizmoEntity] )) { if (EntityIDMatches(s_DebugGizmoEntity, p_EntityTreeNode->EntityId)) { - const ZDebugGizmoEntity_EDrawLayer s_DrawLayer = p_EntityTreeNode->Entity.GetProperty("m_eDrawLayer").Get(); + const ZDebugGizmoEntity_EDrawLayer s_DrawLayer = + p_EntityTreeNode->Entity.GetProperty("m_eDrawLayer").Get(); const EDebugChannel s_DebugChannel = ConvertDrawLayerToDebugChannel(s_DrawLayer); - AddGizmoEntity(p_EntityTreeNode->Entity, "ZDebugGizmoEntity", s_DebugChannel, "m_GizmoGeomRID"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZDebugGizmoEntity", + s_DebugChannel, + "m_GizmoGeomRID" + ); } } else if (auto s_PureWaveModifierEntity = p_EntityTreeNode->Entity.QueryInterface( @@ -719,12 +1155,16 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre if (EntityIDMatches(s_PureWaveModifierEntity, p_EntityTreeNode->EntityId)) { const float4 s_DirectionGizmoScale(-3.f, -3.f, 3.f, 0.f); const float4 s_DirectionGizmoTranslate(0.f, 0.f, 0.f, 0.f); - const SMatrix s_DirectionGizmoTransform = SMatrix::ScaleTranslate(s_DirectionGizmoScale, s_DirectionGizmoTranslate); + const SMatrix s_DirectionGizmoTransform = SMatrix::ScaleTranslate( + s_DirectionGizmoScale, s_DirectionGizmoTranslate + ); const float s_Radius = p_EntityTreeNode->Entity.GetProperty("m_fRadius").Get(); const float4 s_RadiusGizmoScale(s_Radius, s_Radius, s_Radius, 0.f); const float4 s_RadiusGizmoTranslate(0.f, 0.f, 0.f, 0.f); - const SMatrix s_RadiusGizmoTransform = SMatrix::ScaleTranslate(s_RadiusGizmoScale, s_RadiusGizmoTranslate); + const SMatrix s_RadiusGizmoTransform = SMatrix::ScaleTranslate( + s_RadiusGizmoScale, s_RadiusGizmoTranslate + ); AddGizmoEntity( p_EntityTreeNode->Entity, @@ -758,7 +1198,8 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre s_TypeName = "ZDarkLightEntity"; } - const ILightEntity_ELightType s_LightType = p_EntityTreeNode->Entity.GetProperty("m_eLightType").Get(); + const ILightEntity_ELightType s_LightType = + p_EntityTreeNode->Entity.GetProperty("m_eLightType").Get(); switch (s_LightType) { case ILightEntity_ELightType::LT_DIRECTIONAL: @@ -815,14 +1256,24 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::CubemapProbeEntity] )) { if (EntityIDMatches(s_CubemapProbeEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZCubemapProbeEntity", DEBUGCHANNEL_LIGHT, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZCubemapProbeEntity", + DEBUGCHANNEL_LIGHT, + "m_pHelper" + ); } } else if (auto s_VolumeLightEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::VolumeLightEntity] )) { if (EntityIDMatches(s_VolumeLightEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZVolumeLightEntity", DEBUGCHANNEL_LIGHT, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZVolumeLightEntity", + DEBUGCHANNEL_LIGHT, + "m_pHelper" + ); } } else if (auto s_ParticleEmitterBoxEntity = p_EntityTreeNode->Entity.QueryInterface( @@ -877,36 +1328,30 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::ParticleGlobalAttractorEntity] )) { if (EntityIDMatches(s_ParticleGlobalAttractorEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZParticleGlobalAttractorEntity", DEBUGCHANNEL_PARTICLES, "m_pHelper"); } } else if (auto s_ParticleKillVolumeEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::ParticleKillVolumeEntity] )) { if (EntityIDMatches(s_ParticleKillVolumeEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZParticleKillVolumeEntity", DEBUGCHANNEL_PARTICLES, "m_pHelper"); } } else if (auto s_GateEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::GateEntity] )) { if (EntityIDMatches(s_GateEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZGateEntity", DEBUGCHANNEL_PARTITIONING, "m_pHelper"); - AddGizmoEntity(p_EntityTreeNode->Entity, "ZGateEntity", DEBUGCHANNEL_PARTITIONING, "m_pHelperClosed"); } } else if (auto s_OccluderEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::OccluderEntity] )) { if (EntityIDMatches(s_OccluderEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZOccluderEntity", DEBUGCHANNEL_PARTITIONING, "m_pHelper"); } } else if (auto s_DecalsSpawnEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::DecalsSpawnEntity] )) { if (EntityIDMatches(s_DecalsSpawnEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZDecalsSpawnEntity", DEBUGCHANNEL_DECALS, "m_pHelper"); } } else if (auto s_StaticDecalEntity = p_EntityTreeNode->Entity.QueryInterface( @@ -925,50 +1370,82 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::LiquidTrailEntity] )) { if (EntityIDMatches(s_LiquidTrailEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZLiquidTrailEntity", DEBUGCHANNEL_DECALS, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZLiquidTrailEntity", + DEBUGCHANNEL_DECALS, + "m_pHelper" + ); } } else if (auto s_CrowdActorGroupEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::CrowdActorGroupEntity] )) { if (EntityIDMatches(s_CrowdActorGroupEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZCrowdActorGroupEntity", DEBUGCHANNEL_CROWD, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZCrowdActorGroupEntity", + DEBUGCHANNEL_CROWD, + "m_pHelper" + ); } } else if (auto s_CrowdActorGroupFocalPointEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::CrowdActorGroupFocalPointEntity] )) { if (EntityIDMatches(s_CrowdActorGroupFocalPointEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZCrowdActorGroupFocalPointEntity", DEBUGCHANNEL_CROWD, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZCrowdActorGroupFocalPointEntity", + DEBUGCHANNEL_CROWD, + "m_pHelper" + ); } } else if (auto s_CrowdEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::CrowdEntity] )) { if (EntityIDMatches(s_CrowdEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZCrowdEntity", DEBUGCHANNEL_CROWD, "m_pGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZCrowdEntity", + DEBUGCHANNEL_CROWD, + "m_pGizmo" + ); } } else if (auto s_ManualActorEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::ManualActorEntity] )) { if (EntityIDMatches(s_ManualActorEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZManualActorEntity", DEBUGCHANNEL_CROWD, "m_pGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZManualActorEntity", + DEBUGCHANNEL_CROWD, + "m_pGizmo" + ); } } else if (auto s_SplineCrowdFlowEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::SplineCrowdFlowEntity] )) { if (EntityIDMatches(s_SplineCrowdFlowEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSplineCrowdFlowEntity", DEBUGCHANNEL_CROWD, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSplineCrowdFlowEntity", + DEBUGCHANNEL_CROWD, + "m_pHelper" + ); } } else if (p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::BoxShapeAspect] )) { - const bool isComposite = p_EntityTreeNode->Entity.QueryInterface(s_CompositeEntityTypeID); + const bool s_IsCompositeEntity = p_EntityTreeNode->Entity.QueryInterface( + s_CompositeEntityTypeID + ); - if (!isComposite) { + if (!s_IsCompositeEntity) { AddGizmoEntity( p_EntityTreeNode->Entity, "ZBoxShapeAspect", @@ -980,9 +1457,11 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre else if (p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::CapsuleShapeAspect] )) { - const bool isComposite = p_EntityTreeNode->Entity.QueryInterface(s_CompositeEntityTypeID); + const bool s_IsCompositeEntity = p_EntityTreeNode->Entity.QueryInterface( + s_CompositeEntityTypeID + ); - if (!isComposite) { + if (!s_IsCompositeEntity) { AddGizmoEntity( p_EntityTreeNode->Entity, "ZCapsuleShapeAspect", @@ -994,9 +1473,11 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre else if (p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::SphereShapeAspect] )) { - const bool isComposite = p_EntityTreeNode->Entity.QueryInterface(s_CompositeEntityTypeID); + const bool s_IsCompositeEntity = p_EntityTreeNode->Entity.QueryInterface( + s_CompositeEntityTypeID + ); - if (!isComposite) { + if (!s_IsCompositeEntity) { AddGizmoEntity( p_EntityTreeNode->Entity, "ZSphereShapeAspect", @@ -1009,7 +1490,12 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::WindEntity] )) { if (EntityIDMatches(s_WindEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZWindEntity", DEBUGCHANNEL_PHYSICS, "m_pHelperGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZWindEntity", + DEBUGCHANNEL_PHYSICS, + "m_pHelperGizmo" + ); } } else if (auto s_AISoundConnector = p_EntityTreeNode->Entity.QueryInterface( @@ -1081,21 +1567,36 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::AgitatedWaypointEntity] )) { if (EntityIDMatches(s_AgitatedWaypointEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZAgitatedWaypointEntity", DEBUGCHANNEL_AI, "m_pGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZAgitatedWaypointEntity", + DEBUGCHANNEL_AI, + "m_pGizmo" + ); } } else if (auto s_CombatActEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::CombatActEntity] )) { if (EntityIDMatches(s_CombatActEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZCombatActEntity", DEBUGCHANNEL_AI, "m_pGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZCombatActEntity", + DEBUGCHANNEL_AI, + "m_pGizmo" + ); } } else if (auto s_LookAtEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::LookAtEntity] )) { if (EntityIDMatches(s_LookAtEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZLookAtEntity", DEBUGCHANNEL_AI, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZLookAtEntity", + DEBUGCHANNEL_AI, + "m_pHelper" + ); } } else if (auto s_OutfitProviderEntity = p_EntityTreeNode->Entity.QueryInterface( @@ -1114,7 +1615,12 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::PointOfInterestEntity] )) { if (EntityIDMatches(s_PointOfInterestEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZPointOfInterestEntity", DEBUGCHANNEL_AI, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZPointOfInterestEntity", + DEBUGCHANNEL_AI, + "m_pHelper" + ); } } else if (auto s_ActBehaviorEntity = p_EntityTreeNode->Entity.QueryInterface( @@ -1144,7 +1650,9 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre } const ZActBehaviorEntity_ERotationAlignment s_AlignRotation = - p_EntityTreeNode->Entity.GetProperty("m_AlignRotation").Get(); + p_EntityTreeNode->Entity.GetProperty( + "m_AlignRotation" + ).Get(); if (s_AlignRotation != ZActBehaviorEntity_ERotationAlignment::RA_NONE) { const SColorRGB s_Color = p_EntityTreeNode->Entity.GetProperty("m_Color").Get(); @@ -1184,15 +1692,31 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::SpawnPointEntity] )) { if (EntityIDMatches(s_SpawnPointEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSpawnPointEntity", DEBUGCHANNEL_AI, "m_pGizmo"); - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSpawnPointEntity", DEBUGCHANNEL_AI, "m_pAlignGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSpawnPointEntity", + DEBUGCHANNEL_AI, + "m_pGizmo" + ); + + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSpawnPointEntity", + DEBUGCHANNEL_AI, + "m_pAlignGizmo" + ); } } else if (auto s_BystanderPointEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::BystanderPointEntity] )) { if (EntityIDMatches(s_BystanderPointEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZBystanderPointEntity", DEBUGCHANNEL_AI, "m_pGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZBystanderPointEntity", + DEBUGCHANNEL_AI, + "m_pGizmo" + ); } } else if (auto s_WaypointEntity = p_EntityTreeNode->Entity.QueryInterface( @@ -1253,86 +1777,160 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre m_DebugEntityTypeIds[DebugEntityTypeName::PostfilterAreaBoxEntity] )) { if (EntityIDMatches(s_PostfilterAreaBoxEntit, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZPostfilterAreaBoxEntity", DEBUGCHANNEL_GAME, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZPostfilterAreaBoxEntity", + DEBUGCHANNEL_GAME, + "m_pHelper" + ); } } else if (auto s_FogBoxEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::FogBoxEntity] )) { if (EntityIDMatches(s_FogBoxEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZFogBoxEntity", DEBUGCHANNEL_GAME, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZFogBoxEntity", + DEBUGCHANNEL_GAME, + "m_pHelper" + ); } } else if (auto s_BoxVolumeEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::BoxVolumeEntity] )) { if (EntityIDMatches(s_BoxVolumeEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZBoxVolumeEntity", DEBUGCHANNEL_ENGINE, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZBoxVolumeEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelper" + ); } } else if (auto s_SphereVolumeEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::SphereVolumeEntity] )) { if (EntityIDMatches(s_SphereVolumeEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSphereVolumeEntity", DEBUGCHANNEL_ENGINE, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSphereVolumeEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelper" + ); } } else if (auto s_VolumeShapeEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::VolumeShapeEntity] )) { if (EntityIDMatches(s_VolumeShapeEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZVolumeShapeEntity", DEBUGCHANNEL_ENGINE, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZVolumeShapeEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelper" + ); } } else if (auto s_CameraEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::CameraEntity] )) { if (EntityIDMatches(s_CameraEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZCameraEntity", DEBUGCHANNEL_ENGINE, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZCameraEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelper" + ); } } else if (auto s_OrientationEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::OrientationEntity] )) { if (EntityIDMatches(s_OrientationEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZOrientationEntity", DEBUGCHANNEL_ENGINE, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZOrientationEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelper" + ); } } else if (auto s_ScatterContainerEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::ScatterContainerEntity] )) { if (EntityIDMatches(s_ScatterContainerEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZScatterContainerEntity", DEBUGCHANNEL_ENGINE, "m_pHelperGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZScatterContainerEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelperGizmo" + ); } } else if (auto s_TrailShapeEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::TrailShapeEntity] )) { if (EntityIDMatches(s_TrailShapeEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZTrailShapeEntity", DEBUGCHANNEL_ENGINE, "m_pHelper"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZTrailShapeEntity", + DEBUGCHANNEL_ENGINE, + "m_pHelper" + ); } } else if (auto s_SplineEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::SplineEntity] )) { if (EntityIDMatches(s_SplineEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSplineEntity", DEBUGCHANNEL_ENGINE, "m_pSplineGizmo"); - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSplineEntity", DEBUGCHANNEL_ENGINE, "m_pMarkerGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSplineEntity", + DEBUGCHANNEL_ENGINE, + "m_pSplineGizmo" + ); + + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSplineEntity", + DEBUGCHANNEL_ENGINE, + "m_pMarkerGizmo" + ); } } else if (auto s_SplineControlPointEntity = p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::SplineControlPointEntity] )) { if (EntityIDMatches(s_SplineControlPointEntity, p_EntityTreeNode->EntityId)) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZSplineControlPointEntity", DEBUGCHANNEL_ENGINE, "m_pControlPointGizmo"); + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZSplineControlPointEntity", + DEBUGCHANNEL_ENGINE, + "m_pControlPointGizmo" + ); + } + } + else if (auto s_SequenceEntity = p_EntityTreeNode->Entity.QueryInterface( + m_DebugEntityTypeIds[DebugEntityTypeName::SequenceEntity] + )) { + if (EntityIDMatches(s_SequenceEntity, p_EntityTreeNode->EntityId)) { + AddDebugEntity( + p_EntityTreeNode->Entity, + "ZSequenceEntity", + DEBUGCHANNEL_ENGINE + ); } } else if (p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::AudioEmitterSpatialAspect] )) { - const bool isComposite = p_EntityTreeNode->Entity.QueryInterface(s_CompositeEntityTypeID); + const bool s_IsCompositeEntity = p_EntityTreeNode->Entity.QueryInterface( + s_CompositeEntityTypeID + ); - if (!isComposite) { + if (!s_IsCompositeEntity) { AddGizmoEntity( p_EntityTreeNode->Entity, "ZAudioEmitterSpatialAspect", @@ -1344,19 +1942,44 @@ void Editor::GetDebugEntities(const std::shared_ptr& p_EntityTre else if (p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::AudioEmitterVolumetricAspect] )) { - const bool isComposite = p_EntityTreeNode->Entity.QueryInterface(s_CompositeEntityTypeID); + const bool s_IsCompositeEntity = p_EntityTreeNode->Entity.QueryInterface( + s_CompositeEntityTypeID + ); - if (!isComposite) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZAudioEmitterVolumetricAspect", DEBUGCHANNEL_SOUND, "m_pHelper"); + if (!s_IsCompositeEntity) { + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZAudioEmitterVolumetricAspect", + DEBUGCHANNEL_SOUND, + "m_pHelper" + ); + } + } + else if (auto s_SoundGateEntity = p_EntityTreeNode->Entity.QueryInterface( + m_DebugEntityTypeIds[DebugEntityTypeName::SoundGateEntity] + )) { + if (EntityIDMatches(s_SoundGateEntity, p_EntityTreeNode->EntityId)) { + AddDebugEntity( + p_EntityTreeNode->Entity, + "ZSoundGateEntity", + DEBUGCHANNEL_SOUND + ); } } else if (p_EntityTreeNode->Entity.QueryInterface( m_DebugEntityTypeIds[DebugEntityTypeName::ClothWireEntity] )) { - const bool isComposite = p_EntityTreeNode->Entity.QueryInterface(s_CompositeEntityTypeID); + const bool s_IsCompositeEntity = p_EntityTreeNode->Entity.QueryInterface( + s_CompositeEntityTypeID + ); - if (!isComposite) { - AddGizmoEntity(p_EntityTreeNode->Entity, "ZClothWireEntity", DEBUGCHANNEL_CLOTH, "m_pHelper"); + if (!s_IsCompositeEntity) { + AddGizmoEntity( + p_EntityTreeNode->Entity, + "ZClothWireEntity", + DEBUGCHANNEL_CLOTH, + "m_pHelper" + ); } } diff --git a/Mods/Editor/Src/Editor.h b/Mods/Editor/Src/Editor.h index e504a589f..3d4d8bad7 100644 --- a/Mods/Editor/Src/Editor.h +++ b/Mods/Editor/Src/Editor.h @@ -14,6 +14,8 @@ #include "Glacier/EDebugChannel.h" #include "Glacier/ZCurve.h" #include "Glacier/ZAction.h" +#include "Glacier/ZSound.h" +#include "Glacier/ZSequence.h" #include "ImGuizmo.h" #include "EditorServer.h" @@ -328,6 +330,8 @@ class Editor : public IPluginInterface { void DrawDebugEntities(IRenderer* p_Renderer); void DrawGizmo(GizmoEntity& p_GizmoEntity, IRenderer* p_Renderer); void DrawShapes(const DebugEntity& p_DebugEntity, IRenderer* p_Renderer); + void DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Renderer); + void DrawShapes(ZSequenceEntity* p_SequenceEntity, IRenderer* p_Renderer); void GetDebugEntities(const std::shared_ptr& p_EntityTreeNode); void AddDebugEntity( const ZEntityRef p_EntityRef, @@ -355,6 +359,41 @@ class Editor : public IPluginInterface { static bool EntityIDMatches(void* p_Interface, const uint64 p_EntityID); bool RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Direction); + void DrawTrajectoryTrack( + IRenderer* p_Renderer, + ZTrajectoryTrackBase* p_Track, + const SVector4& p_Color, + ZEntityRef p_TargetEntity + ); + template + void DrawTrajectoryItem( + IRenderer* p_Renderer, + TItem* p_Item, + ZTrajectoryTrackBase* p_Track, + const SVector4& p_Color, + ZEntityRef p_TargetEntity, + EvaluateFn&& p_EvaluateFn + ); + static SVector3 EvaluateTrajectoryWorldPosition( + ZMorphemeTrajectorySource* p_Item, + ZTrajectoryTrackBase* p_Track, + ZGameTime p_ItemTime, + ZAnimationResource* p_AnimationResource, + ZEntityRef p_TargetEntity + ); + static SVector3 EvaluateTrajectoryWorldPosition( + ZEulerAngleTrajectorySource* p_Item, + ZTrajectoryTrackBase* p_Track, + ZGameTime p_ItemTime, + ZEntityRef p_TargetEntity + ); + static SVector3 EvaluateTrajectoryWorldPosition( + ZBezierSplineTrajectorySource* p_Item, + ZTrajectoryTrackBase* p_Track, + ZGameTime p_ItemTime, + ZEntityRef p_TargetEntity + ); + static bool IsActorTarget(ZActor* p_Actor); void ProcessTasks(); @@ -481,6 +520,8 @@ class Editor : public IPluginInterface { AudioEmitterSpatialAspect, AudioEmitterVolumetricAspect, ClothWireEntity, + SoundGateEntity, + SequenceEntity, Count }; @@ -633,6 +674,8 @@ class Editor : public IPluginInterface { bool m_DrawPushDebug = false; bool m_DrawSafeZones = true; + bool m_ShowSoundOcclusion = false; + ZEntityRef m_EditorData {}; TEntityRef m_EditorCamera {}; TEntityRef m_EditorCameraRT {}; diff --git a/ZHMModSDK/Include/Functions.h b/ZHMModSDK/Include/Functions.h index a0eabb45a..4a4c37daa 100644 --- a/ZHMModSDK/Include/Functions.h +++ b/ZHMModSDK/Include/Functions.h @@ -56,6 +56,10 @@ class ZHM5WeaponControl; class ZGameKeywordManager; class ZFreeCameraControlEditorStyleEntity; class ICameraEntity; +class ZEulerAngleTrajectorySource; +class ZMorphemeTrajectorySource; +class ZBezierSplineTrajectorySource; +class ZGameTime; namespace bfx { class AreaHandle; @@ -319,4 +323,14 @@ class ZHMSDK_API Functions { static EngineFunction* ZFreeCameraControlEditorStyleEntity_ZoomCamera; + + static EngineFunction* ZEulerAngleTrajectorySource_GetTrajectoryAtTime; + static EngineFunction* ZMorphemeTrajectorySource_GetTrajectoryAtTime; + static EngineFunction* ZBezierSplineTrajectorySource_GetTrajectoryAtTime; }; \ No newline at end of file diff --git a/ZHMModSDK/Include/Glacier/IBoolConditionListener.h b/ZHMModSDK/Include/Glacier/IBoolConditionListener.h new file mode 100644 index 000000000..e2933d08e --- /dev/null +++ b/ZHMModSDK/Include/Glacier/IBoolConditionListener.h @@ -0,0 +1,6 @@ +#pragma once + +#include "IComponentInterface.h" + +class IBoolConditionListener : public IComponentInterface { +}; \ No newline at end of file diff --git a/ZHMModSDK/Include/Glacier/SColorRGBA.h b/ZHMModSDK/Include/Glacier/SColorRGBA.h index 29d238592..b74d26905 100644 --- a/ZHMModSDK/Include/Glacier/SColorRGBA.h +++ b/ZHMModSDK/Include/Glacier/SColorRGBA.h @@ -5,10 +5,10 @@ struct SColorRGBA { uint32_t GetAsUInt32() const { - const uint32_t r = std::clamp(static_cast(this->r * 256.0f), 0, 255); - const uint32_t g = std::clamp(static_cast(this->g * 256.0f), 0, 255); - const uint32_t b = std::clamp(static_cast(this->b * 256.0f), 0, 255); - const uint32_t a = std::clamp(static_cast(this->a * 256.0f), 0, 255); + const uint32_t r = std::clamp(static_cast(this->r * 256.f), 0, 255); + const uint32_t g = std::clamp(static_cast(this->g * 256.f), 0, 255); + const uint32_t b = std::clamp(static_cast(this->b * 256.f), 0, 255); + const uint32_t a = std::clamp(static_cast(this->a * 256.f), 0, 255); return (a << 24) | (b << 16) | (g << 8) | r; // 0xAABBGGRR } diff --git a/ZHMModSDK/Include/Glacier/TSharedValue.h b/ZHMModSDK/Include/Glacier/TSharedValue.h new file mode 100644 index 000000000..8837705ad --- /dev/null +++ b/ZHMModSDK/Include/Glacier/TSharedValue.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +template +struct SSharedValueContainer { + uint32_t m_nRefCount; + T m_value; +}; + +template +class TSharedValue { +public: + SSharedValueContainer* m_pValue; +}; \ No newline at end of file diff --git a/ZHMModSDK/Include/Glacier/ZCircleGenerator.h b/ZHMModSDK/Include/Glacier/ZCircleGenerator.h new file mode 100644 index 000000000..3c17f353f --- /dev/null +++ b/ZHMModSDK/Include/Glacier/ZCircleGenerator.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#include "ZMath.h" + +class ZCircleGenerator { +public: + void Init( + const SVector3& p_Center, + const SVector3& p_Size, + const SVector3& p_Normal, + const SVector3& p_StartDir, + float p_Angle, + float fStepsToACircle + ) { + m_vCenter = p_Center; + + m_vXAxis = p_StartDir * p_Size.x; + + SVector3 s_Cross = SVector3::CrossProduct(p_Normal, p_StartDir); + m_vYAxis = s_Cross * p_Size.y; + + float4 s_initialPosition = m_vCenter + m_vXAxis; + m_v1 = s_initialPosition; + m_v2 = s_initialPosition; + + m_vX.m4 = float4(1.f); + m_vY.m4 = float4(0.f); + + m_nStep = 0; + const float s_NumStepsFloat = std::abs(p_Angle * fStepsToACircle / (2.0f * std::numbers::pi_v)); + const int32_t s_NumSteps = static_cast(s_NumStepsFloat); + m_nNumSteps = s_NumSteps > 2 ? s_NumSteps : 3; + + float s_DeltaTheta = p_Angle / static_cast(m_nNumSteps); + float s_CosT = std::cos(s_DeltaTheta); + float s_SinT = std::sin(s_DeltaTheta); + + m_vRow0 = float4(s_CosT, -s_SinT, 0.f, 0.f); + m_vRow1 = float4(s_SinT, s_CosT, 0.f, 0.f); + } + + bool MoveNext() { + if (m_nStep == m_nNumSteps) { + return false; + } + + m_v1 = m_v2; + m_nStep++; + + float s_X = m_vX.m4.x; + float s_Y = m_vY.m4.x; + + float s_NewX = s_X * m_vRow0.x + s_Y * m_vRow0.y; + float s_NewY = s_X * m_vRow1.x + s_Y * m_vRow1.y; + + m_vX.m4 = float4(s_NewX); + m_vY.m4 = float4(s_NewY); + + m_v2 = m_vCenter + (m_vXAxis * s_NewX) + (m_vYAxis * s_NewY); + + return true; + } + + float4 m_v1; + float4 m_v2; + + float4 m_vCenter; + + uint32_t m_nStep; + uint32_t m_nNumSteps; + + float4 m_vRow0; + float4 m_vRow1; + + float1 m_vX; + float1 m_vY; + + float4 m_vXAxis; + float4 m_vYAxis; +}; \ No newline at end of file diff --git a/ZHMModSDK/Include/Glacier/ZColor.h b/ZHMModSDK/Include/Glacier/ZColor.h index 62f933821..6f4ea11dc 100644 --- a/ZHMModSDK/Include/Glacier/ZColor.h +++ b/ZHMModSDK/Include/Glacier/ZColor.h @@ -4,18 +4,17 @@ class ZColor { public: - static SVector4 UnpackUnsigned(const unsigned int lPackedVec4) { - const unsigned int alpha = (lPackedVec4 >> 24) & 0xFF; - const unsigned int blue = (lPackedVec4 >> 16) & 0xFF; - const unsigned int green = (lPackedVec4 >> 8) & 0xFF; - const unsigned int red = lPackedVec4 & 0xFF; - SVector4 result; + static SVector4 UnpackUnsigned(const uint32 p_PackedVector4) { + const uint32 s_Alpha = (p_PackedVector4 >> 24) & 0xFF; + const uint32 s_Blue = (p_PackedVector4 >> 16) & 0xFF; + const uint32 s_Green = (p_PackedVector4 >> 8) & 0xFF; + const uint32 s_Red = p_PackedVector4 & 0xFF; - result.x = static_cast(red) / 255.0f; - result.y = static_cast(green) / 255.0f; - result.z = static_cast(blue) / 255.0f; - result.w = static_cast(alpha) / 255.0f; - - return result; + return SVector4( + static_cast(s_Red) / 255.f, + static_cast(s_Green) / 255.f, + static_cast(s_Blue) / 255.f, + static_cast(s_Alpha) / 255.f + ); } }; \ No newline at end of file diff --git a/ZHMModSDK/Include/Glacier/ZItem.h b/ZHMModSDK/Include/Glacier/ZItem.h index 20ec08c89..08acf15ec 100644 --- a/ZHMModSDK/Include/Glacier/ZItem.h +++ b/ZHMModSDK/Include/Glacier/ZItem.h @@ -5,6 +5,7 @@ #include "ZGeomEntity.h" #include "ZHitman5.h" #include "ZGameTime.h" +#include "IBoolConditionListener.h" class IItemBase : public IComponentInterface {}; @@ -19,8 +20,6 @@ class ZUIDataProvider { PAD(0x40); }; -class IBoolConditionListener : public IComponentInterface {}; - class IColliderController : public IComponentInterface {}; class ZHM5Action; diff --git a/ZHMModSDK/Include/Glacier/ZMath.h b/ZHMModSDK/Include/Glacier/ZMath.h index 4d3e9cf2f..b0a38bf5d 100644 --- a/ZHMModSDK/Include/Glacier/ZMath.h +++ b/ZHMModSDK/Include/Glacier/ZMath.h @@ -287,7 +287,7 @@ struct alignas(16) float4 { return float4(-x, -y, -z, -w); } - static float4 CrossProduct(float4& v1, float4& v2) { + static float4 CrossProduct(const float4& v1, const float4& v2) { return _mm_sub_ps( _mm_mul_ps( _mm_shuffle_ps(v1.m, v1.m, _MM_SHUFFLE(3, 0, 2, 1)), @@ -338,6 +338,10 @@ struct alignas(16) float4 { inline float Length() const { return sqrtf(x * x + y * y + z * z + w * w); } + + inline float Length3() const { + return sqrtf(x * x + y * y + z * z); + } inline float4 Normalized() const { const float s_LengthSq = DotProduct(*this, *this); @@ -366,6 +370,10 @@ struct alignas(16) float4 { }; }; +struct float1 { + float4 m4; +}; + struct SQuaternion { float4 w128; }; diff --git a/ZHMModSDK/Include/Glacier/ZResource.h b/ZHMModSDK/Include/Glacier/ZResource.h index 8ee758ddb..9adf36e6b 100644 --- a/ZHMModSDK/Include/Glacier/ZResource.h +++ b/ZHMModSDK/Include/Glacier/ZResource.h @@ -270,7 +270,7 @@ class ZResourceDataBuffer : public ZSharedPointerTarget { bool m_bOwnsDataPtr = false; }; -typedef TSharedPointer ZResourceDataPtr; +using ZResourceDataPtr = TSharedPointer; class ZResourceReader : public ZSharedPointerTarget { public: @@ -283,7 +283,7 @@ class ZResourceReader : public ZSharedPointerTarget { static_assert(sizeof(ZResourceReader) == 88); -typedef TSharedPointer ZResourceReaderPtr; +using ZResourceReaderPtr = TSharedPointer; class ZResourcePending { public: diff --git a/ZHMModSDK/Include/Glacier/ZSequence.h b/ZHMModSDK/Include/Glacier/ZSequence.h new file mode 100644 index 000000000..487b55d1b --- /dev/null +++ b/ZHMModSDK/Include/Glacier/ZSequence.h @@ -0,0 +1,231 @@ +#pragma once + +#include "ZEntity.h" +#include "Enums.h" +#include "TSharedValue.h" +#include "ZCurve.h" +#include "ZGameTime.h" +#include "ZMath.h" +#include "ZResource.h" +#include "IBoolConditionListener.h" + +class ZSequenceEntity; +struct STrajectorySpeed; +class IEntityGroup; +class IBoolCondition; +class ZSplineEntity; + +class ISequenceItem : public IComponentInterface { +public: + virtual ~ISequenceItem() = 0; +}; + +class ISequenceValueSource : public IComponentInterface { +public: + virtual ~ISequenceValueSource() = 0; +}; + +class ITrajectorySequenceItem : public IComponentInterface { +public: + virtual ~ITrajectorySequenceItem() = 0; +}; + +class ISequenceTrack : public IComponentInterface { +public: + virtual ~ISequenceTrack() = 0; +}; + +class ITrajectoryTrack : public IComponentInterface { +public: + virtual ~ITrajectoryTrack() = 0; + virtual bool GetStartWorldTransform(SMatrix& matrix) = 0; + virtual ECoordinateSpace GetCoordinateSpaceOfFirstItem() = 0; +}; + +class ZSequenceItemBase : public ZEntityImpl, public ISequenceItem { +public: + ZEntityRef m_pTrack; + ZGameTime m_startTime; + ZGameTime m_duration; + bool m_triggerIfSpanning; + ZEntityRef m_pNextItem; + ZEntityRef m_pPreviousItem; +}; + +class ZEulerAngleTrajectorySource : + public ZSequenceItemBase, + public ISequenceValueSource, + public ITrajectorySequenceItem { +public: + ECoordinateSpace m_eCoordinateSpace; + TSharedValue m_CurveX; + TSharedValue m_CurveY; + TSharedValue m_CurveZ; + TSharedValue m_CurveRotX; + TSharedValue m_CurveRotY; + TSharedValue m_CurveRotZ; +}; + +class ZSequenceTrackBase : public ZEntityImpl, public ISequenceTrack { +public: + class UpdateHelperData; + + virtual void ZSequenceTrackBase_Unk20() = 0; + virtual void ZSequenceTrackBase_Unk21() = 0; + virtual void ZSequenceTrackBase_Unk22() = 0; + virtual void ZSequenceTrackBase_Unk23() = 0; + virtual void ZSequenceTrackBase_Unk24() = 0; + + TArray> m_aItems; + TEntityRef m_pSequence; + UpdateHelperData* m_helperData; +}; + +class ZEntityTrackBase : public ZSequenceTrackBase { +public: + virtual void ZEntityTrackBase_Unk25() = 0; + + TEntityRef m_entityGroup; +}; + +class ZTrajectoryTrackBase : public ZEntityTrackBase, public ITrajectoryTrack { +public: + virtual void ZTrajectoryTrackBase_Unk26() = 0; + virtual SMatrix* GetTargetObjectToParentMatrix(SMatrix& result) = 0; + virtual void SetTargetObjectToParentMatrix(const SMatrix& mObjectToParent) = 0; + virtual SMatrix* GetTargetObjectToWorldMatrix(SMatrix& result) = 0; + virtual void SetTargetObjectToWorldMatrix(const SMatrix& mObjectToWorld, TEntityRef pItem, ZGameTime newItemTime) = 0; + + bool m_bRestoreValueAfterEnd; + float32 m_fBlendInTime; + SMatrix m_OriginalValue; + SMatrix m_targetStartTransform; + bool m_bSequenceOriginWarningSent; + STrajectorySpeed* m_pSpeedInfo; +}; + +class ZTrajectoryTrack : public ZTrajectoryTrackBase { +}; + +class ZAnimationResource; + +class ZMorphemeAnimSourceBase : public ZSequenceItemBase, public ISequenceValueSource { +public: + TResourcePtr m_pAnimationResource; + ZGameTime m_startAnimationTime; + ZGameTime m_SkipFromEnd; + bool m_bCrop; + bool m_bLoop; + bool m_triggerIfSpanningOverride; +}; + +class ZMorphemeTrajectorySource : public ZMorphemeAnimSourceBase, public ITrajectorySequenceItem { +public: + ECoordinateSpace m_eCoordinateSpace; + float32 m_fBlendOut; +}; + +class ZMorphemeTrajectoryTrack : public ZTrajectoryTrackBase { + bool m_bIsCollidedTransformStored; + SMatrix m_mSequenceEndTransform; + SMatrix m_mDeltaTransform; + bool m_bRunAIInGaps; +}; + +namespace NMP { + class DataBuffer; + class Vector3; + class Quat; +} + +namespace MR { + class AnimScalingFacial; + class TrajectoryControlBase; + class ILowLevelAnimSource; + class RigToAnimMap; + class EventSequenceHeader; + class Rig; + + class IAnimSource { + public: + virtual ~IAnimSource() = 0; + virtual void computeAtTime(float, MR::AnimScalingFacial*, NMP::DataBuffer*) = 0; + virtual void computeAtTimeSingleTransform(float, unsigned int, NMP::Vector3*, NMP::Quat*) = 0; + virtual float getDuration() = 0; + virtual float getSampleFrequency() = 0; + virtual const MR::TrajectoryControlBase* getTrajectoryChannelData() = 0; + virtual void locate() = 0; + virtual void dislocate() = 0; + virtual bool CanRetarget() = 0; + }; +} + +class ZFoVAnimation; +class ZAnimatedBoneScales; + +class ZAnimationResource { +public: + ZRuntimeResourceID m_ridResource; + SMatrix43 m_sequenceOrigin; + ZFoVAnimation* m_pFoVAnimation; + ZAnimatedBoneScales* m_pAnimatedBoneScales; + MR::IAnimSource* m_pAnimationSource; + MR::RigToAnimMap* m_pRigToAnimMap; + MR::EventSequenceHeader* m_pEventSequenceHeaderAMD; + MR::Rig* m_pRig; +}; + +class ZSequenceEntity : + public ZEntityImpl, + public IBoolConditionListener { +public: + PAD(0x60); + TArray m_aTracksAndGroups; // 0x80 + ZGameTime m_sequenceTime; // 0x98 + ZGameTime m_duration; // 0xA0 + ZGameTime m_startTime; // 0xA8 + bool m_bUpdateOnGameTime; // 0xB0 + PAD(0xF); + bool m_bSaveState; // 0xC0 + TEntityRef m_sequenceOrigin; // 0xC8 + TEntityRef m_rUpdateCondition; // 0xD8 + int32 m_nLoopCount; // 0x140 + bool m_bPausedOnStart; // 0x152 + bool m_bLetterbox; // 0x170 + ZGameTime m_letterboxFadeIn; // 0x178 + ZGameTime m_letterboxFadeOut; // 0x180 + ZSequenceEntity_ELetterBoxAspect m_eLetterBoxAspect; // 0x188 + bool m_bFadeOutBeforeEnd; // 0x18C + bool m_bCanBeInterrupted; // 0x18D + bool m_bResetOnStop; // 0x18E + bool m_bRestoreAborted; // 0x18F + bool m_bRestoreFromStop; // 0x190 + EVrSequenceCameraMode m_eVrCameraMode; // 0x194 + bool m_bIsVrCameraTranslationAllowed; // 0x198 +}; + +class ISequenceTrackGroup : public IComponentInterface { +}; + +class IEntityGroup : public IComponentInterface { +}; + +class ZTrackGroupBase : public ZEntityImpl, public ISequenceTrackGroup { +public: + TArray m_aTracksAndGroups; + TEntityRef m_pSequence; +}; + +class ZEntityGroup : public ZTrackGroupBase, public IEntityGroup { +public: + ZEntityRef m_targetEntity; +}; + +class ZBezierSplineTrajectorySource : + public ZSequenceItemBase, + public ISequenceValueSource, + public ITrajectorySequenceItem { +public: + ECoordinateSpace m_eCoordinateSpace; // 0x60 + TEntityRef m_rSpline; // 0x68 +}; \ No newline at end of file diff --git a/ZHMModSDK/Include/Glacier/ZSound.h b/ZHMModSDK/Include/Glacier/ZSound.h new file mode 100644 index 000000000..7f77ee842 --- /dev/null +++ b/ZHMModSDK/Include/Glacier/ZSound.h @@ -0,0 +1,45 @@ +#pragma once + +#include "ZSpatialEntity.h" +#include "ZCurve.h" +#include "TSharedValue.h" + +class ISoundAmbienceElement; +class ISoundGateController; + +class ZSoundGateEntity : public ZBoundedEntity { +public: + bool m_bEnabled; // 0xB8 + bool m_bIsOpen; // 0xB9 + float32 m_fThicknessSource; // 0xBC + float32 m_fThicknessDestination; // 0xC0 + float32 m_fLateralMargin; // 0xC4 + float32 m_fLateralMarginLeft; // 0xC8 + float32 m_fLateralMarginRight; // 0xCC + float32 m_fVerticalMargin; // 0xD0 + float32 m_fVerticalMarginTop; // 0xD4 + float32 m_fVerticalMarginBottom; // 0xD8 + float32 m_fOcclusionRadius; // 0xDC + float32 m_fOcclusionMinRadius; // 0xE0 + float32 m_fOcclusionMaxDistance; // 0xE4 + float32 m_fOcclusionMinDistance; // 0xE8 + uint32 m_nOcclusionMaxAngleDeg; // 0xEC + float32 m_fDestinationDiffractionScaling; // 0xF0 + float32 m_fOpenOcclusionBlendDistance; // 0xF4 + float32 m_fProjectionBlendDistance; // 0xF8 + ESoundGateType m_GateType; // 0xFC + ESoundGateFlags m_GateFlags; // 0x100 + SVector2 m_vLocalSize; // 0x104 + float32 m_fOcclusionFactorOpen; // 0x10C + float32 m_fOcclusionFactorClosed; // 0x110 + float32 m_fOpenCloseChangeTime; // 0x114 + bool m_bIgnoreForProjection; // 0x118 + TEntityRef m_AmbienceSide0; // 0x120 + TEntityRef m_AmbienceSide1; // 0x130 + TSharedValue m_FadeOutCurve; // 0x140 + TSharedValue m_FadeInCurve; // 0x148 + TSharedValue m_OcclusionTransitionCurve; // 0x150 + TEntityRef m_rController; // 0x158 + float32 m_fReverbBlendDistance; // 0x168 + PAD(0xD4); // 0x16C +}; \ No newline at end of file diff --git a/ZHMModSDK/Include/IRenderer.h b/ZHMModSDK/Include/IRenderer.h index a51f9ef8f..6d70f7839 100644 --- a/ZHMModSDK/Include/IRenderer.h +++ b/ZHMModSDK/Include/IRenderer.h @@ -141,4 +141,11 @@ class IRenderer { virtual void SetMaxDrawDistance(float p_MaxDrawDistance) = 0; virtual float GetMaxDrawDistance() const = 0; + + virtual void DrawSphere3D( + const SMatrix& p_Transform, + const float4& p_Position, + const float4& p_Size, + const SVector4& p_Color + ) = 0; }; \ No newline at end of file diff --git a/ZHMModSDK/Src/Functions.cpp b/ZHMModSDK/Src/Functions.cpp index 26b965a1f..63fe16226 100644 --- a/ZHMModSDK/Src/Functions.cpp +++ b/ZHMModSDK/Src/Functions.cpp @@ -582,4 +582,25 @@ PATTERN_FUNCTION( "xxxxxxxxxxxxxx?xxxxx", ZFreeCameraControlEditorStyleEntity_ZoomCamera, void(ZFreeCameraControlEditorStyleEntity* th, float fZoom) +); + +PATTERN_FUNCTION( + "\x48\x8B\xC4\x48\x89\x58\x10\x57\x48\x81\xEC\x00\x00\x00\x00\x0F\x29\x70\xE8\x48\x8B\xD9", + "xxxxxxxxxxx????xxxxxxx", + ZEulerAngleTrajectorySource_GetTrajectoryAtTime, + SMatrix* (ZEulerAngleTrajectorySource* th, SMatrix* result, ZGameTime& animationTime) +); + +PATTERN_FUNCTION( + "\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x00\x8B\x41\x58", + "xxxxxxxxx?xxx", + ZMorphemeTrajectorySource_GetTrajectoryAtTime, + SMatrix* (ZMorphemeTrajectorySource* th, SMatrix* result, ZGameTime& animationTime) +); + +PATTERN_FUNCTION( + "\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x00\x48\x8B\x41\x18\x48\x8B\xD9", + "xxxxxxxxxxxxxx?xxxxxxx", + ZBezierSplineTrajectorySource_GetTrajectoryAtTime, + SMatrix* (ZBezierSplineTrajectorySource* th, SMatrix* result, ZGameTime& animationTime) ); \ No newline at end of file diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp index 3e1445a6e..d1ea88ed5 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp @@ -12,6 +12,8 @@ #include "Glacier/ZRender.h" #include "Glacier/MDF_FONT.h" #include "Glacier/ZCameraEntity.h" +#include "Glacier/ZCircleGenerator.h" +#include "Glacier/ZColor.h" #include "Hooks.h" #include "Logging.h" @@ -1090,20 +1092,27 @@ void DirectXTKRenderer::DrawBox3D( p_Center - s_Right + s_Up - s_Forward, p_Center + s_Right + s_Up - s_Forward, p_Center + s_Right - s_Up - s_Forward, - p_Center + s_Right + s_Up + s_Forward, - p_Center - s_Right + s_Up + s_Forward, p_Center - s_Right - s_Up + s_Forward, + p_Center - s_Right + s_Up + s_Forward, + p_Center + s_Right + s_Up + s_Forward, p_Center + s_Right - s_Up + s_Forward, }; - static constexpr int s_Edges[12][2] = { - {0, 1}, {1, 2}, {2, 3}, {3, 0}, // bottom face - {4, 5}, {5, 6}, {6, 7}, {7, 4}, // top face - {0, 6}, {1, 5}, {2, 4}, {3, 7} // vertical edges + static constexpr int s_Faces[12][3] = { + {0, 1, 2}, {0, 2, 3}, // bottom + {4, 6, 5}, {4, 7, 6}, // top + {0, 3, 7}, {0, 7, 4}, // front + {1, 5, 6}, {1, 6, 2}, // back + {0, 4, 5}, {0, 5, 1}, // left + {3, 2, 6}, {3, 6, 7} // right }; - for (const auto& s_Edge : s_Edges) { - DrawLine3D(s_Corners[s_Edge[0]], s_Corners[s_Edge[1]], p_Color, p_Color); + for (const auto& s_Face : s_Faces) { + DrawTriangle3D( + s_Corners[s_Face[0]], p_Color, + s_Corners[s_Face[1]], p_Color, + s_Corners[s_Face[2]], p_Color + ); } } @@ -1124,27 +1133,20 @@ void DirectXTKRenderer::DrawBoxWire3D( p_Center - s_Right + s_Up - s_Forward, p_Center + s_Right + s_Up - s_Forward, p_Center + s_Right - s_Up - s_Forward, - p_Center - s_Right - s_Up + s_Forward, - p_Center - s_Right + s_Up + s_Forward, p_Center + s_Right + s_Up + s_Forward, + p_Center - s_Right + s_Up + s_Forward, + p_Center - s_Right - s_Up + s_Forward, p_Center + s_Right - s_Up + s_Forward, }; - static constexpr int s_Faces[12][3] = { - {0, 1, 2}, {0, 2, 3}, // bottom - {4, 6, 5}, {4, 7, 6}, // top - {0, 3, 7}, {0, 7, 4}, // front - {1, 5, 6}, {1, 6, 2}, // back - {0, 4, 5}, {0, 5, 1}, // left - {3, 2, 6}, {3, 6, 7} // right + static constexpr int s_Edges[12][2] = { + {0, 1}, {1, 2}, {2, 3}, {3, 0}, // bottom face + {4, 5}, {5, 6}, {6, 7}, {7, 4}, // top face + {0, 6}, {1, 5}, {2, 4}, {3, 7} // vertical edges }; - for (const auto& s_Face : s_Faces) { - DrawTriangle3D( - s_Corners[s_Face[0]], p_Color, - s_Corners[s_Face[1]], p_Color, - s_Corners[s_Face[2]], p_Color - ); + for (const auto& s_Edge : s_Edges) { + DrawLine3D(s_Corners[s_Edge[0]], s_Corners[s_Edge[1]], p_Color, p_Color); } } @@ -1678,6 +1680,188 @@ float DirectXTKRenderer::GetMaxDrawDistance() const { return m_ViewFrustum.GetMaxDrawDistance(); } +void DirectXTKRenderer::DrawSphere3D( + const SMatrix& p_Transform, + const float4& p_Position, + const float4& p_Size, + const SVector4& p_Color +) { + SMatrix s_Transform = p_Transform; + s_Transform.Trans = p_Position; + s_Transform.Trans.w = 1.f; + + const SColorRGBA s_Color { + p_Color.x, + p_Color.y, + p_Color.z, + p_Color.w + }; + + DrawSphere3DOutlinedShaded(s_Transform, p_Size.x, s_Color.GetAsUInt32(), 48.f, false); +} + +void DirectXTKRenderer::DrawSphere3DOutlinedShaded( + const SMatrix& p_Transform, + float p_Size, + uint32_t p_Color, + float p_StepsToACircle, + bool p_IsOrthographic +) { + const auto s_CurrentCamera = Functions::GetCurrentCamera->Call(); + + if (!s_CurrentCamera) { + return; + } + + constexpr float s_TwoPi = 6.2831855f; + + const uint32_t s_OutlineColor = (p_Color & 0x00FFFFFF) | 0xBB000000; + const uint32_t s_ShadeColor = (p_Color & 0x00FFFFFF) | 0x64000000; + + const SMatrix s_CameraTransform = s_CurrentCamera->GetObjectToWorldMatrix(); + + const float4 s_CameraPosition = s_CameraTransform.Pos; + const float4 s_CameraForward = -s_CameraTransform.Backward; + const float4 s_SphereCenter = p_Transform.Trans; + + if (!p_IsOrthographic) { + const float4 s_ToCamera = s_CameraPosition - s_SphereCenter; + const float s_Distance = s_ToCamera.Length3(); + + if (s_Distance > p_Size) { + const float4 s_ToCameraNormalized = s_ToCamera / s_Distance; + + const float s_OutlineOffset = (p_Size * p_Size) / s_Distance; + const float s_OutlineRadius = std::sqrt( + p_Size * p_Size - s_OutlineOffset * s_OutlineOffset + ); + + const float4 s_OutlineCenter = + s_SphereCenter + s_ToCameraNormalized * s_OutlineOffset; + + DrawArc( + s_OutlineCenter, + s_OutlineRadius, + -s_ToCameraNormalized, + s_CameraTransform.YAxis, + s_OutlineColor, + s_TwoPi, + p_StepsToACircle + ); + } + } + + const float4 s_Axes[] = { + p_Transform.XAxis, + p_Transform.YAxis, + p_Transform.ZAxis + }; + + for (size_t i = 0; i < 3; ++i) { + const float4& s_Normal = s_Axes[i]; + + float4 s_StartDirection = float4::CrossProduct( + s_Normal, + s_CameraForward + ); + + if (std::abs(s_StartDirection.x) <= 0.0001f && + std::abs(s_StartDirection.y) <= 0.0001f && + std::abs(s_StartDirection.z) <= 0.0001f) { + s_StartDirection = float4::CrossProduct( + s_Axes[(i + 1) % 3], + s_Normal + ); + } + + s_StartDirection.Normalize(); + + DrawArcShaded( + s_SphereCenter, + p_Size, + s_Normal, + s_StartDirection, + p_Color, + s_ShadeColor, + s_TwoPi, + p_StepsToACircle + ); + } +} + +void DirectXTKRenderer::DrawArc( + const float4& p_Center, + float p_Size, + const float4& p_Normal, + const float4& p_StartDirection, + uint32_t p_Color, + float p_Angle, + float p_StepsToACircle +) { + const float4 s_Size(p_Size); + + ZCircleGenerator s_CircleGenerator; + s_CircleGenerator.Init( + p_Center, + s_Size, + p_Normal, + p_StartDirection, + p_Angle, + p_StepsToACircle + ); + + const SVector4 s_Color = ZColor::UnpackUnsigned(p_Color); + + while (s_CircleGenerator.MoveNext()) { + DrawLine3D( + s_CircleGenerator.m_v1, + s_CircleGenerator.m_v2, + s_Color, + s_Color + ); + } +} + +void DirectXTKRenderer::DrawArcShaded( + const float4& p_Center, + float p_Size, + const float4& p_Normal, + const float4& p_StartDirection, + uint32_t p_Color, + uint32_t p_ShadeColor, + float p_Angle, + float p_StepsToACircle +) { + const auto s_CurrentCamera = Functions::GetCurrentCamera->Call(); + + if (!s_CurrentCamera) { + return; + } + + const float4 s_Size(p_Size); + + ZCircleGenerator s_CircleGenerator; + s_CircleGenerator.Init(p_Center, s_Size, p_Normal, p_StartDirection, p_Angle, p_StepsToACircle); + + const float4 s_CameraPosition = s_CurrentCamera->GetObjectToWorldMatrix().Pos; + + while (s_CircleGenerator.MoveNext()) { + const float4& s_Start = s_CircleGenerator.m_v1; + const float4& s_End = s_CircleGenerator.m_v2; + + const float4 s_Direction = s_End - s_Start; + + const float s_Facing = float4::DotProduct( + float4::CrossProduct(p_Normal, s_Direction), + s_CameraPosition - s_Start + ); + + const SVector4 s_Color = ZColor::UnpackUnsigned(s_Facing >= 0.f ? p_Color : p_ShadeColor); + + DrawLine3D(s_Start, s_End, s_Color, s_Color); + } +} + AABB DirectXTKRenderer::TransformAABB(const DirectX::SimpleMath::Matrix& p_Transform, const AABB& p_AABB) { const DirectX::SimpleMath::Vector3 s_Corners[8] = { {p_AABB.min.x, p_AABB.min.y, p_AABB.min.z}, diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h index 66a2a8dd4..faaea9050 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h @@ -200,6 +200,42 @@ namespace Rendering::Renderers { void SetMaxDrawDistance(float p_MaxDrawDistance) override; float GetMaxDrawDistance() const override; + void DrawSphere3D( + const SMatrix& p_Transform, + const float4& p_Position, + const float4& p_Size, + const SVector4& p_Color + ) override; + + void DrawSphere3DOutlinedShaded( + const SMatrix& p_Transform, + float p_Size, + uint32_t p_Color, + float p_StepsToACircle, + bool p_IsOrthographic + ); + + void DrawArc( + const float4& p_Center, + float p_Size, + const float4& p_Normal, + const float4& p_StartDirection, + uint32_t p_Color, + float p_Angle, + float p_StepsToACircle + ); + + void DrawArcShaded( + const float4& p_Center, + float p_Size, + const float4& p_Normal, + const float4& p_StartDirection, + uint32_t p_Color, + uint32_t p_ShadeColor, + float p_Angle, + float p_StepsToACircle + ); + static AABB TransformAABB(const DirectX::SimpleMath::Matrix& p_Transform, const AABB& p_AABB); private: From fc7731573fe9dd94b76440f97dbefce323969fd0 Mon Sep 17 00:00:00 2001 From: Pavle Date: Fri, 21 Aug 2026 09:56:27 +0200 Subject: [PATCH 02/10] Fix view frustum culling --- .../Rendering/Renderers/DirectXTKRenderer.cpp | 34 +++- ZHMModSDK/Src/Rendering/ViewFrustum.cpp | 186 +++++++++--------- ZHMModSDK/Src/Rendering/ViewFrustum.h | 20 +- 3 files changed, 133 insertions(+), 107 deletions(-) diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp index d1ea88ed5..816f228ef 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp @@ -1058,6 +1058,9 @@ void DirectXTKRenderer::DrawBox3D(const SVector3& p_Min, const SVector3& p_Max, SVector3(p_Max.x, p_Min.y, p_Max.z), }; + const bool s_WasFrustumCullingEnabled = m_IsFrustumCullingEnabled; + m_IsFrustumCullingEnabled = false; + DrawLine3D(s_Corners[0], s_Corners[1], p_Color, p_Color); DrawLine3D(s_Corners[1], s_Corners[2], p_Color, p_Color); DrawLine3D(s_Corners[2], s_Corners[3], p_Color, p_Color); @@ -1073,6 +1076,8 @@ void DirectXTKRenderer::DrawBox3D(const SVector3& p_Min, const SVector3& p_Max, DrawLine3D(s_Corners[2], s_Corners[4], p_Color, p_Color); DrawLine3D(s_Corners[3], s_Corners[7], p_Color, p_Color); + + m_IsFrustumCullingEnabled = s_WasFrustumCullingEnabled; } void DirectXTKRenderer::DrawBox3D( @@ -1107,6 +1112,9 @@ void DirectXTKRenderer::DrawBox3D( {3, 2, 6}, {3, 6, 7} // right }; + const bool s_WasFrustumCullingEnabled = m_IsFrustumCullingEnabled; + m_IsFrustumCullingEnabled = false; + for (const auto& s_Face : s_Faces) { DrawTriangle3D( s_Corners[s_Face[0]], p_Color, @@ -1114,6 +1122,8 @@ void DirectXTKRenderer::DrawBox3D( s_Corners[s_Face[2]], p_Color ); } + + m_IsFrustumCullingEnabled = s_WasFrustumCullingEnabled; } void DirectXTKRenderer::DrawBoxWire3D( @@ -1145,9 +1155,14 @@ void DirectXTKRenderer::DrawBoxWire3D( {0, 6}, {1, 5}, {2, 4}, {3, 7} // vertical edges }; + const bool s_WasFrustumCullingEnabled = m_IsFrustumCullingEnabled; + m_IsFrustumCullingEnabled = false; + for (const auto& s_Edge : s_Edges) { DrawLine3D(s_Corners[s_Edge[0]], s_Corners[s_Edge[1]], p_Color, p_Color); } + + m_IsFrustumCullingEnabled = s_WasFrustumCullingEnabled; } void DirectXTKRenderer::DrawOBB3D( @@ -1171,6 +1186,9 @@ void DirectXTKRenderer::DrawOBB3D( DirectX::XMVector3Transform(DirectX::SimpleMath::Vector3(p_Max.x, p_Min.y, p_Max.z), s_Transform), }; + const bool s_WasFrustumCullingEnabled = m_IsFrustumCullingEnabled; + m_IsFrustumCullingEnabled = false; + DrawLine3D(s_Corners[0], s_Corners[1], p_Color, p_Color); DrawLine3D(s_Corners[1], s_Corners[2], p_Color, p_Color); DrawLine3D(s_Corners[2], s_Corners[3], p_Color, p_Color); @@ -1186,6 +1204,8 @@ void DirectXTKRenderer::DrawOBB3D( DrawLine3D(s_Corners[2], s_Corners[4], p_Color, p_Color); DrawLine3D(s_Corners[3], s_Corners[7], p_Color, p_Color); + + m_IsFrustumCullingEnabled = s_WasFrustumCullingEnabled; } void DirectXTKRenderer::DrawBoundingQuads3D( @@ -1219,12 +1239,17 @@ void DirectXTKRenderer::DrawBoundingQuads3D( DrawTriangle3D(v0, p_Color, v2, p_Color, v3, p_Color); }; + const bool s_WasFrustumCullingEnabled = m_IsFrustumCullingEnabled; + m_IsFrustumCullingEnabled = false; + drawQuad(0, 1, 2, 3); // Front face drawQuad(4, 5, 6, 7); // Back face drawQuad(0, 1, 5, 6); // Left face drawQuad(2, 3, 7, 4); // Right face drawQuad(1, 2, 4, 5); // Top face drawQuad(0, 3, 7, 6); // Bottom face + + m_IsFrustumCullingEnabled = s_WasFrustumCullingEnabled; } void DirectXTKRenderer::DrawTriangle3D( @@ -1572,9 +1597,12 @@ void DirectXTKRenderer::DrawMesh( const SVector3 s_Center = (s_pRenderPrimitiveResource->m_vMin + s_pRenderPrimitiveResource->m_vMax) * 0.5f; const SVector3 s_Extents = (s_pRenderPrimitiveResource->m_vMax - s_pRenderPrimitiveResource->m_vMin) * 0.5f; - if (m_IsFrustumCullingEnabled && - !IsOBBInsideViewFrustum(float4(s_Center, 1.f), float4(s_Extents, 1.f), p_Transform)) { - return; + if (m_IsFrustumCullingEnabled) { + const float4 s_WorldCenter = p_Transform.WVectorTransform(float4(s_Center, 1.f)); + + if (!IsOBBInsideViewFrustum(s_WorldCenter, float4(s_Extents, 1.f), p_Transform)) { + return; + } } ScopedD3DRef s_Device; diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp index 12b7441fa..4d1490d2d 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp @@ -44,7 +44,8 @@ void ViewFrustum::UpdateClipPlanes(const SMatrix& p_View, const SMatrix& p_Proje SMatrix ViewFrustum::MatrixPerspectiveFovRH( const float p_FovYDeg, const float p_AspectWByH, const float p_NearZ, const float p_FarZ ) { - const float s_Height = tanf(p_FovYDeg * 0.5f) * (p_NearZ * 2.f); + const float s_FovYRad = DirectX::XMConvertToRadians(p_FovYDeg); + const float s_Height = std::tan(s_FovYRad * 0.5f) * (p_NearZ * 2.f); const float s_Width = s_Height * p_AspectWByH; return MatrixPerspectiveRH(s_Width, s_Height, p_NearZ, p_FarZ); @@ -54,22 +55,22 @@ SMatrix ViewFrustum::MatrixPerspectiveRH( const float p_Width, const float p_Height, const float p_NearZ, const float p_FarZ ) { return SMatrix( - {(p_NearZ * 2.f) / p_Width, 0.f, 0.f, 0.f}, - {0.f, (p_NearZ * 2.f) / p_Height, 0.f, 0.f}, - {0.f, 0.f, -p_FarZ / (p_FarZ - p_NearZ), -1.f}, - {0.f, 0.f, -(p_FarZ * p_NearZ) / (p_FarZ - p_NearZ), 0.f} + { (p_NearZ * 2.f) / p_Width, 0.f, 0.f, 0.f }, + { 0.f, (p_NearZ * 2.f) / p_Height, 0.f, 0.f }, + { 0.f, 0.f, -p_FarZ / (p_FarZ - p_NearZ), -1.f }, + { 0.f, 0.f, -(p_FarZ * p_NearZ) / (p_FarZ - p_NearZ), 0.f } ); } void ViewFrustum::MatrixCreateClipPlanes(float4* p_Planes, const SMatrix& p_ViewProjection) { const SMatrix s_ViewProjection = p_ViewProjection.Transposed(); - static const float4 s_PlaneFront = {0.f, 0.f, -1.f, 0.f}; - static const float4 s_PlaneBack = {0.f, 0.f, 1.f, -1.f}; - static const float4 s_PlaneLeft = {-1.f, 0.f, 0.f, -1.f}; - static const float4 s_PlaneRight = {1.f, 0.f, 0.f, -1.f}; - static const float4 s_PlaneBottom = {0.f, -1.f, 0.f, -1.f}; - static const float4 s_PlaneTop = {0.f, 1.f, 0.f, -1.f}; + static const float4 s_PlaneFront = { 0.f, 0.f, -1.f, 0.f }; + static const float4 s_PlaneBack = { 0.f, 0.f, 1.f, -1.f }; + static const float4 s_PlaneLeft = { -1.f, 0.f, 0.f, -1.f }; + static const float4 s_PlaneRight = { 1.f, 0.f, 0.f, -1.f }; + static const float4 s_PlaneBottom = { 0.f, -1.f, 0.f, -1.f }; + static const float4 s_PlaneTop = { 0.f, 1.f, 0.f, -1.f }; p_Planes[0] = s_ViewProjection.WVectorTransformH(s_PlaneFront); p_Planes[1] = s_ViewProjection.WVectorTransformH(s_PlaneBack); @@ -88,15 +89,15 @@ void ViewFrustum::MatrixCreateClipPlanesNormalized(float4* p_Planes, const SMatr } bool ViewFrustum::ContainsPoint(const SVector3& p_Point) const { - return CheckPointInsidePlanes(p_Point) != ECheckInsideFlag::CHECK_INSIDE_FULLY_OUTSIDE; + return CheckPointInsidePlanes(p_Point) != ContainmentType::FullyOutside; } bool ViewFrustum::ContainsAABB(const AABB& p_AABB) const { - return CheckAABBInsidePlanes(p_AABB) != ECheckInsideFlag::CHECK_INSIDE_FULLY_OUTSIDE; + return CheckAABBInsidePlanes(p_AABB) != ContainmentType::FullyOutside; } bool ViewFrustum::ContainsOBB(const SMatrix& p_Transform, const float4& p_Center, const float4& p_HalfSize) const { - return CheckOBBInsidePlanes(p_Transform, p_Center, p_HalfSize) != ECheckInsideFlag::CHECK_INSIDE_FULLY_OUTSIDE; + return CheckOBBInsidePlanes(p_Transform, p_Center, p_HalfSize) != ContainmentType::FullyOutside; } void ViewFrustum::SetDistanceCullingEnabled(const bool p_Enabled) { @@ -115,105 +116,100 @@ float ViewFrustum::GetMaxDrawDistance() const { return m_MaxDrawDistance; } -ViewFrustum::ECheckInsideFlag ViewFrustum::CheckPointInsidePlanes(const SVector3& p_Point) const { - float positiveExtentSum = 0.f; - float negativeExtentSum = 0.f; +ViewFrustum::ContainmentType ViewFrustum::CheckPointInsidePlanes(const SVector3& p_Point) const { + constexpr float s_Epsilon = 1.f / 4096.f; - for (const auto& plane : m_Planes) { - const SVector3 normal(plane.x, plane.y, plane.z); + for (const auto& s_Plane : m_Planes) { + const SVector3 s_Normal = s_Plane; - const float distance = - normal.x * p_Point.x + - normal.y * p_Point.y + - normal.z * p_Point.z + - plane.w; + const float s_Distance = + s_Normal.x * p_Point.x + + s_Normal.y * p_Point.y + + s_Normal.z * p_Point.z + + s_Plane.w; - positiveExtentSum += std::max(distance, 0.0f); - negativeExtentSum += std::max(distance, 0.0f); - } - - if (negativeExtentSum > 0.f) { - return ECheckInsideFlag::CHECK_INSIDE_FULLY_OUTSIDE; + if (s_Distance > s_Epsilon) { + return ContainmentType::FullyOutside; + } } - constexpr float epsilon = 1.f / 4096.f; - - return positiveExtentSum <= epsilon - ? ECheckInsideFlag::CHECK_INSIDE_FULLY_INSIDE - : ECheckInsideFlag::CHECK_INSIDE_PARTIALLY_INSIDE; + return ContainmentType::FullyInside; } -ViewFrustum::ECheckInsideFlag ViewFrustum::CheckAABBInsidePlanes(const AABB& p_AABB) const { - float positiveExtentSum = 0.f; - float negativeExtentSum = 0.f; +ViewFrustum::ContainmentType ViewFrustum::CheckAABBInsidePlanes(const AABB& p_AABB) const { + const SVector3 s_Center = (p_AABB.min + p_AABB.max) * 0.5f; + const SVector3 s_HalfSize = (p_AABB.max - p_AABB.min) * 0.5f; - const SVector3 center = (p_AABB.min + p_AABB.max) * 0.5f; - const SVector3 halfSize = (p_AABB.max - p_AABB.min) * 0.5f; + bool s_IsPartiallyInside = false; - for (const auto& plane : m_Planes) { - const SVector3 normal(plane.x, plane.y, plane.z); + constexpr float s_Epsilon = 1.f / 4096.f; - const float distance = - normal.x * center.x + - normal.y * center.y + - normal.z * center.z + - plane.w; + for (const auto& s_Plane : m_Planes) { + const SVector3 s_Normal = s_Plane; - const float radius = - std::fabs(normal.x) * halfSize.x + - std::fabs(normal.y) * halfSize.y + - std::fabs(normal.z) * halfSize.z; + const float s_Distance = + s_Normal.x * s_Center.x + + s_Normal.y * s_Center.y + + s_Normal.z * s_Center.z + + s_Plane.w; - positiveExtentSum += std::max(distance + radius, 0.0f); - negativeExtentSum += std::max(distance - radius, 0.0f); - } + const float s_Radius = + std::fabs(s_Normal.x) * s_HalfSize.x + + std::fabs(s_Normal.y) * s_HalfSize.y + + std::fabs(s_Normal.z) * s_HalfSize.z; - if (negativeExtentSum > 0.f) { - return ECheckInsideFlag::CHECK_INSIDE_FULLY_OUTSIDE; - } + if (s_Distance - s_Radius > s_Epsilon) { + return ContainmentType::FullyOutside; + } - constexpr float epsilon = 1.f / 4096.f; + if (s_Distance + s_Radius > 0.f) { + s_IsPartiallyInside = true; + } + } - return positiveExtentSum <= epsilon - ? ECheckInsideFlag::CHECK_INSIDE_FULLY_INSIDE - : ECheckInsideFlag::CHECK_INSIDE_PARTIALLY_INSIDE; + return s_IsPartiallyInside ? ContainmentType::PartiallyInside : ContainmentType::FullyInside; } - -ViewFrustum::ECheckInsideFlag ViewFrustum::CheckOBBInsidePlanes( - const SMatrix& p_ObjectInternal, const float4& p_LocalCenter, const float4& p_LocalSize +#include +ViewFrustum::ContainmentType ViewFrustum::CheckOBBInsidePlanes( + const SMatrix& p_Transform, + const float4& p_Center, + const float4& p_HalfSize ) const { - const float4 worldCenter = p_ObjectInternal.WVectorTransform(p_LocalCenter); - - const SVector3 axisX(p_ObjectInternal.XAxis.x, p_ObjectInternal.XAxis.y, p_ObjectInternal.XAxis.z); - const SVector3 axisY(p_ObjectInternal.YAxis.x, p_ObjectInternal.YAxis.y, p_ObjectInternal.YAxis.z); - const SVector3 axisZ(p_ObjectInternal.ZAxis.x, p_ObjectInternal.ZAxis.y, p_ObjectInternal.ZAxis.z); - - float positiveExtentSum = 0.f; - float negativeExtentSum = 0.f; - - for (const auto& plane : m_Planes) { - const SVector3 normal(plane.x, plane.y, plane.z); - - const float distance = normal.x * worldCenter.x + - normal.y * worldCenter.y + - normal.z * worldCenter.z + - plane.w; - - const float radius = std::fabs(normal * axisX) * p_LocalSize.x + - std::fabs(normal * axisY) * p_LocalSize.y + - std::fabs(normal * axisZ) * p_LocalSize.z; - - positiveExtentSum += std::max(distance + radius, 0.0f); - negativeExtentSum += std::max(distance - radius, 0.0f); - } + const SVector3 s_AxisX = p_Transform.XAxis; + const SVector3 s_AxisY = p_Transform.YAxis; + const SVector3 s_AxisZ = p_Transform.ZAxis; + + bool s_IsPartiallyInside = false; + + constexpr float s_Epsilon = 1.f / 4096.f; + + for (const auto& s_Plane : m_Planes) { + const SVector3 s_Normal = s_Plane; + + const float s_Distance = + s_Normal.x * p_Center.x + + s_Normal.y * p_Center.y + + s_Normal.z * p_Center.z + + s_Plane.w; + + const float s_Radius = + std::fabs(s_Normal * s_AxisX) * p_HalfSize.x + + std::fabs(s_Normal * s_AxisY) * p_HalfSize.y + + std::fabs(s_Normal * s_AxisZ) * p_HalfSize.z; + + if (s_Distance - s_Radius > s_Epsilon) { + Logger::Info("m_IsDistanceCullingEnabled {} Culled OBB: plane normal ({}, {}, {}) w={} | distance={} radius={} | center=({}, {}, {}) | localSize=({}, {}, {})", + m_IsDistanceCullingEnabled, + s_Normal.x, s_Normal.y, s_Normal.z, s_Plane.w, s_Distance, s_Radius, + p_Center.x, p_Center.y, p_Center.z, + p_HalfSize.x, p_HalfSize.y, p_HalfSize.z); + return ContainmentType::FullyOutside; + } - if (negativeExtentSum > 0.f) { - return ECheckInsideFlag::CHECK_INSIDE_FULLY_OUTSIDE; + if (s_Distance + s_Radius > 0.f) { + s_IsPartiallyInside = true; + } } - constexpr float epsilon = 1.f / 4096.f; - - return positiveExtentSum <= epsilon - ? ECheckInsideFlag::CHECK_INSIDE_FULLY_INSIDE - : ECheckInsideFlag::CHECK_INSIDE_PARTIALLY_INSIDE; + return s_IsPartiallyInside ? ContainmentType::PartiallyInside : ContainmentType::FullyInside; } \ No newline at end of file diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.h b/ZHMModSDK/Src/Rendering/ViewFrustum.h index e452dda6c..01c272efe 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.h +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.h @@ -21,11 +21,11 @@ class ZHMSDK_API ViewFrustum { float GetMaxDrawDistance() const; private: - enum class ECheckInsideFlag { - CHECK_INSIDE_FULLY_OUTSIDE, - CHECK_INSIDE_PARTIALLY_INSIDE, - CHECK_INSIDE_FULLY_INSIDE, - CHECK_INSIDE_UNKNOWN + enum class ContainmentType { + FullyOutside, + PartiallyInside, + FullyInside, + Unknown }; SMatrix MatrixPerspectiveFovRH( @@ -35,10 +35,12 @@ class ZHMSDK_API ViewFrustum { void MatrixCreateClipPlanes(float4* p_Planes, const SMatrix& p_ViewProjection); void MatrixCreateClipPlanesNormalized(float4* p_Planes, const SMatrix& p_ViewProjection); - ECheckInsideFlag CheckPointInsidePlanes(const SVector3& p_Point) const; - ECheckInsideFlag CheckAABBInsidePlanes(const AABB& p_AABB) const; - ECheckInsideFlag CheckOBBInsidePlanes( - const SMatrix& p_ObjectInternal, const float4& p_LocalCenter, const float4& p_LocalSize + ContainmentType CheckPointInsidePlanes(const SVector3& p_Point) const; + ContainmentType CheckAABBInsidePlanes(const AABB& p_AABB) const; + ContainmentType CheckOBBInsidePlanes( + const SMatrix& p_Transform, + const float4& p_Center, + const float4& p_HalfSize ) const; bool m_IsDistanceCullingEnabled = false; From 27a74a3da777781a17d32f56b8da265fd6bf2e44 Mon Sep 17 00:00:00 2001 From: Pavle Date: Fri, 21 Aug 2026 10:05:12 +0200 Subject: [PATCH 03/10] Add frustum culling for spheres --- ZHMModSDK/Include/IRenderer.h | 3 +- .../Rendering/Renderers/DirectXTKRenderer.cpp | 13 ++++++- .../Rendering/Renderers/DirectXTKRenderer.h | 2 +- ZHMModSDK/Src/Rendering/ViewFrustum.cpp | 37 ++++++++++++++++--- ZHMModSDK/Src/Rendering/ViewFrustum.h | 8 ++-- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/ZHMModSDK/Include/IRenderer.h b/ZHMModSDK/Include/IRenderer.h index 6d70f7839..4a7fcd47a 100644 --- a/ZHMModSDK/Include/IRenderer.h +++ b/ZHMModSDK/Include/IRenderer.h @@ -132,6 +132,7 @@ class IRenderer { virtual bool IsOBBInsideViewFrustum( const float4& p_Center, const float4& p_HalfSize, const SMatrix& p_Transform ) const = 0; + virtual bool IsSphereInsideViewFrustum(const SVector3& p_Center, const float p_Radius) const = 0; virtual void SetFrustumCullingEnabled(bool p_Enabled) = 0; virtual bool IsFrustumCullingEnabled() const = 0; @@ -145,7 +146,7 @@ class IRenderer { virtual void DrawSphere3D( const SMatrix& p_Transform, const float4& p_Position, - const float4& p_Size, + float p_Size, const SVector4& p_Color ) = 0; }; \ No newline at end of file diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp index 816f228ef..ed4fc838f 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp @@ -1666,6 +1666,10 @@ bool DirectXTKRenderer::IsOBBInsideViewFrustum( return m_ViewFrustum.ContainsOBB(p_Transform, p_Center, p_HalfSize); } +bool DirectXTKRenderer::IsSphereInsideViewFrustum(const SVector3& p_Center, const float p_Radius) const { + return m_ViewFrustum.ContainsSphere(p_Center, p_Radius); +} + void DirectXTKRenderer::SetFrustumCullingEnabled(const bool p_Enabled) { m_IsFrustumCullingEnabled = p_Enabled; } @@ -1711,9 +1715,14 @@ float DirectXTKRenderer::GetMaxDrawDistance() const { void DirectXTKRenderer::DrawSphere3D( const SMatrix& p_Transform, const float4& p_Position, - const float4& p_Size, + float p_Size, const SVector4& p_Color ) { + if (m_IsFrustumCullingEnabled && + !IsSphereInsideViewFrustum(SVector3(p_Position.x, p_Position.y, p_Position.z), p_Size)) { + return; + } + SMatrix s_Transform = p_Transform; s_Transform.Trans = p_Position; s_Transform.Trans.w = 1.f; @@ -1725,7 +1734,7 @@ void DirectXTKRenderer::DrawSphere3D( p_Color.w }; - DrawSphere3DOutlinedShaded(s_Transform, p_Size.x, s_Color.GetAsUInt32(), 48.f, false); + DrawSphere3DOutlinedShaded(s_Transform, p_Size, s_Color.GetAsUInt32(), 48.f, false); } void DirectXTKRenderer::DrawSphere3DOutlinedShaded( diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h index faaea9050..8e82955e3 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h @@ -203,7 +203,7 @@ namespace Rendering::Renderers { void DrawSphere3D( const SMatrix& p_Transform, const float4& p_Position, - const float4& p_Size, + float p_Size, const SVector4& p_Color ) override; diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp index 4d1490d2d..36786f5e9 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp @@ -100,6 +100,10 @@ bool ViewFrustum::ContainsOBB(const SMatrix& p_Transform, const float4& p_Center return CheckOBBInsidePlanes(p_Transform, p_Center, p_HalfSize) != ContainmentType::FullyOutside; } +bool ViewFrustum::ContainsSphere(const SVector3& p_Center, const float p_Radius) const { + return CheckSphereInsidePlanes(p_Center, p_Radius) != ContainmentType::FullyOutside; +} + void ViewFrustum::SetDistanceCullingEnabled(const bool p_Enabled) { m_IsDistanceCullingEnabled = p_Enabled; } @@ -169,7 +173,7 @@ ViewFrustum::ContainmentType ViewFrustum::CheckAABBInsidePlanes(const AABB& p_AA return s_IsPartiallyInside ? ContainmentType::PartiallyInside : ContainmentType::FullyInside; } -#include + ViewFrustum::ContainmentType ViewFrustum::CheckOBBInsidePlanes( const SMatrix& p_Transform, const float4& p_Center, @@ -198,11 +202,6 @@ ViewFrustum::ContainmentType ViewFrustum::CheckOBBInsidePlanes( std::fabs(s_Normal * s_AxisZ) * p_HalfSize.z; if (s_Distance - s_Radius > s_Epsilon) { - Logger::Info("m_IsDistanceCullingEnabled {} Culled OBB: plane normal ({}, {}, {}) w={} | distance={} radius={} | center=({}, {}, {}) | localSize=({}, {}, {})", - m_IsDistanceCullingEnabled, - s_Normal.x, s_Normal.y, s_Normal.z, s_Plane.w, s_Distance, s_Radius, - p_Center.x, p_Center.y, p_Center.z, - p_HalfSize.x, p_HalfSize.y, p_HalfSize.z); return ContainmentType::FullyOutside; } @@ -211,5 +210,31 @@ ViewFrustum::ContainmentType ViewFrustum::CheckOBBInsidePlanes( } } + return s_IsPartiallyInside ? ContainmentType::PartiallyInside : ContainmentType::FullyInside; +} + +ViewFrustum::ContainmentType ViewFrustum::CheckSphereInsidePlanes(const SVector3& p_Center, const float p_Radius) const { + bool s_IsPartiallyInside = false; + + for (const auto& s_Plane : m_Planes) { + const SVector3 s_Normal = s_Plane; + + const float s_Distance = + s_Normal.x * p_Center.x + + s_Normal.y * p_Center.y + + s_Normal.z * p_Center.z + + s_Plane.w; + + constexpr float s_Epsilon = 1.f / 4096.f; + + if (s_Distance - p_Radius > s_Epsilon) { + return ContainmentType::FullyOutside; + } + + if (s_Distance + p_Radius > 0.f) { + s_IsPartiallyInside = true; + } + } + return s_IsPartiallyInside ? ContainmentType::PartiallyInside : ContainmentType::FullyInside; } \ No newline at end of file diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.h b/ZHMModSDK/Src/Rendering/ViewFrustum.h index 01c272efe..4b0df1531 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.h +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.h @@ -13,11 +13,12 @@ class ZHMSDK_API ViewFrustum { bool ContainsPoint(const SVector3& p_Point) const; bool ContainsAABB(const AABB& p_AABB) const; bool ContainsOBB(const SMatrix& p_Transform, const float4& p_Center, const float4& p_HalfSize) const; + bool ContainsSphere(const SVector3& p_Center, const float p_Radius) const; void SetDistanceCullingEnabled(const bool p_Enabled); bool IsDistanceCullingEnabled() const; - void SetMaxDrawDistance(const float p_MaxDrawDistance); + void SetMaxDrawDistance(float p_MaxDrawDistance); float GetMaxDrawDistance() const; private: @@ -29,9 +30,9 @@ class ZHMSDK_API ViewFrustum { }; SMatrix MatrixPerspectiveFovRH( - const float p_FovYDeg, const float p_AspectWByH, const float p_NearZ, const float p_FarZ + float p_FovYDeg, float p_AspectWByH, float p_NearZ, float p_FarZ ); - SMatrix MatrixPerspectiveRH(const float p_Width, const float p_Height, const float p_NearZ, const float p_FarZ); + SMatrix MatrixPerspectiveRH(float p_Width, float p_Height, float p_NearZ, float p_FarZ); void MatrixCreateClipPlanes(float4* p_Planes, const SMatrix& p_ViewProjection); void MatrixCreateClipPlanesNormalized(float4* p_Planes, const SMatrix& p_ViewProjection); @@ -42,6 +43,7 @@ class ZHMSDK_API ViewFrustum { const float4& p_Center, const float4& p_HalfSize ) const; + ContainmentType CheckSphereInsidePlanes(const SVector3& p_Center, float p_Radius) const; bool m_IsDistanceCullingEnabled = false; float m_MaxDrawDistance = 50.f; From 7ad189e384ce03e872c4c57c7b469557a3b4c5a3 Mon Sep 17 00:00:00 2001 From: Pavle Date: Fri, 21 Aug 2026 10:13:13 +0200 Subject: [PATCH 04/10] Add raycasting for debug entity shapes --- Mods/Editor/Src/Components/DebugChannels.cpp | 577 ++++++++++++++++--- Mods/Editor/Src/Components/EntityTree.cpp | 20 +- Mods/Editor/Src/Editor.cpp | 4 +- Mods/Editor/Src/Editor.h | 64 +- 4 files changed, 586 insertions(+), 79 deletions(-) diff --git a/Mods/Editor/Src/Components/DebugChannels.cpp b/Mods/Editor/Src/Components/DebugChannels.cpp index 1b6118f34..e858a6981 100644 --- a/Mods/Editor/Src/Components/DebugChannels.cpp +++ b/Mods/Editor/Src/Components/DebugChannels.cpp @@ -695,7 +695,7 @@ void Editor::DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Render const float4 s_Size( p_SoundGateEntity->m_vLocalSize.x, - 0, + 0.f, p_SoundGateEntity->m_vLocalSize.y, 1.f ); @@ -851,32 +851,6 @@ void Editor::DrawTrajectoryItem( p_Renderer->DrawSphere3D(SMatrix {}, s_StartPosition, 0.05f, SVector4 { 0.0f, 1.0f, 0.0f, 1.0f }); p_Renderer->DrawSphere3D(SMatrix {}, s_PreviousSamplePosition, 0.05f, SVector4 { 1.0f, 0.0f, 0.0f, 1.0f }); - - const auto s_CurrentCamera = Functions::GetCurrentCamera->Call(); - - if (!s_CurrentCamera) { - return; - } - - auto s_CameraTransform = s_CurrentCamera->GetObjectToWorldMatrix(); - - /*s_CameraTransform.Trans = s_StartPosition; - - p_Renderer->DrawText3D( - fmt::format("{:016x}", p_Item->m_pEntityType->m_nEntityID).c_str(), - s_CameraTransform, - SVector4(1.f, 1.f, 0.f, 1.f), - 0.2f - ); - - s_CameraTransform.Trans = s_PreviousSamplePosition; - - p_Renderer->DrawText3D( - fmt::format("{:016x}", p_Item->m_pEntityType->m_nEntityID).c_str(), - s_CameraTransform, - SVector4(1.f, 1.f, 0.f, 1.f), - 0.2f - );*/ } SVector3 Editor::EvaluateTrajectoryWorldPosition( @@ -2152,7 +2126,72 @@ bool Editor::EntityIDMatches(void* p_Interface, const uint64 p_EntityID) { return false; } -bool Editor::RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Direction) { +bool Editor::RayCastDebugEntities(const SVector3& p_WorldPosition, const SVector3& p_Direction) { + RayCastResult s_Result; + + RayCastGizmos(p_WorldPosition, p_Direction, s_Result); + RayCastShapes(p_WorldPosition, p_Direction, s_Result); + + if (!s_Result.m_Entity) { + if (m_raycastLogging) { + Logger::Debug("[Editor] RayCastDebugEntities found no hits."); + } + + m_SelectedDebugEntity = {}; + + return false; + } + + m_SelectedDebugEntity = s_Result.m_Entity; + + if (m_raycastLogging) { + Logger::Debug( + "[Editor] RayCastDebugEntities hit '{}' (channel {}) at distance {}", + s_Result.m_TypeName, + static_cast(s_Result.m_DebugChannel), + s_Result.m_Distance + ); + } + + const auto s_SceneCtx = Globals::Hitman5Module->m_pEntitySceneContext; + + for (int i = 0; i < s_SceneCtx->m_aLoadedBricks.size(); ++i) { + const auto& s_Brick = s_SceneCtx->m_aLoadedBricks[i]; + + if (m_SelectedDebugEntity.IsAnyParent(s_Brick.m_EntityRef)) { + Logger::Debug( + "[Editor] Found debug entity in brick {} (idx = {}).", + s_Brick.m_RuntimeResourceID, + i + ); + + m_SelectedBrickIndex = i; + break; + } + } + + if (m_SelectedDebugEntity.GetEntity() && + m_SelectedDebugEntity.GetEntity()->GetType()) { + const auto& s_Type = *m_SelectedDebugEntity.GetEntity()->GetType(); + const auto& s_Interfaces = *s_Type.m_pInterfaceData; + + Logger::Trace( + "[Editor] Hit entity of type '{}' with id '{:x}'.", + s_Interfaces[0].m_Type->GetTypeInfo()->pszTypeName, + s_Type.m_nEntityID + ); + } + + OnSelectEntity(m_SelectedDebugEntity, true, std::nullopt); + + return true; +} + +void Editor::RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Direction, RayCastResult& p_Result) { + if (!m_DrawGizmos) { + return; + } + DirectX::SimpleMath::Ray s_Ray( DirectX::SimpleMath::Vector3(p_WorldPosition.x, p_WorldPosition.y, p_WorldPosition.z), DirectX::SimpleMath::Vector3(p_Direction.x, p_Direction.y, p_Direction.z) @@ -2196,7 +2235,8 @@ bool Editor::RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Di break; } - ZRenderPrimitiveResource* s_RenderPrimitiveResource = static_cast(s_GizmoEntity->m_PrimResourcePtr.GetResourceData()); + ZRenderPrimitiveResource* s_RenderPrimitiveResource = + static_cast(s_GizmoEntity->m_PrimResourcePtr.GetResourceData()); if (!s_RenderPrimitiveResource) { continue; @@ -2213,7 +2253,8 @@ bool Editor::RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Di SMatrix s_Transform; if (s_GizmoEntity->m_TypeName == "ZActBehaviorEntity") { - const TEntityRef s_MoveToTransform = s_GizmoEntity->m_EntityRef.GetProperty>("m_rMoveToTransform").Get(); + const TEntityRef s_MoveToTransform = + s_GizmoEntity->m_EntityRef.GetProperty>("m_rMoveToTransform").Get(); s_Transform = s_MoveToTransform.m_pInterfaceRef->GetObjectToWorldMatrix() * s_GizmoEntity->m_Transform; } @@ -2223,67 +2264,473 @@ bool Editor::RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Di s_Transform = s_SpatialEntity->GetObjectToWorldMatrix(); } - DirectX::XMMATRIX s_Transform2 = DirectX::XMLoadFloat4x4(reinterpret_cast(&s_Transform)); + DirectX::XMMATRIX s_Transform2 = + DirectX::XMLoadFloat4x4(reinterpret_cast(&s_Transform)); s_Box.Transform(s_Box, s_Transform2); float s_Distance = 0.f; - if (s_Ray.Intersects(s_Box, s_Distance)) { - if (s_Distance < s_ClosestDistance && s_Distance <= 200.f) { - s_ClosestDistance = s_Distance; - s_HitEntity = s_GizmoEntity->m_EntityRef; - s_HitEntityTypeName = s_GizmoEntity->m_TypeName; - s_HitEntityDebugChannel = s_GizmoEntity->m_DebugChannel; - } + if (s_Ray.Intersects(s_Box, s_Distance) && s_Distance < p_Result.m_Distance && s_Distance <= 200.f) { + p_Result.m_Distance = s_Distance; + p_Result.m_Entity = s_GizmoEntity->m_EntityRef; + p_Result.m_TypeName = s_GizmoEntity->m_TypeName; + p_Result.m_DebugChannel = s_GizmoEntity->m_DebugChannel; } } } } +} - if (!s_HitEntity) { - if (m_raycastLogging) - Logger::Debug("[Editor] RaycastGizmos found no hits."); +void Editor::RayCastShapes(const SVector3& p_WorldPosition, const SVector3& p_Direction, RayCastResult& p_Result) { + if (!m_DrawShapes) { + return; + } - m_SelectedGizmoEntity = {}; + DirectX::SimpleMath::Ray s_Ray( + DirectX::SimpleMath::Vector3( + p_WorldPosition.x, + p_WorldPosition.y, + p_WorldPosition.z + ), + DirectX::SimpleMath::Vector3( + p_Direction.x, + p_Direction.y, + p_Direction.z + ) + ); - return false; + std::scoped_lock s_Lock(m_DebugEntitiesMutex); + + for (const auto& [s_EntityRef, s_DebugEntities] : m_EntityRefToDebugEntities) { + for (const auto& s_DebugEntity : s_DebugEntities) { + if (s_DebugEntity->m_HasGizmo) { + continue; + } + + switch (m_ShapeDrawMode) { + case DebugDrawMode::SelectedChannelsAndTypes: { + if (!m_DebugChannelToShapeState[s_DebugEntity->m_DebugChannel] || + !m_DebugChannelToTypeNameToShapeState + [s_DebugEntity->m_DebugChannel][s_DebugEntity->m_TypeName]) { + continue; + } + + break; + } + case DebugDrawMode::SelectedEntity: { + if (s_DebugEntity->m_EntityRef != m_SelectedEntity) { + continue; + } + + break; + } + case DebugDrawMode::All: + break; + } + + float s_Distance = FLT_MAX; + bool s_Hit = false; + + if (s_DebugEntity->m_TypeName == "ZSoundGateEntity") { + auto s_SoundGateEntity = s_DebugEntity->m_EntityRef.QueryInterface( + m_DebugEntityTypeIds[DebugEntityTypeName::SoundGateEntity] + ); + + if (!s_SoundGateEntity) { + continue; + } + + s_Hit = RayCastShape( + s_Ray, + s_SoundGateEntity, + s_Distance + ); + } + else if (s_DebugEntity->m_TypeName == "ZSequenceEntity") { + auto s_SequenceEntity = s_DebugEntity->m_EntityRef.QueryInterface( + m_DebugEntityTypeIds[DebugEntityTypeName::SequenceEntity] + ); + + if (!s_SequenceEntity) { + continue; + } + + s_Hit = RayCastShape( + s_Ray, + s_SequenceEntity, + s_Distance + ); + } + + if (s_Hit && s_Distance < p_Result.m_Distance && s_Distance <= 200.f) { + p_Result.m_Distance = s_Distance; + p_Result.m_Entity = s_DebugEntity->m_EntityRef; + p_Result.m_TypeName = s_DebugEntity->m_TypeName; + p_Result.m_DebugChannel = s_DebugEntity->m_DebugChannel; + } + } } +} - m_SelectedGizmoEntity = s_HitEntity; +bool Editor::RayCastShape( + const DirectX::SimpleMath::Ray& p_Ray, + ZSoundGateEntity* p_SoundGateEntity, + float& p_Distance +) { + const SMatrix s_WorldMatrix = p_SoundGateEntity->GetObjectToWorldMatrix(); - if (m_raycastLogging) { - Logger::Debug( - "[Editor] RaycastGizmos hit gizmo '{}' (channel {}) at distance {}", - s_HitEntityTypeName, - static_cast(s_HitEntityDebugChannel), - s_ClosestDistance - ); + const SVector3 s_Center { 0.f, 0.f, 0.f }; + + const SVector3 s_Extents { + p_SoundGateEntity->m_vLocalSize.x, + p_SoundGateEntity->m_fThicknessSource + + p_SoundGateEntity->m_fThicknessDestination, + p_SoundGateEntity->m_vLocalSize.y + }; + + return RayIntersectsBox( + p_Ray, + s_WorldMatrix, + s_Center, + s_Extents, + p_Distance + ); +} + +bool Editor::RayCastShape( + const DirectX::SimpleMath::Ray& p_Ray, + ZSequenceEntity* p_SequenceEntity, + float& p_Distance +) { + bool s_Hit = false; + + for (const auto& s_Entity : p_SequenceEntity->m_aTracksAndGroups) { + ZEntityGroup* s_Group = s_Entity.QueryInterface(); + + if (!s_Group) { + continue; + } + + for (const auto& s_Entity : s_Group->m_aTracksAndGroups) { + ZTrajectoryTrackBase* s_Track = + s_Entity.QueryInterface(); + + if (!s_Track) { + continue; + } + + if (RayCastTrajectoryTrack( + p_Ray, + s_Track, + s_Group->m_targetEntity, + p_Distance + )) { + s_Hit = true; + } + } } - const auto s_SceneCtx = Globals::Hitman5Module->m_pEntitySceneContext; + return s_Hit; +} - for (int i = 0; i < s_SceneCtx->m_aLoadedBricks.size(); ++i) { - const auto& s_Brick = s_SceneCtx->m_aLoadedBricks[i]; +bool Editor::RayCastTrajectoryTrack( + const DirectX::SimpleMath::Ray& p_Ray, + ZTrajectoryTrackBase* p_Track, + ZEntityRef p_TargetEntity, + float& p_Distance +) { + bool s_Hit = false; - if (m_SelectedGizmoEntity.IsAnyParent(s_Brick.m_EntityRef)) { - Logger::Debug("[Editor] Found gizmo entity in brick {} (idx = {}).", s_Brick.m_RuntimeResourceID, i); - m_SelectedBrickIndex = i; - break; + for (const auto& s_ItemEntity : p_Track->m_aItems) { + ZMorphemeTrajectorySource* s_Item = + s_ItemEntity.m_entityRef.QueryInterface(); + + if (s_Item) { + s_Hit |= RayCastTrajectoryItem( + p_Ray, + s_Item, + p_Track, + p_TargetEntity, + p_Distance, + [&](auto* p_Item, auto* p_Track, ZGameTime p_ItemTime, ZEntityRef p_TargetEntity) { + return EvaluateTrajectoryWorldPosition( + p_Item, + p_Track, + p_ItemTime, + p_Item->m_pAnimationResource.GetResource(), + p_TargetEntity + ); + } + ); + + continue; + } + + ZEulerAngleTrajectorySource* s_Item2 = + s_ItemEntity.m_entityRef.QueryInterface(); + + if (s_Item2) { + s_Hit |= RayCastTrajectoryItem( + p_Ray, + s_Item2, + p_Track, + p_TargetEntity, + p_Distance, + [&](auto* p_Item, auto* p_Track, ZGameTime p_ItemTime, ZEntityRef p_TargetEntity) { + return EvaluateTrajectoryWorldPosition( + p_Item, + p_Track, + p_ItemTime, + p_TargetEntity + ); + } + ); + + continue; + } + + ZBezierSplineTrajectorySource* s_Item3 = + s_ItemEntity.m_entityRef.QueryInterface(); + + if (s_Item3) { + s_Hit |= RayCastTrajectoryItem( + p_Ray, + s_Item3, + p_Track, + p_TargetEntity, + p_Distance, + [&](auto* p_Item, auto* p_Track, ZGameTime p_ItemTime, ZEntityRef p_TargetEntity) { + return EvaluateTrajectoryWorldPosition( + p_Item, + p_Track, + p_ItemTime, + p_TargetEntity + ); + } + ); } } - if (m_SelectedGizmoEntity.GetEntity() && m_SelectedGizmoEntity.GetEntity()->GetType()) { - const auto& s_Type = *m_SelectedGizmoEntity.GetEntity()->GetType(); - const auto& s_Interfaces = *s_Type.m_pInterfaceData; + return s_Hit; +} - Logger::Trace( - "[Editor] Hit entity of type '{}' with id '{:x}'.", s_Interfaces[0].m_Type->GetTypeInfo()->pszTypeName, - s_Type.m_nEntityID +template +bool Editor::RayCastTrajectoryItem( + const DirectX::SimpleMath::Ray& p_Ray, + TItem* p_Item, + ZTrajectoryTrackBase* p_Track, + ZEntityRef p_TargetEntity, + float& p_Distance, + EvaluateFn&& p_EvaluateFn +) { + static constexpr float s_TrajectoryPickRadius = 0.1f; + static constexpr float s_EndpointRadius = 0.1f; + + const ZGameTime s_ItemStart = p_Item->m_startTime; + const ZGameTime s_ItemEnd = s_ItemStart + p_Item->m_duration; + + ZGameTime s_SampleTime = s_ItemStart; + const ZGameTime s_SampleStep = ZGameTime::FromSeconds(0.02f); + + const SVector3 s_StartPosition = p_EvaluateFn(p_Item, p_Track, s_ItemStart, p_TargetEntity); + + SVector3 s_PreviousSamplePosition = s_StartPosition; + + bool s_Hit = false; + + float s_Distance; + + if (RayIntersectsSphere( + p_Ray, + s_StartPosition, + s_EndpointRadius, + s_Distance + ) && + s_Distance < p_Distance) { + p_Distance = s_Distance; + s_Hit = true; + } + + while (s_SampleTime < s_ItemEnd) { + s_SampleTime = std::min( + s_SampleTime + s_SampleStep, + s_ItemEnd ); + + const SVector3 s_CurrentSamplePosition = + p_EvaluateFn( + p_Item, + p_Track, + s_SampleTime, + p_TargetEntity + ); + + if (RayIntersectsTrajectorySegment( + p_Ray, + s_PreviousSamplePosition, + s_CurrentSamplePosition, + s_TrajectoryPickRadius, + s_Distance + ) && + s_Distance < p_Distance) { + p_Distance = s_Distance; + s_Hit = true; + } + + s_PreviousSamplePosition = s_CurrentSamplePosition; + } + + if (RayIntersectsSphere( + p_Ray, + s_PreviousSamplePosition, + s_EndpointRadius, + s_Distance + ) && s_Distance < p_Distance) { + p_Distance = s_Distance; + s_Hit = true; + } + + return s_Hit; +} + +bool Editor::RayIntersectsTrajectorySegment( + const DirectX::SimpleMath::Ray& p_Ray, + const SVector3& p_Start, + const SVector3& p_End, + float p_Radius, + float& p_Distance +) { + const DirectX::SimpleMath::Vector3 s_RayOrigin = p_Ray.position; + const DirectX::SimpleMath::Vector3 s_RayDirection = p_Ray.direction; + + const DirectX::SimpleMath::Vector3 s_Start(p_Start.x, p_Start.y, p_Start.z); + const DirectX::SimpleMath::Vector3 s_End(p_End.x, p_End.y, p_End.z); + + const DirectX::SimpleMath::Vector3 s_SegmentDirection = s_End - s_Start; + const DirectX::SimpleMath::Vector3 s_Offset = s_RayOrigin - s_Start; + + const float s_A = s_RayDirection.Dot(s_RayDirection); + const float s_B = s_RayDirection.Dot(s_SegmentDirection); + const float s_C = s_SegmentDirection.Dot(s_SegmentDirection); + const float s_D = s_RayDirection.Dot(s_Offset); + const float s_E = s_SegmentDirection.Dot(s_Offset); + + if (s_C <= FLT_EPSILON) { + return false; } - OnSelectEntity(m_SelectedGizmoEntity, true, std::nullopt); + const float s_Denominator = s_A * s_C - s_B * s_B; + + float s_RayT; + float s_SegmentT; + + if (std::abs(s_Denominator) > FLT_EPSILON) { + s_RayT = (s_B * s_E - s_C * s_D) / s_Denominator; + s_SegmentT = (s_A * s_E - s_B * s_D) / s_Denominator; + } + else { + s_RayT = 0.f; + s_SegmentT = s_E / s_C; + } + + s_RayT = std::max(s_RayT, 0.f); + s_SegmentT = std::clamp(s_SegmentT, 0.f, 1.f); + + // Recalculate the closest point on the ray after clamping the segment. + s_RayT = std::max( + (s_B * s_SegmentT - s_D) / s_A, + 0.f + ); + + // Recalculate the segment point in case clamping the ray changed it. + s_SegmentT = std::clamp( + (s_B * s_RayT + s_E) / s_C, + 0.f, + 1.f + ); + + // And one final ray update for the clamped segment point. + s_RayT = std::max( + (s_B * s_SegmentT - s_D) / s_A, + 0.f + ); + + const DirectX::SimpleMath::Vector3 s_ClosestRayPoint = + s_RayOrigin + s_RayDirection * s_RayT; + + const DirectX::SimpleMath::Vector3 s_ClosestSegmentPoint = + s_Start + s_SegmentDirection * s_SegmentT; + + if (DirectX::SimpleMath::Vector3::DistanceSquared( + s_ClosestRayPoint, + s_ClosestSegmentPoint + ) > p_Radius * p_Radius) { + return false; + } + + p_Distance = DirectX::SimpleMath::Vector3::Distance(s_RayOrigin, s_ClosestRayPoint); return true; } + +bool Editor::RayIntersectsBox( + const DirectX::SimpleMath::Ray& p_Ray, + const SMatrix& p_Transform, + const SVector3& p_Center, + const SVector3& p_Extents, + float& p_Distance +) { + const DirectX::XMMATRIX s_WorldMatrix = DirectX::XMLoadFloat4x4( + reinterpret_cast(&p_Transform) + ); + + const DirectX::XMMATRIX s_InverseWorldMatrix = XMMatrixInverse(nullptr, s_WorldMatrix); + + const DirectX::SimpleMath::Vector3 s_LocalPosition = + DirectX::SimpleMath::Vector3::Transform(p_Ray.position, s_InverseWorldMatrix); + + const DirectX::SimpleMath::Vector3 s_LocalDirection = + DirectX::SimpleMath::Vector3::TransformNormal(p_Ray.direction, s_InverseWorldMatrix); + + const DirectX::SimpleMath::Ray s_LocalRay(s_LocalPosition, s_LocalDirection); + + const DirectX::BoundingBox s_Box( + DirectX::SimpleMath::Vector3(p_Center.x, p_Center.y, p_Center.z), + DirectX::SimpleMath::Vector3(p_Extents.x, p_Extents.y, p_Extents.z) + ); + + float s_LocalDistance; + + if (!s_LocalRay.Intersects(s_Box, s_LocalDistance)) { + return false; + } + + const DirectX::SimpleMath::Vector3 s_LocalHitPosition = + s_LocalPosition + s_LocalDirection * s_LocalDistance; + + const DirectX::SimpleMath::Vector3 s_WorldHitPosition = + DirectX::SimpleMath::Vector3::Transform(s_LocalHitPosition, s_WorldMatrix); + + p_Distance = DirectX::SimpleMath::Vector3::Distance(p_Ray.position, s_WorldHitPosition); + + return true; +} + +bool Editor::RayIntersectsSphere( + const DirectX::SimpleMath::Ray& p_Ray, + const SVector3& p_Position, + float p_Radius, + float& p_Distance +) { + const DirectX::BoundingSphere s_Sphere( + DirectX::SimpleMath::Vector3( + p_Position.x, + p_Position.y, + p_Position.z + ), + p_Radius + ); + + return p_Ray.Intersects(s_Sphere, p_Distance); +} \ No newline at end of file diff --git a/Mods/Editor/Src/Components/EntityTree.cpp b/Mods/Editor/Src/Components/EntityTree.cpp index 5a97e42cf..caab6d40f 100644 --- a/Mods/Editor/Src/Components/EntityTree.cpp +++ b/Mods/Editor/Src/Components/EntityTree.cpp @@ -1167,8 +1167,8 @@ void Editor::DestroyEntityInternal(ZEntityRef p_Entity, std::optionalEntity) { - m_SelectedGizmoEntity = {}; + if (m_SelectedDebugEntity == s_CurrentChild->Entity) { + m_SelectedDebugEntity = {}; } for (auto& s_Child : s_ChildrenQueue.front()->Children) { @@ -1253,8 +1253,8 @@ void Editor::DestroyEntityNodeInternal( m_SelectedEntity = {}; } - if (m_SelectedGizmoEntity == p_NodeToRemove->Entity) { - m_SelectedGizmoEntity = {}; + if (m_SelectedDebugEntity == p_NodeToRemove->Entity) { + m_SelectedDebugEntity = {}; } } @@ -1276,8 +1276,8 @@ void Editor::DestroyEntityNodeInternal( m_SelectedEntity = {}; } - if (m_SelectedGizmoEntity == s_CurrentChild->Entity) { - m_SelectedGizmoEntity = {}; + if (m_SelectedDebugEntity == s_CurrentChild->Entity) { + m_SelectedDebugEntity = {}; } for (auto& s_Child : s_CurrentChild->Children) { @@ -1354,8 +1354,8 @@ DEFINE_PLUGIN_DETOUR( m_SelectedEntity = nullptr; } - if (m_SelectedGizmoEntity == entityRef) { - m_SelectedGizmoEntity = nullptr; + if (m_SelectedDebugEntity == entityRef) { + m_SelectedDebugEntity = nullptr; } std::shared_ptr s_NodeToRemove; diff --git a/Mods/Editor/Src/Editor.cpp b/Mods/Editor/Src/Editor.cpp index 7aa3b2396..b199f6f87 100644 --- a/Mods/Editor/Src/Editor.cpp +++ b/Mods/Editor/Src/Editor.cpp @@ -710,7 +710,7 @@ void Editor::OnMouseDown(SVector2 p_Pos, bool p_FirstClick) { SVector3 s_Direction; SDK()->ScreenToWorld(p_Pos, s_World, s_Direction); - if (m_DrawGizmos && RayCastGizmos(s_World, s_Direction)) { + if (RayCastDebugEntities(s_World, s_Direction)) { return; } @@ -1361,7 +1361,7 @@ DEFINE_PLUGIN_DETOUR(Editor, void, OnClearScene, ZEntitySceneContext* th, bool p m_SelectedAction = nullptr; - m_SelectedGizmoEntity = nullptr; + m_SelectedDebugEntity = nullptr; { std::scoped_lock s_Lock(m_DebugEntitiesMutex); diff --git a/Mods/Editor/Src/Editor.h b/Mods/Editor/Src/Editor.h index 3d4d8bad7..8247acf85 100644 --- a/Mods/Editor/Src/Editor.h +++ b/Mods/Editor/Src/Editor.h @@ -7,6 +7,8 @@ #include #include +#include + #include "IPluginInterface.h" #include "Glacier/ZEntity.h" #include "Glacier/ZInput.h" @@ -100,6 +102,13 @@ class Editor : public IPluginInterface { SMatrix m_Transform; }; + struct RayCastResult { + ZEntityRef m_Entity; + std::string m_TypeName; + EDebugChannel m_DebugChannel; + float m_Distance = FLT_MAX; + }; + struct PinInfo { std::string name; std::string description; @@ -357,7 +366,58 @@ class Editor : public IPluginInterface { void DeleteDebugEntities(const std::shared_ptr& p_RootNode); EDebugChannel ConvertDrawLayerToDebugChannel(const ZDebugGizmoEntity_EDrawLayer p_DrawLayer); static bool EntityIDMatches(void* p_Interface, const uint64 p_EntityID); - bool RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Direction); + + bool RayCastDebugEntities(const SVector3& p_WorldPosition, const SVector3& p_Direction); + void RayCastGizmos(const SVector3& p_WorldPosition, const SVector3& p_Direction, RayCastResult& p_Result); + void RayCastShapes(const SVector3& p_WorldPosition, const SVector3& p_Direction, RayCastResult& p_Result); + + bool RayCastShape( + const DirectX::SimpleMath::Ray& p_Ray, + ZSoundGateEntity* p_SoundGateEntity, + float& p_Distance + ); + bool RayCastShape( + const DirectX::SimpleMath::Ray& p_Ray, + ZSequenceEntity* p_SequenceEntity, + float& p_Distance + ); + + bool RayCastTrajectoryTrack( + const DirectX::SimpleMath::Ray& p_Ray, + ZTrajectoryTrackBase* p_Track, + ZEntityRef p_TargetEntity, + float& p_Distance + ); + template + bool RayCastTrajectoryItem( + const DirectX::SimpleMath::Ray& p_Ray, + TItem* p_Item, + ZTrajectoryTrackBase* p_Track, + ZEntityRef p_TargetEntity, + float& p_Distance, + EvaluateFn&& p_EvaluateFn + ); + + bool RayIntersectsTrajectorySegment( + const DirectX::SimpleMath::Ray& p_Ray, + const SVector3& p_Start, + const SVector3& p_End, + float p_Radius, + float& p_Distance + ); + bool RayIntersectsBox( + const DirectX::SimpleMath::Ray& p_Ray, + const SMatrix& p_Transform, + const SVector3& p_Center, + const SVector3& p_Extents, + float& p_Distance + ); + bool RayIntersectsSphere( + const DirectX::SimpleMath::Ray& p_Ray, + const SVector3& p_Position, + float p_Radius, + float& p_Distance + ); void DrawTrajectoryTrack( IRenderer* p_Renderer, @@ -666,7 +726,7 @@ class Editor : public IPluginInterface { bool m_DrawShapes = false; DebugDrawMode m_ShapeDrawMode = DebugDrawMode::SelectedChannelsAndTypes; - ZEntityRef m_SelectedGizmoEntity = nullptr; + ZEntityRef m_SelectedDebugEntity = nullptr; bool m_DrawCoverInvalidOnNPCErrors = true; bool m_DrawHeroGuidesSolid = false; From 4a2c95a6c6ae09d76e499ea11a8735dd591a60a0 Mon Sep 17 00:00:00 2001 From: Pavle Date: Fri, 21 Aug 2026 10:33:55 +0200 Subject: [PATCH 05/10] Add missing IsSphereInsideViewFrustum declaration --- ZHMModSDK/Include/IRenderer.h | 2 +- .../Rendering/Renderers/DirectXTKRenderer.cpp | 8 ++++---- .../Src/Rendering/Renderers/DirectXTKRenderer.h | 1 + ZHMModSDK/Src/Rendering/ViewFrustum.cpp | 16 ++++++---------- ZHMModSDK/Src/Rendering/ViewFrustum.h | 4 ++-- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/ZHMModSDK/Include/IRenderer.h b/ZHMModSDK/Include/IRenderer.h index 4a7fcd47a..6efddabd5 100644 --- a/ZHMModSDK/Include/IRenderer.h +++ b/ZHMModSDK/Include/IRenderer.h @@ -132,7 +132,7 @@ class IRenderer { virtual bool IsOBBInsideViewFrustum( const float4& p_Center, const float4& p_HalfSize, const SMatrix& p_Transform ) const = 0; - virtual bool IsSphereInsideViewFrustum(const SVector3& p_Center, const float p_Radius) const = 0; + virtual bool IsSphereInsideViewFrustum(const SVector3& p_Center, float p_Radius) const = 0; virtual void SetFrustumCullingEnabled(bool p_Enabled) = 0; virtual bool IsFrustumCullingEnabled() const = 0; diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp index ed4fc838f..7353f41da 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp @@ -1666,11 +1666,11 @@ bool DirectXTKRenderer::IsOBBInsideViewFrustum( return m_ViewFrustum.ContainsOBB(p_Transform, p_Center, p_HalfSize); } -bool DirectXTKRenderer::IsSphereInsideViewFrustum(const SVector3& p_Center, const float p_Radius) const { +bool DirectXTKRenderer::IsSphereInsideViewFrustum(const SVector3& p_Center, float p_Radius) const { return m_ViewFrustum.ContainsSphere(p_Center, p_Radius); } -void DirectXTKRenderer::SetFrustumCullingEnabled(const bool p_Enabled) { +void DirectXTKRenderer::SetFrustumCullingEnabled(bool p_Enabled) { m_IsFrustumCullingEnabled = p_Enabled; } @@ -1678,7 +1678,7 @@ bool DirectXTKRenderer::IsFrustumCullingEnabled() const { return m_IsFrustumCullingEnabled; } -void DirectXTKRenderer::SetDistanceCullingEnabled(const bool p_Enabled) { +void DirectXTKRenderer::SetDistanceCullingEnabled(bool p_Enabled) { if (m_ViewFrustum.IsDistanceCullingEnabled() == p_Enabled) { return; } @@ -1695,7 +1695,7 @@ bool DirectXTKRenderer::IsDistanceCullingEnabled() const { return m_ViewFrustum.IsDistanceCullingEnabled(); } -void DirectXTKRenderer::SetMaxDrawDistance(const float p_MaxDrawDistance) { +void DirectXTKRenderer::SetMaxDrawDistance(float p_MaxDrawDistance) { if (m_ViewFrustum.GetMaxDrawDistance() == p_MaxDrawDistance) { return; } diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h index 8e82955e3..9981631cf 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h @@ -190,6 +190,7 @@ namespace Rendering::Renderers { bool IsOBBInsideViewFrustum( const float4& p_Center, const float4& p_HalfSize, const SMatrix& p_Transform ) const override; + bool IsSphereInsideViewFrustum(const SVector3& p_Center, float p_Radius) const override; void SetFrustumCullingEnabled(bool p_Enabled) override; bool IsFrustumCullingEnabled() const override; diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp index 36786f5e9..29a55950b 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp @@ -41,9 +41,7 @@ void ViewFrustum::UpdateClipPlanes(const SMatrix& p_View, const SMatrix& p_Proje MatrixCreateClipPlanesNormalized(m_Planes, s_View * s_Projection); } -SMatrix ViewFrustum::MatrixPerspectiveFovRH( - const float p_FovYDeg, const float p_AspectWByH, const float p_NearZ, const float p_FarZ -) { +SMatrix ViewFrustum::MatrixPerspectiveFovRH(float p_FovYDeg, float p_AspectWByH, float p_NearZ, float p_FarZ) { const float s_FovYRad = DirectX::XMConvertToRadians(p_FovYDeg); const float s_Height = std::tan(s_FovYRad * 0.5f) * (p_NearZ * 2.f); const float s_Width = s_Height * p_AspectWByH; @@ -51,9 +49,7 @@ SMatrix ViewFrustum::MatrixPerspectiveFovRH( return MatrixPerspectiveRH(s_Width, s_Height, p_NearZ, p_FarZ); } -SMatrix ViewFrustum::MatrixPerspectiveRH( - const float p_Width, const float p_Height, const float p_NearZ, const float p_FarZ -) { +SMatrix ViewFrustum::MatrixPerspectiveRH(float p_Width, float p_Height, float p_NearZ, float p_FarZ) { return SMatrix( { (p_NearZ * 2.f) / p_Width, 0.f, 0.f, 0.f }, { 0.f, (p_NearZ * 2.f) / p_Height, 0.f, 0.f }, @@ -100,11 +96,11 @@ bool ViewFrustum::ContainsOBB(const SMatrix& p_Transform, const float4& p_Center return CheckOBBInsidePlanes(p_Transform, p_Center, p_HalfSize) != ContainmentType::FullyOutside; } -bool ViewFrustum::ContainsSphere(const SVector3& p_Center, const float p_Radius) const { +bool ViewFrustum::ContainsSphere(const SVector3& p_Center, float p_Radius) const { return CheckSphereInsidePlanes(p_Center, p_Radius) != ContainmentType::FullyOutside; } -void ViewFrustum::SetDistanceCullingEnabled(const bool p_Enabled) { +void ViewFrustum::SetDistanceCullingEnabled(bool p_Enabled) { m_IsDistanceCullingEnabled = p_Enabled; } @@ -112,7 +108,7 @@ bool ViewFrustum::IsDistanceCullingEnabled() const { return m_IsDistanceCullingEnabled; } -void ViewFrustum::SetMaxDrawDistance(const float p_MaxDrawDistance) { +void ViewFrustum::SetMaxDrawDistance(float p_MaxDrawDistance) { m_MaxDrawDistance = p_MaxDrawDistance; } @@ -213,7 +209,7 @@ ViewFrustum::ContainmentType ViewFrustum::CheckOBBInsidePlanes( return s_IsPartiallyInside ? ContainmentType::PartiallyInside : ContainmentType::FullyInside; } -ViewFrustum::ContainmentType ViewFrustum::CheckSphereInsidePlanes(const SVector3& p_Center, const float p_Radius) const { +ViewFrustum::ContainmentType ViewFrustum::CheckSphereInsidePlanes(const SVector3& p_Center, float p_Radius) const { bool s_IsPartiallyInside = false; for (const auto& s_Plane : m_Planes) { diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.h b/ZHMModSDK/Src/Rendering/ViewFrustum.h index 4b0df1531..a5b083327 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.h +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.h @@ -13,9 +13,9 @@ class ZHMSDK_API ViewFrustum { bool ContainsPoint(const SVector3& p_Point) const; bool ContainsAABB(const AABB& p_AABB) const; bool ContainsOBB(const SMatrix& p_Transform, const float4& p_Center, const float4& p_HalfSize) const; - bool ContainsSphere(const SVector3& p_Center, const float p_Radius) const; + bool ContainsSphere(const SVector3& p_Center, float p_Radius) const; - void SetDistanceCullingEnabled(const bool p_Enabled); + void SetDistanceCullingEnabled(bool p_Enabled); bool IsDistanceCullingEnabled() const; void SetMaxDrawDistance(float p_MaxDrawDistance); From 347ab8a37aec138988b5226e4ede74f726ea2470 Mon Sep 17 00:00:00 2001 From: Pavle Date: Sat, 22 Aug 2026 16:38:51 +0200 Subject: [PATCH 06/10] Fix sound gate destination box position --- Mods/Editor/Src/Components/DebugChannels.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mods/Editor/Src/Components/DebugChannels.cpp b/Mods/Editor/Src/Components/DebugChannels.cpp index e858a6981..13647f673 100644 --- a/Mods/Editor/Src/Components/DebugChannels.cpp +++ b/Mods/Editor/Src/Components/DebugChannels.cpp @@ -714,10 +714,10 @@ void Editor::DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Render 0.f ); - s_WorldMatrix.Trans = s_WorldMatrix.WVectorTransform(s_SourceOffset); + const float4 s_SourcePosition = s_WorldMatrix.WVectorTransform(s_SourceOffset); p_Renderer->DrawBox3D( - s_WorldMatrix.Trans, + s_SourcePosition, s_Size, s_WorldMatrix, ZColor::UnpackUnsigned(0x3F008000) @@ -730,10 +730,10 @@ void Editor::DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Render 0.f ); - s_WorldMatrix.Trans = s_WorldMatrix.WVectorTransform(s_DestinationOffset); + const float4 s_DestinationPosition = s_WorldMatrix.WVectorTransform(s_DestinationOffset); p_Renderer->DrawBox3D( - s_WorldMatrix.Trans, + s_DestinationPosition, s_Size, s_WorldMatrix, ZColor::UnpackUnsigned(0x3F800080) From 66b01ae3a5130987ea097eb547494c94095efd12 Mon Sep 17 00:00:00 2001 From: Pavle Date: Sun, 23 Aug 2026 23:08:12 +0200 Subject: [PATCH 07/10] Fix sound gate debug box positions --- Mods/Editor/Src/Components/DebugChannels.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Mods/Editor/Src/Components/DebugChannels.cpp b/Mods/Editor/Src/Components/DebugChannels.cpp index 13647f673..7a29bfeec 100644 --- a/Mods/Editor/Src/Components/DebugChannels.cpp +++ b/Mods/Editor/Src/Components/DebugChannels.cpp @@ -716,10 +716,13 @@ void Editor::DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Render const float4 s_SourcePosition = s_WorldMatrix.WVectorTransform(s_SourceOffset); + SMatrix s_SourceMatrix = s_WorldMatrix; + s_SourceMatrix.Trans = s_SourcePosition; + p_Renderer->DrawBox3D( s_SourcePosition, s_Size, - s_WorldMatrix, + s_SourceMatrix, ZColor::UnpackUnsigned(0x3F008000) ); @@ -732,10 +735,13 @@ void Editor::DrawShapes(ZSoundGateEntity* p_SoundGateEntity, IRenderer* p_Render const float4 s_DestinationPosition = s_WorldMatrix.WVectorTransform(s_DestinationOffset); + SMatrix s_DestinationMatrix = s_WorldMatrix; + s_DestinationMatrix.Trans = s_DestinationPosition; + p_Renderer->DrawBox3D( s_DestinationPosition, s_Size, - s_WorldMatrix, + s_DestinationMatrix, ZColor::UnpackUnsigned(0x3F800080) ); From 66d4d61162ed0258268d12ad7382761c627834aa Mon Sep 17 00:00:00 2001 From: Pavle Date: Mon, 24 Aug 2026 07:10:53 +0200 Subject: [PATCH 08/10] Use full box size in DrawBox3D and DrawBoxWire3D --- .../Rendering/Renderers/DirectXTKRenderer.cpp | 24 +++++++++++-------- .../Rendering/Renderers/DirectXTKRenderer.h | 4 ++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp index 7353f41da..0d9ddd4c3 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp @@ -1081,16 +1081,18 @@ void DirectXTKRenderer::DrawBox3D(const SVector3& p_Min, const SVector3& p_Max, } void DirectXTKRenderer::DrawBox3D( - const SVector3& p_Center, const SVector3& p_HalfSize, const SMatrix& p_Transform, const SVector4& p_Color + const SVector3& p_Center, const SVector3& p_Size, const SMatrix& p_Transform, const SVector4& p_Color ) { + const SVector3 s_HalfSize = p_Size * 0.5f; + if (m_IsFrustumCullingEnabled && - !IsOBBInsideViewFrustum(p_Center, p_HalfSize, p_Transform)) { + !IsOBBInsideViewFrustum(p_Center, s_HalfSize, p_Transform)) { return; } - const SVector3 s_Right = p_Transform.Right * p_HalfSize.x; - const SVector3 s_Forward = -(p_Transform.Backward * p_HalfSize.y); - const SVector3 s_Up = p_Transform.ZAxis * p_HalfSize.z; + const SVector3 s_Right = p_Transform.Right * s_HalfSize.x; + const SVector3 s_Forward = -(p_Transform.Backward * s_HalfSize.y); + const SVector3 s_Up = p_Transform.ZAxis * s_HalfSize.z; const SVector3 s_Corners[8] = { p_Center - s_Right - s_Up - s_Forward, @@ -1127,16 +1129,18 @@ void DirectXTKRenderer::DrawBox3D( } void DirectXTKRenderer::DrawBoxWire3D( - const SVector3& p_Center, const SVector3& p_HalfSize, const SMatrix& p_Transform, const SVector4& p_Color + const SVector3& p_Center, const SVector3& p_Size, const SMatrix& p_Transform, const SVector4& p_Color ) { + const SVector3 s_HalfSize = p_Size * 0.5f; + if (m_IsFrustumCullingEnabled && - !IsOBBInsideViewFrustum(p_Center, p_HalfSize, p_Transform)) { + !IsOBBInsideViewFrustum(p_Center, s_HalfSize, p_Transform)) { return; } - const SVector3 s_Right = p_Transform.Right * p_HalfSize.x; - const SVector3 s_Forward = -(p_Transform.Backward * p_HalfSize.y); - const SVector3 s_Up = p_Transform.ZAxis * p_HalfSize.z; + const SVector3 s_Right = p_Transform.Right * s_HalfSize.x; + const SVector3 s_Forward = -(p_Transform.Backward * s_HalfSize.y); + const SVector3 s_Up = p_Transform.ZAxis * s_HalfSize.z; const SVector3 s_Corners[8] = { p_Center - s_Right - s_Up - s_Forward, diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h index 9981631cf..988c3168c 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.h @@ -124,10 +124,10 @@ namespace Rendering::Renderers { void DrawBox3D(const SVector3& p_Min, const SVector3& p_Max, const SVector4& p_Color) override; void DrawBox3D( - const SVector3& p_Center, const SVector3& p_HalfSize, const SMatrix& p_Transform, const SVector4& p_Color + const SVector3& p_Center, const SVector3& p_Size, const SMatrix& p_Transform, const SVector4& p_Color ) override; void DrawBoxWire3D( - const SVector3& p_Center, const SVector3& p_HalfSize, const SMatrix& p_Transform, const SVector4& p_Color + const SVector3& p_Center, const SVector3& p_Size, const SMatrix& p_Transform, const SVector4& p_Color ) override; void DrawOBB3D( From 828913534cb23989bb64baafa7828349eda54b90 Mon Sep 17 00:00:00 2001 From: Pavle Date: Mon, 24 Aug 2026 07:11:54 +0200 Subject: [PATCH 09/10] Apply position scale when computing mesh bounds --- ZHMModSDK/Include/Glacier/ZMath.h | 6 +++--- ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp | 7 +++++-- ZHMModSDK/Src/Rendering/ViewFrustum.cpp | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ZHMModSDK/Include/Glacier/ZMath.h b/ZHMModSDK/Include/Glacier/ZMath.h index b0a38bf5d..92536acc6 100644 --- a/ZHMModSDK/Include/Glacier/ZMath.h +++ b/ZHMModSDK/Include/Glacier/ZMath.h @@ -76,9 +76,9 @@ class SVector3 { const bool operator==(const SVector3& p_Other) const { return x == p_Other.x && y == p_Other.y && z == p_Other.z; } - - float operator*(const SVector3& p_Other) const { - return x * p_Other.x + y * p_Other.y + z * p_Other.z; + + SVector3 operator*(const SVector3& p_Other) const { + return SVector3(x * p_Other.x, y * p_Other.y, z * p_Other.z); } SVector3 operator*(const float p_Value) const { diff --git a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp index 0d9ddd4c3..b5b47628e 100644 --- a/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp +++ b/ZHMModSDK/Src/Rendering/Renderers/DirectXTKRenderer.cpp @@ -1598,8 +1598,11 @@ void DirectXTKRenderer::DrawMesh( const float4& p_TextureScaleBias, const SVector4& p_MaterialColor ) { - const SVector3 s_Center = (s_pRenderPrimitiveResource->m_vMin + s_pRenderPrimitiveResource->m_vMax) * 0.5f; - const SVector3 s_Extents = (s_pRenderPrimitiveResource->m_vMax - s_pRenderPrimitiveResource->m_vMin) * 0.5f; + const SVector3 s_RawCenter = (s_pRenderPrimitiveResource->m_vMin + s_pRenderPrimitiveResource->m_vMax) * 0.5f; + const SVector3 s_RawExtents = (s_pRenderPrimitiveResource->m_vMax - s_pRenderPrimitiveResource->m_vMin) * 0.5f; + + const SVector3 s_Center = s_RawCenter * SVector3(p_PositionScale) + SVector3(p_PositionBias); + const SVector3 s_Extents = s_RawExtents * SVector3(p_PositionScale); if (m_IsFrustumCullingEnabled) { const float4 s_WorldCenter = p_Transform.WVectorTransform(float4(s_Center, 1.f)); diff --git a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp index 29a55950b..e043cfc00 100644 --- a/ZHMModSDK/Src/Rendering/ViewFrustum.cpp +++ b/ZHMModSDK/Src/Rendering/ViewFrustum.cpp @@ -193,9 +193,9 @@ ViewFrustum::ContainmentType ViewFrustum::CheckOBBInsidePlanes( s_Plane.w; const float s_Radius = - std::fabs(s_Normal * s_AxisX) * p_HalfSize.x + - std::fabs(s_Normal * s_AxisY) * p_HalfSize.y + - std::fabs(s_Normal * s_AxisZ) * p_HalfSize.z; + std::fabs(s_Normal.Dot(s_AxisX)) * p_HalfSize.x + + std::fabs(s_Normal.Dot(s_AxisY)) * p_HalfSize.y + + std::fabs(s_Normal.Dot(s_AxisZ)) * p_HalfSize.z; if (s_Distance - s_Radius > s_Epsilon) { return ContainmentType::FullyOutside; From e36f9961dd2d882f4abcddda1a49ab295def90af Mon Sep 17 00:00:00 2001 From: Pavle Date: Tue, 25 Aug 2026 07:17:35 +0200 Subject: [PATCH 10/10] Rename DrawBox3D and DrawBoxWire3D size parameters in IRenderer --- ZHMModSDK/Include/IRenderer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ZHMModSDK/Include/IRenderer.h b/ZHMModSDK/Include/IRenderer.h index 6efddabd5..97619a3f6 100644 --- a/ZHMModSDK/Include/IRenderer.h +++ b/ZHMModSDK/Include/IRenderer.h @@ -63,11 +63,11 @@ class IRenderer { virtual void DrawBox3D(const SVector3& p_Min, const SVector3& p_Max, const SVector4& p_Color) = 0; virtual void DrawBox3D( - const SVector3& p_Center, const SVector3& p_HalfSize, const SMatrix& p_Transform, const SVector4& p_Color + const SVector3& p_Center, const SVector3& p_Size, const SMatrix& p_Transform, const SVector4& p_Color ) = 0; virtual void DrawBoxWire3D( - const SVector3& p_Center, const SVector3& p_HalfSize, const SMatrix& p_Transform, const SVector4& p_Color + const SVector3& p_Center, const SVector3& p_Size, const SMatrix& p_Transform, const SVector4& p_Color ) = 0; virtual void DrawOBB3D(