// Copyright Epic Games, Inc. All Rights Reserved. #include "AnimGraphNode_RigidBody.h" #include "AnimNodeEditModes.h" #include "Features/IModularFeatures.h" #include "Kismet2/CompilerResultsLog.h" #include "BoneControllers/AnimNode_RigidBody.h" #include "EditorModeManager.h" #include "IPhysicsAssetRenderInterface.h" #include "IAnimNodeEditMode.h" // Details includes #include "PropertyHandle.h" #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" #include "DetailCategoryBuilder.h" #include "Widgets/Input/SButton.h" #include "Widgets/Text/STextBlock.h" ///////////////////////////////////////////////////// // UAnimGraphNode_RigidBody #define LOCTEXT_NAMESPACE "RigidBody" UAnimGraphNode_RigidBody::UAnimGraphNode_RigidBody(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } FText UAnimGraphNode_RigidBody::GetControllerDescription() const { return LOCTEXT("AnimGraphNode_RigidBody_ControllerDescription", "Rigid body simulation for physics asset"); } FText UAnimGraphNode_RigidBody::GetTooltipText() const { return LOCTEXT("AnimGraphNode_RigidBody_Tooltip", "This simulates based on the skeletal mesh component's physics asset"); } FText UAnimGraphNode_RigidBody::GetNodeTitle(ENodeTitleType::Type TitleType) const { return FText(LOCTEXT("AnimGraphNode_RigidBody_NodeTitle", "RigidBody")); } FString UAnimGraphNode_RigidBody::GetNodeCategory() const { return TEXT("Animation|Dynamics"); } void UAnimGraphNode_RigidBody::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog) { Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); } void UAnimGraphNode_RigidBody::Draw(FPrimitiveDrawInterface* PDI, USkeletalMeshComponent* PreviewSkelMeshComp, const bool bIsSelected, const bool bIsPoseWatchEnabled) const { if (const FAnimNode_RigidBody* const RuntimeRigidBodyNode = GetDebuggedAnimNode()) { if (UPhysicsAsset* const PhysicsAsset = RuntimeRigidBodyNode->GetPhysicsAsset()) { IPhysicsAssetRenderInterface& PhysicsAssetRenderInterface = IModularFeatures::Get().GetModularFeature(IPhysicsAssetRenderInterface::GetModularFeatureName()); // Draw Bodies. if (bIsSelected || (bIsPoseWatchEnabled && PoseWatchElementBodies.IsValid() && PoseWatchElementBodies->GetIsVisible())) { FColor PrimitiveColorOverride = FColor::Transparent; // Get primitive color from pose watch component. if (!bIsSelected) { PrimitiveColorOverride = PoseWatchElementBodies->GetColor(); PrimitiveColorOverride.A = 255; } PhysicsAssetRenderInterface.DebugDrawBodies(PreviewSkelMeshComp, PhysicsAsset, PDI, PrimitiveColorOverride); } // Draw Constraints. if (bIsSelected || (bIsPoseWatchEnabled && PoseWatchElementConstraints.IsValid() && PoseWatchElementConstraints->GetIsVisible())) { PhysicsAssetRenderInterface.DebugDrawConstraints(PreviewSkelMeshComp, PhysicsAsset, PDI); } } } } void UAnimGraphNode_RigidBody::OnPoseWatchChanged(const bool IsPoseWatchEnabled, TObjectPtr InPoseWatch, FEditorModeTools& InModeTools, FAnimNode_Base* InRuntimeNode) { Super::OnPoseWatchChanged(IsPoseWatchEnabled, InPoseWatch, InModeTools, InRuntimeNode); UPoseWatch* const PoseWatch = InPoseWatch.Get(); if (PoseWatch) { // A new pose watch has been created for this node - add node specific pose watch components. PoseWatchElementBodies = InPoseWatch.Get()->FindOrAddElement(FText(LOCTEXT("PoseWatchElementLabel_RigidBody_PhysicsBodies", "Physics Bodies")), TEXT("PhysicsAssetEditor.Tree.Body")); PoseWatchElementConstraints = InPoseWatch.Get()->FindOrAddElement(FText(LOCTEXT("PoseWatchElementLabel__RigidBody_PhysicsConstraints", "Physics Constraints")), TEXT("PhysicsAssetEditor.Tree.Constraint")); check(PoseWatchElementConstraints.IsValid()); // Expect to find a valid component; if (PoseWatchElementConstraints.IsValid()) { PoseWatchElementConstraints->SetHasColor(false); } } } void UAnimGraphNode_RigidBody::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { Super::CustomizeDetails(DetailBuilder); IDetailCategoryBuilder& ViewportCategory = DetailBuilder.EditCategory(TEXT("Debug Visualization")); FDetailWidgetRow& WidgetRow = ViewportCategory.AddCustomRow(LOCTEXT("ToggleDebugVisualizationButtonRow", "DebugVisualization")); FAnimNode_RigidBody* const RigidBodyNode = static_cast(GetDebuggedAnimNode()); WidgetRow [ SNew(SHorizontalBox) // Show/Hide Bodies button. + SHorizontalBox::Slot() [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked_Lambda([this](){ this->ToggleBodyVisibility(); return FReply::Handled(); }) .ButtonColorAndOpacity_Lambda([this](){ return (AreAnyBodiesHidden()) ? FAppStyle::Get().GetSlateColor("Colors.AccentRed") : FAppStyle::Get().GetSlateColor("Colors.AccentGreen"); }) .Content() [ SNew(STextBlock) .Text_Lambda([this]() { return (AreAnyBodiesHidden()) ? LOCTEXT("ShowAllBodiesButtonText", "Show All Bodies") : LOCTEXT("HideAllBodiesButtonText", "Hide All Bodies"); }) .ToolTipText(LOCTEXT("ToggleBodyVisibilityButtonToolTip", "Toggle debug visualization of all physics bodies")) ] ] // Show/Hide Constraints button. + SHorizontalBox::Slot() [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked_Lambda([this](){ this->ToggleConstraintVisibility(); return FReply::Handled(); }) .ButtonColorAndOpacity_Lambda([this]() { return (AreAnyConstraintsHidden()) ? FAppStyle::Get().GetSlateColor("Colors.AccentRed") : FAppStyle::Get().GetSlateColor("Colors.AccentGreen"); }) .Content() [ SNew(STextBlock) .Text_Lambda([this]() { return (AreAnyConstraintsHidden()) ? LOCTEXT("ShowAllConstraintsButtonText", "Show All Constraints") : LOCTEXT("HideAllConstraintsButtonText", "Hide All Constraints"); }) .ToolTipText(LOCTEXT("ToggleConstraintVisibilityButtonToolTip", "Toggle debug visualization of all physics constriants")) ] ] ]; } void UAnimGraphNode_RigidBody::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); IPhysicsAssetRenderInterface& PhysicsAssetRenderInterface = IModularFeatures::Get().GetModularFeature(IPhysicsAssetRenderInterface::GetModularFeatureName()); PhysicsAssetRenderInterface.SaveConfig(); } void UAnimGraphNode_RigidBody::ToggleBodyVisibility() { FAnimNode_RigidBody* const RigidBodyNode = static_cast(GetDebuggedAnimNode()); IPhysicsAssetRenderInterface& PhysicsAssetRenderInterface = IModularFeatures::Get().GetModularFeature(IPhysicsAssetRenderInterface::GetModularFeatureName()); if (RigidBodyNode) { PhysicsAssetRenderInterface.ToggleShowAllBodies(RigidBodyNode->GetPhysicsAsset()); } } void UAnimGraphNode_RigidBody::ToggleConstraintVisibility() { FAnimNode_RigidBody* const RigidBodyNode = static_cast(GetDebuggedAnimNode()); IPhysicsAssetRenderInterface& PhysicsAssetRenderInterface = IModularFeatures::Get().GetModularFeature(IPhysicsAssetRenderInterface::GetModularFeatureName()); if (RigidBodyNode) { PhysicsAssetRenderInterface.ToggleShowAllConstraints(RigidBodyNode->GetPhysicsAsset()); } } bool UAnimGraphNode_RigidBody::AreAnyBodiesHidden() const { FAnimNode_RigidBody* const RigidBodyNode = static_cast(GetDebuggedAnimNode()); IPhysicsAssetRenderInterface& PhysicsAssetRenderInterface = IModularFeatures::Get().GetModularFeature(IPhysicsAssetRenderInterface::GetModularFeatureName()); if (RigidBodyNode) { return PhysicsAssetRenderInterface.AreAnyBodiesHidden(RigidBodyNode->GetPhysicsAsset()); } return false; } bool UAnimGraphNode_RigidBody::AreAnyConstraintsHidden() const { FAnimNode_RigidBody* const RigidBodyNode = static_cast(GetDebuggedAnimNode()); IPhysicsAssetRenderInterface& PhysicsAssetRenderInterface = IModularFeatures::Get().GetModularFeature(IPhysicsAssetRenderInterface::GetModularFeatureName()); if (RigidBodyNode) { return PhysicsAssetRenderInterface.AreAnyConstraintsHidden(RigidBodyNode->GetPhysicsAsset()); } return false; } #undef LOCTEXT_NAMESPACE