init
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class KawaiiPhysicsEd : ModuleRules
|
||||
{
|
||||
public KawaiiPhysicsEd(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"KawaiiPhysics",
|
||||
"AnimGraph",
|
||||
"BlueprintGraph",
|
||||
"Persona",
|
||||
"UnrealEd",
|
||||
"AnimGraphRuntime",
|
||||
"Slate",
|
||||
"SlateCore"
|
||||
});
|
||||
|
||||
if(Target.Version.MajorVersion >= 5)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("EditorFramework");
|
||||
if (Target.Version.MinorVersion >= 1)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("AnimationEditMode");
|
||||
}
|
||||
|
||||
// StructUtils plugin has been integrated into the engine starting from 5.5
|
||||
if (Target.Version.MinorVersion <= 4)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("StructUtils");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,667 @@
|
||||
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
|
||||
|
||||
#include "AnimGraphNode_KawaiiPhysics.h"
|
||||
|
||||
#include "Subsystems/AssetEditorSubsystem.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "DetailCategoryBuilder.h"
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "KawaiiPhysicsBoneConstraintsDataAsset.h"
|
||||
#include "KawaiiPhysicsLimitsDataAsset.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Framework/Notifications/NotificationManager.h"
|
||||
#include "Selection.h"
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
#include "Widgets/Notifications/SNotificationList.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Dialogs/DlgPickAssetPath.h"
|
||||
#include "Kismet2/CompilerResultsLog.h"
|
||||
#include "Widgets/Layout/SUniformGridPanel.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimGraphNode_KawaiiPhysics)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "KawaiiPhysics"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
UAnimGraphNode_KawaiiPhysics::UAnimGraphNode_KawaiiPhysics(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
FText UAnimGraphNode_KawaiiPhysics::GetControllerDescription() const
|
||||
{
|
||||
return LOCTEXT("Kawaii Physics", "Kawaii Physics");
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
FText UAnimGraphNode_KawaiiPhysics::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
if ((TitleType == ENodeTitleType::ListView || TitleType == ENodeTitleType::MenuTitle))
|
||||
{
|
||||
return GetControllerDescription();
|
||||
}
|
||||
// @TODO: the bone can be altered in the property editor, so we have to
|
||||
// choose to mark this dirty when that happens for this to properly work
|
||||
//if (!CachedNodeTitles.IsTitleCached(TitleType, this))
|
||||
|
||||
FFormatNamedArguments Args;
|
||||
Args.Add(TEXT("ControllerDescription"), GetControllerDescription());
|
||||
Args.Add(TEXT("RootBoneName"), FText::FromName(Node.RootBone.BoneName));
|
||||
Args.Add(TEXT("Tag"), FText::FromString(Node.KawaiiPhysicsTag.ToString()));
|
||||
|
||||
// FText::Format() is slow, so we cache this to save on performance
|
||||
if (TitleType == ENodeTitleType::ListView || TitleType == ENodeTitleType::MenuTitle)
|
||||
{
|
||||
const FText Title = Node.KawaiiPhysicsTag.IsValid()
|
||||
? FText::Format(
|
||||
LOCTEXT("AnimGraphNode_KawaiiPhysics_ListTitle",
|
||||
"{ControllerDescription} - Root: {RootBoneName} - Tag: {Tag}"), Args)
|
||||
: FText::Format(
|
||||
LOCTEXT("AnimGraphNode_KawaiiPhysics_ListTitle",
|
||||
"{ControllerDescription} - Root: {RootBoneName}"), Args);
|
||||
|
||||
CachedNodeTitles.SetCachedTitle(TitleType, Title, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
const FText Title = Node.KawaiiPhysicsTag.IsValid()
|
||||
? FText::Format(
|
||||
LOCTEXT("AnimGraphNode_KawaiiPhysics_Title",
|
||||
"{ControllerDescription}\nRoot: {RootBoneName}\nTag: {Tag} "), Args)
|
||||
: FText::Format(
|
||||
LOCTEXT("AnimGraphNode_KawaiiPhysics_Title",
|
||||
"{ControllerDescription}\nRoot: {RootBoneName}"), Args);
|
||||
|
||||
CachedNodeTitles.SetCachedTitle(TitleType, Title, this);
|
||||
}
|
||||
return CachedNodeTitles[TitleType];
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
Node.ModifyBones.Empty();
|
||||
ReconstructNode();
|
||||
}
|
||||
|
||||
FEditorModeID UAnimGraphNode_KawaiiPhysics::GetEditorMode() const
|
||||
{
|
||||
return "AnimGraph.SkeletalControl.KawaiiPhysics";
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::ValidateAnimNodePostCompile(FCompilerResultsLog& MessageLog,
|
||||
UAnimBlueprintGeneratedClass* CompiledClass,
|
||||
int32 CompiledNodeIndex)
|
||||
{
|
||||
UAnimGraphNode_SkeletalControlBase::ValidateAnimNodePostCompile(MessageLog, CompiledClass, CompiledNodeIndex);
|
||||
|
||||
Node.RootBone.Initialize(CompiledClass->TargetSkeleton);
|
||||
if (Node.RootBone.BoneIndex >= 0)
|
||||
{
|
||||
if (Node.ExcludeBones.Contains(Node.RootBone))
|
||||
{
|
||||
MessageLog.Warning(TEXT("@@ ExcludeBones should NOT has RootBone."), this);
|
||||
}
|
||||
}
|
||||
// for template ABP
|
||||
else if (CompiledClass->TargetSkeleton)
|
||||
{
|
||||
MessageLog.Warning(TEXT("@@ RootBone is empty."), this);
|
||||
}
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::CopyNodeDataToPreviewNode(FAnimNode_Base* AnimNode)
|
||||
{
|
||||
FAnimNode_KawaiiPhysics* KawaiiPhysics = static_cast<FAnimNode_KawaiiPhysics*>(AnimNode);
|
||||
|
||||
// pushing properties to preview instance, for live editing
|
||||
// Default
|
||||
KawaiiPhysics->RootBone = Node.RootBone;
|
||||
KawaiiPhysics->ExcludeBones = Node.ExcludeBones;
|
||||
KawaiiPhysics->AdditionalRootBones = Node.AdditionalRootBones;
|
||||
KawaiiPhysics->TargetFramerate = Node.TargetFramerate;
|
||||
KawaiiPhysics->OverrideTargetFramerate = Node.OverrideTargetFramerate;
|
||||
|
||||
// Physics Settings
|
||||
KawaiiPhysics->PhysicsSettings = Node.PhysicsSettings;
|
||||
KawaiiPhysics->DampingCurveData = Node.DampingCurveData;
|
||||
KawaiiPhysics->WorldDampingLocationCurveData = Node.WorldDampingLocationCurveData;
|
||||
KawaiiPhysics->WorldDampingRotationCurveData = Node.WorldDampingRotationCurveData;
|
||||
KawaiiPhysics->StiffnessCurveData = Node.StiffnessCurveData;
|
||||
KawaiiPhysics->RadiusCurveData = Node.RadiusCurveData;
|
||||
KawaiiPhysics->LimitAngleCurveData = Node.LimitAngleCurveData;
|
||||
KawaiiPhysics->bUpdatePhysicsSettingsInGame = Node.bUpdatePhysicsSettingsInGame;
|
||||
KawaiiPhysics->PlanarConstraint = Node.PlanarConstraint;
|
||||
KawaiiPhysics->ResetBoneTransformWhenBoneNotFound = Node.ResetBoneTransformWhenBoneNotFound;
|
||||
|
||||
// DummyBone
|
||||
KawaiiPhysics->DummyBoneLength = Node.DummyBoneLength;
|
||||
KawaiiPhysics->BoneForwardAxis = Node.BoneForwardAxis;
|
||||
|
||||
// Limits
|
||||
KawaiiPhysics->SphericalLimits = Node.SphericalLimits;
|
||||
KawaiiPhysics->CapsuleLimits = Node.CapsuleLimits;
|
||||
KawaiiPhysics->BoxLimits = Node.BoxLimits;
|
||||
KawaiiPhysics->PlanarLimits = Node.PlanarLimits;
|
||||
KawaiiPhysics->LimitsDataAsset = Node.LimitsDataAsset;
|
||||
KawaiiPhysics->PhysicsAssetForLimits = Node.PhysicsAssetForLimits;
|
||||
|
||||
// ExternalForce
|
||||
KawaiiPhysics->Gravity = Node.Gravity;
|
||||
KawaiiPhysics->ExternalForces = Node.ExternalForces;
|
||||
KawaiiPhysics->CustomExternalForces = Node.CustomExternalForces;
|
||||
|
||||
// Wind
|
||||
KawaiiPhysics->bEnableWind = Node.bEnableWind;
|
||||
KawaiiPhysics->WindScale = Node.WindScale;
|
||||
|
||||
// BoneConstraint
|
||||
KawaiiPhysics->BoneConstraintGlobalComplianceType = Node.BoneConstraintGlobalComplianceType;
|
||||
KawaiiPhysics->BoneConstraintIterationCountBeforeCollision = Node.BoneConstraintIterationCountBeforeCollision;
|
||||
KawaiiPhysics->BoneConstraintIterationCountAfterCollision = Node.BoneConstraintIterationCountAfterCollision;
|
||||
KawaiiPhysics->bAutoAddChildDummyBoneConstraint = Node.bAutoAddChildDummyBoneConstraint;
|
||||
KawaiiPhysics->BoneConstraints = Node.BoneConstraints;
|
||||
KawaiiPhysics->BoneConstraintsDataAsset = Node.BoneConstraintsDataAsset;
|
||||
|
||||
// SimulationSpace
|
||||
KawaiiPhysics->SimulationSpace = Node.SimulationSpace;
|
||||
KawaiiPhysics->SimulationBaseBone = Node.SimulationBaseBone;
|
||||
|
||||
// Reset for sync without compile
|
||||
KawaiiPhysics->ModifyBones.Empty();
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::CustomizeDetailTools(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
IDetailCategoryBuilder& ViewportCategory = DetailBuilder.EditCategory(TEXT("Kawaii Physics Tools"));
|
||||
FDetailWidgetRow& WidgetRow = ViewportCategory.AddCustomRow(LOCTEXT("KawaiiPhysics", "KawaiiPhysicsTools"));
|
||||
|
||||
WidgetRow
|
||||
[
|
||||
SNew(SUniformGridPanel)
|
||||
.SlotPadding(FMargin(2, 0, 2, 0))
|
||||
+ SUniformGridPanel::Slot(0, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->ExportLimitsDataAsset();
|
||||
return FReply::Handled();
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Export Limits")))
|
||||
]
|
||||
]
|
||||
+ SUniformGridPanel::Slot(1, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->ExportBoneConstraintsDataAsset();
|
||||
return FReply::Handled();
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Export BoneConstraints")))
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::CustomizeDetailDebugVisualizations(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
IDetailCategoryBuilder& ViewportCategory = DetailBuilder.EditCategory(TEXT("Debug Visualization"));
|
||||
FDetailWidgetRow& WidgetRow = ViewportCategory.AddCustomRow(
|
||||
LOCTEXT("ToggleDebugVisualizationButtonRow", "DebugVisualization"));
|
||||
|
||||
WidgetRow
|
||||
[
|
||||
SNew(SUniformGridPanel)
|
||||
.SlotPadding(FMargin(2, 0, 2, 0))
|
||||
// Show/Hide Bones button.
|
||||
+ SUniformGridPanel::Slot(0, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawBone = !this->bEnableDebugDrawBone;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawBone
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowBoneText", "Bone"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide LengthRate button.
|
||||
+ SUniformGridPanel::Slot(1, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugBoneLengthRate = !this->bEnableDebugBoneLengthRate;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugBoneLengthRate
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowLengthRateText", "Length Rate"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide AngleLimit button.
|
||||
+ SUniformGridPanel::Slot(2, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawLimitAngle = !this->bEnableDebugDrawLimitAngle;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawLimitAngle
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowLimitAngleText", "Limit Angle"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide SphereLimit button.
|
||||
+ SUniformGridPanel::Slot(0, 1)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawSphereLimit = !this->bEnableDebugDrawSphereLimit;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawSphereLimit
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowSphereLimitText", "Sphere Limit"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide CapsuleLimit button.
|
||||
+ SUniformGridPanel::Slot(1, 1)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawCapsuleLimit = !this->bEnableDebugDrawCapsuleLimit;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawCapsuleLimit
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowCapsuleLimitText", "Capsule Limit"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide BoxLimit button.
|
||||
+ SUniformGridPanel::Slot(2, 1)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawBoxLimit = !this->bEnableDebugDrawBoxLimit;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawBoxLimit
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowBoxLimitText", "Box Limit"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide PlanerLimit button.
|
||||
+ SUniformGridPanel::Slot(0, 2)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawPlanerLimit = !this->bEnableDebugDrawPlanerLimit;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawPlanerLimit
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowPlanerLimitText", "Planer Limit"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide BoneConstraint button.
|
||||
+ SUniformGridPanel::Slot(1, 2)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawBoneConstraint = !this->bEnableDebugDrawBoneConstraint;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawBoneConstraint
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowBoneConstraintText", "Bone Constraint"); })
|
||||
]
|
||||
]
|
||||
// Show/Hide ExternalForce button.
|
||||
+ SUniformGridPanel::Slot(2, 2)
|
||||
[
|
||||
SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
this->bEnableDebugDrawExternalForce = !this->bEnableDebugDrawExternalForce;
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ButtonColorAndOpacity_Lambda([this]()
|
||||
{
|
||||
return this->bEnableDebugDrawExternalForce
|
||||
? FAppStyle::Get().GetSlateColor("Colors.AccentGreen")
|
||||
: FAppStyle::Get().GetSlateColor("Colors.AccentRed");
|
||||
})
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() { return LOCTEXT("ShowExternalForceText", "External Force"); })
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
Super::CustomizeDetails(DetailBuilder);
|
||||
|
||||
CustomizeDetailTools(DetailBuilder);
|
||||
CustomizeDetailDebugVisualizations(DetailBuilder);
|
||||
|
||||
// Force order of details panel categories - Must set order for all of them as any that are edited automatically move to the top.
|
||||
auto CategorySorter = [](const TMap<FName, IDetailCategoryBuilder*>& Categories)
|
||||
{
|
||||
int32 Order = 0;
|
||||
auto SafeSetOrder = [&Categories, &Order](const FName& CategoryName)
|
||||
{
|
||||
if (IDetailCategoryBuilder* const* Builder = Categories.Find(CategoryName))
|
||||
{
|
||||
(*Builder)->SetSortOrder(Order++);
|
||||
}
|
||||
};
|
||||
|
||||
// Tools, Debug
|
||||
SafeSetOrder(FName("Kawaii Physics Tools"));
|
||||
SafeSetOrder(FName("Debug Visualization"));
|
||||
SafeSetOrder(FName("Functions"));
|
||||
|
||||
// Basic
|
||||
SafeSetOrder(FName("Bones"));
|
||||
SafeSetOrder(FName("Physics Settings"));
|
||||
SafeSetOrder(FName("Physics Settings Advanced"));
|
||||
|
||||
// Limits
|
||||
SafeSetOrder(FName("Limits"));
|
||||
SafeSetOrder(FName("Bone Constraint (Experimental)"));
|
||||
|
||||
// Other
|
||||
SafeSetOrder(FName("World Collision"));
|
||||
SafeSetOrder(FName("ExternalForce"));
|
||||
|
||||
// AnimNode
|
||||
SafeSetOrder(FName("Tag"));
|
||||
SafeSetOrder(FName("Alpha"));
|
||||
};
|
||||
|
||||
DetailBuilder.SortCategories(CategorySorter);
|
||||
}
|
||||
|
||||
struct FKawaiiPhysicsVersion
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
BeforeCustomVersionWasAdded,
|
||||
UseRuntimeFloatCurve,
|
||||
// -----<new versions can be added above this line>-------------------------------------------------
|
||||
VersionPlusOne,
|
||||
LatestVersion = VersionPlusOne - 1
|
||||
};
|
||||
|
||||
// The GUID for this custom version number
|
||||
const static FGuid GUID;
|
||||
|
||||
private:
|
||||
FKawaiiPhysicsVersion()
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
const FGuid FKawaiiPhysicsVersion::GUID(0x4B2D3E25, 0xCD681D29, 0x2DB298D7, 0xAD3E55FA);
|
||||
|
||||
const FCustomVersionRegistration GRegisterKawaiiPhysCustomVersion(FKawaiiPhysicsVersion::GUID,
|
||||
FKawaiiPhysicsVersion::LatestVersion,
|
||||
TEXT("Kawaii-Phys"));
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::Serialize(FArchive& Ar)
|
||||
{
|
||||
Super::Serialize(Ar);
|
||||
|
||||
Ar.UsingCustomVersion(FKawaiiPhysicsVersion::GUID);
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::CreateExportDataAssetPath(FString& PackageName, const FString& DefaultSuffix) const
|
||||
{
|
||||
FString AssetName;
|
||||
const FString AnimBlueprintPath = GetAnimBlueprint()->GetPackage()->GetName();
|
||||
const FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
|
||||
AssetToolsModule.Get().CreateUniqueAssetName(AnimBlueprintPath, DefaultSuffix, PackageName, AssetName);
|
||||
}
|
||||
|
||||
UPackage* UAnimGraphNode_KawaiiPhysics::CreateDataAssetPackage(const FString& DialogTitle, const FString& DefaultSuffix,
|
||||
FString& AssetName) const
|
||||
{
|
||||
FString PackageName;
|
||||
CreateExportDataAssetPath(PackageName, DefaultSuffix);
|
||||
|
||||
const TSharedRef<SDlgPickAssetPath> NewAssetDlg =
|
||||
SNew(SDlgPickAssetPath)
|
||||
.Title(FText::FromString(DialogTitle))
|
||||
.DefaultAssetPath(FText::FromString(PackageName));
|
||||
|
||||
if (NewAssetDlg->ShowModal() == EAppReturnType::Cancel)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const FString PackagePath(NewAssetDlg->GetFullAssetPath().ToString());
|
||||
AssetName = NewAssetDlg->GetAssetName().ToString();
|
||||
|
||||
return CreatePackage(*PackagePath);
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::ShowExportAssetNotification(UObject* NewAsset,
|
||||
FText NotificationText)
|
||||
{
|
||||
FNotificationInfo NotificationInfo(NotificationText);
|
||||
NotificationInfo.ExpireDuration = 5.0f;
|
||||
NotificationInfo.Hyperlink = FSimpleDelegate::CreateLambda([NewAsset]()
|
||||
{
|
||||
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(NewAsset);
|
||||
});
|
||||
NotificationInfo.HyperlinkText = LOCTEXT("OpenCreatedAsset", "Open Created Asset");
|
||||
|
||||
TSharedPtr<SNotificationItem> NotificationItem = FSlateNotificationManager::Get().AddNotification(
|
||||
NotificationInfo);
|
||||
NotificationItem->SetCompletionState(SNotificationItem::CS_Success);
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::ExportLimitsDataAsset()
|
||||
{
|
||||
FString AssetName;
|
||||
UPackage* Package = CreateDataAssetPackage(
|
||||
TEXT("Choose Location for Collision Data Asset"), TEXT("_Collision"), AssetName);
|
||||
if (!Package)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UKawaiiPhysicsLimitsDataAsset* NewDataAsset =
|
||||
NewObject<UKawaiiPhysicsLimitsDataAsset>(Package, UKawaiiPhysicsLimitsDataAsset::StaticClass(),
|
||||
FName(AssetName), RF_Public | RF_Standalone))
|
||||
{
|
||||
// look for a valid component in the object being debugged,
|
||||
// we might be set to something other than the preview.
|
||||
if (UObject* ObjectBeingDebugged = GetAnimBlueprint()->GetObjectBeingDebugged())
|
||||
{
|
||||
if (const UAnimInstance* InstanceBeingDebugged = Cast<UAnimInstance>(ObjectBeingDebugged))
|
||||
{
|
||||
NewDataAsset->Skeleton = InstanceBeingDebugged->CurrentSkeleton;
|
||||
}
|
||||
}
|
||||
|
||||
// copy data
|
||||
auto CopyLimits = [&](auto& DataLimits, auto& SourceLimits)
|
||||
{
|
||||
DataLimits = SourceLimits;
|
||||
for (auto& DataLimit : DataLimits)
|
||||
{
|
||||
DataLimit.SourceType = ECollisionSourceType::DataAsset;
|
||||
}
|
||||
};
|
||||
CopyLimits(NewDataAsset->SphericalLimits, Node.SphericalLimits);
|
||||
CopyLimits(NewDataAsset->CapsuleLimits, Node.CapsuleLimits);
|
||||
CopyLimits(NewDataAsset->BoxLimits, Node.BoxLimits);
|
||||
CopyLimits(NewDataAsset->PlanarLimits, Node.PlanarLimits);
|
||||
|
||||
// select new asset
|
||||
USelection* SelectionSet = GEditor->GetSelectedObjects();
|
||||
SelectionSet->DeselectAll();
|
||||
SelectionSet->Select(NewDataAsset);
|
||||
|
||||
FAssetRegistryModule::AssetCreated(NewDataAsset);
|
||||
Package->MarkPackageDirty();
|
||||
|
||||
// Add Notification
|
||||
FText NotificationText = FText::Format(
|
||||
LOCTEXT("ExportedLimitsDataAsset", "Exposted Limits Data Asset: {0}"), FText::FromString(AssetName));
|
||||
ShowExportAssetNotification(NewDataAsset, NotificationText);
|
||||
}
|
||||
}
|
||||
|
||||
void UAnimGraphNode_KawaiiPhysics::ExportBoneConstraintsDataAsset()
|
||||
{
|
||||
FString AssetName;
|
||||
UPackage* Package = CreateDataAssetPackage(
|
||||
TEXT("Choose Location for BoneConstraints Data Asset"), TEXT("_BoneConstraint"), AssetName);
|
||||
if (!Package)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UKawaiiPhysicsBoneConstraintsDataAsset* NewDataAsset =
|
||||
NewObject<UKawaiiPhysicsBoneConstraintsDataAsset>(
|
||||
Package, UKawaiiPhysicsBoneConstraintsDataAsset::StaticClass(),
|
||||
FName(AssetName), RF_Public | RF_Standalone))
|
||||
{
|
||||
// look for a valid component in the object being debugged,
|
||||
// we might be set to something other than the preview.
|
||||
if (UObject* ObjectBeingDebugged = GetAnimBlueprint()->GetObjectBeingDebugged())
|
||||
{
|
||||
if (const UAnimInstance* InstanceBeingDebugged = Cast<UAnimInstance>(ObjectBeingDebugged))
|
||||
{
|
||||
NewDataAsset->PreviewSkeleton = InstanceBeingDebugged->CurrentSkeleton;
|
||||
NewDataAsset->UpdatePreviewBoneList();
|
||||
}
|
||||
}
|
||||
|
||||
// copy data
|
||||
NewDataAsset->BoneConstraintsData.SetNum(Node.BoneConstraints.Num());
|
||||
for (int32 i = 0; i < Node.BoneConstraints.Num(); i++)
|
||||
{
|
||||
NewDataAsset->BoneConstraintsData[i].Update(Node.BoneConstraints[i]);
|
||||
}
|
||||
|
||||
// select new asset
|
||||
USelection* SelectionSet = GEditor->GetSelectedObjects();
|
||||
SelectionSet->DeselectAll();
|
||||
SelectionSet->Select(NewDataAsset);
|
||||
|
||||
FAssetRegistryModule::AssetCreated(NewDataAsset);
|
||||
Package->MarkPackageDirty();
|
||||
|
||||
// Add Notification
|
||||
FText NotificationText = FText::Format(
|
||||
LOCTEXT("ExportedBoneConstraintsDataAsset", "Exposted BoneConstraints Data Asset: {0}"),
|
||||
FText::FromString(AssetName));
|
||||
ShowExportAssetNotification(NewDataAsset, NotificationText);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,27 @@
|
||||
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
|
||||
|
||||
#include "KawaiiPhysicsEd.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "Textures/SlateIcon.h"
|
||||
#include "KawaiiPhysicsEditMode.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FKawaiiPhysicsModuleEd"
|
||||
|
||||
|
||||
void FKawaiiPhysicsEdModule::StartupModule()
|
||||
{
|
||||
FEditorModeRegistry::Get().RegisterMode<FKawaiiPhysicsEditMode>("AnimGraph.SkeletalControl.KawaiiPhysics",
|
||||
LOCTEXT("FKawaiiPhysicsEditMode", "Kawaii Physics"),
|
||||
FSlateIcon(), false);
|
||||
}
|
||||
|
||||
|
||||
void FKawaiiPhysicsEdModule::ShutdownModule()
|
||||
{
|
||||
FEditorModeRegistry::Get().UnregisterMode("AnimGraph.SkeletalControl.KawaiiPhysics");
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FKawaiiPhysicsEdModule, KawaiiPhysicsEd)
|
||||
//IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, KawaiiPhysicsEd, "KawaiiPhysicsEd" );
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AnimGraphNode_Base.h"
|
||||
#include "AnimNode_KawaiiPhysics.h"
|
||||
#include "AnimGraphNode_SkeletalControlBase.h"
|
||||
#include "EdGraph/EdGraphNodeUtils.h"
|
||||
|
||||
#include "AnimGraphNode_KawaiiPhysics.generated.h"
|
||||
|
||||
class FCompilerResultsLog;
|
||||
|
||||
UCLASS()
|
||||
class UAnimGraphNode_KawaiiPhysics : public UAnimGraphNode_SkeletalControlBase
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
UPROPERTY(EditAnywhere, Category = Settings)
|
||||
FAnimNode_KawaiiPhysics Node;
|
||||
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
|
||||
// UObject interface
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
|
||||
protected:
|
||||
// UAnimGraphNode_Base interface
|
||||
virtual FEditorModeID GetEditorMode() const override;
|
||||
virtual void ValidateAnimNodePostCompile(FCompilerResultsLog& MessageLog,
|
||||
UAnimBlueprintGeneratedClass* CompiledClass,
|
||||
int32 CompiledNodeIndex) override;
|
||||
virtual void CopyNodeDataToPreviewNode(FAnimNode_Base* AnimNode) override;
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||
// End of UAnimGraphNode_Base interface
|
||||
|
||||
//virtual FText GetControllerDescription() const override;
|
||||
virtual FText GetControllerDescription() const override;
|
||||
virtual const FAnimNode_SkeletalControlBase* GetNode() const override { return &Node; }
|
||||
// End of UAnimGraphNode_SkeletalControlBase interface
|
||||
|
||||
// UObject interface
|
||||
virtual void Serialize(FArchive& Ar) override;
|
||||
|
||||
// End of UObject interface
|
||||
|
||||
virtual void CustomizeDetailTools(IDetailLayoutBuilder& DetailBuilder);
|
||||
virtual void CustomizeDetailDebugVisualizations(IDetailLayoutBuilder& DetailBuilder);
|
||||
|
||||
private:
|
||||
/** Creates the export data asset path. */
|
||||
void CreateExportDataAssetPath(FString& PackageName, const FString& DefaultSuffix) const;
|
||||
|
||||
/** Creates the data asset package. */
|
||||
UPackage* CreateDataAssetPackage(const FString& DialogTitle, const FString& DefaultSuffix,
|
||||
FString& AssetName) const;
|
||||
|
||||
/** Shows the export asset notification. */
|
||||
void ShowExportAssetNotification(UObject* NewAsset, FText NotificationText);
|
||||
|
||||
/** Exports the limits data asset. */
|
||||
void ExportLimitsDataAsset();
|
||||
|
||||
/** Exports the bone constraints data asset. */
|
||||
void ExportBoneConstraintsDataAsset();
|
||||
|
||||
public:
|
||||
/** Enables or disables debug drawing for bones. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawBone = true;
|
||||
|
||||
/** Enables or disables debug drawing for bone length rate. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugBoneLengthRate = true;
|
||||
|
||||
/** Enables or disables debug drawing for limit angles. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawLimitAngle = true;
|
||||
|
||||
/** Enables or disables debug drawing for spherical limits. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawSphereLimit = true;
|
||||
|
||||
/** Enables or disables debug drawing for capsule limits. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawCapsuleLimit = true;
|
||||
|
||||
/** Enables or disables debug drawing for box limits. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawBoxLimit = true;
|
||||
|
||||
/** Enables or disables debug drawing for planar limits. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawPlanerLimit = true;
|
||||
|
||||
/** Enables or disables debug drawing for bone constraints. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawBoneConstraint = true;
|
||||
|
||||
/** Enables or disables debug drawing for external forces. */
|
||||
UPROPERTY()
|
||||
bool bEnableDebugDrawExternalForce = true;
|
||||
|
||||
private:
|
||||
/** Constructing FText strings can be costly, so we cache the node's title */
|
||||
FNodeTitleTextTable CachedNodeTitles;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
class FKawaiiPhysicsEdModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AnimNodeEditMode.h"
|
||||
#include "AnimGraphNode_KawaiiPhysics.h"
|
||||
#include "AnimNode_KawaiiPhysics.h"
|
||||
|
||||
#define UE_WIDGET UE::Widget
|
||||
|
||||
class FEditorViewportClient;
|
||||
class FPrimitiveDrawInterface;
|
||||
class USkeletalMeshComponent;
|
||||
struct FViewportClick;
|
||||
|
||||
class FKawaiiPhysicsEditMode : public FAnimNodeEditMode
|
||||
{
|
||||
public:
|
||||
FKawaiiPhysicsEditMode();
|
||||
|
||||
/** IAnimNodeEditMode interface */
|
||||
virtual void EnterMode(class UAnimGraphNode_Base* InEditorNode, struct FAnimNode_Base* InRuntimeNode) override;
|
||||
virtual void ExitMode() override;
|
||||
virtual FVector GetWidgetLocation() const override;
|
||||
virtual UE_WIDGET::EWidgetMode GetWidgetMode() const override;
|
||||
virtual ECoordSystem GetWidgetCoordinateSystem() const override;
|
||||
virtual void DoTranslation(FVector& InTranslation) override;
|
||||
virtual void DoRotation(FRotator& InRotation) override;
|
||||
virtual void DoScale(FVector& InScale) override;
|
||||
|
||||
/** FEdMode interface */
|
||||
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override;
|
||||
virtual bool HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy,
|
||||
const FViewportClick& Click) override;
|
||||
virtual bool GetCustomDrawingCoordinateSystem(FMatrix& InMatrix, void* InData) override;
|
||||
virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey,
|
||||
EInputEvent InEvent) override;
|
||||
virtual bool ShouldDrawWidget() const override;
|
||||
virtual void DrawHUD(FEditorViewportClient* ViewportClient, FViewport* Viewport, const FSceneView* View,
|
||||
FCanvas* Canvas) override;
|
||||
|
||||
protected:
|
||||
void OnExternalNodePropertyChange(FPropertyChangedEvent& InPropertyEvent);
|
||||
FDelegateHandle NodePropertyDelegateHandle;
|
||||
|
||||
void OnLimitDataAssetPropertyChange(FPropertyChangedEvent& InPropertyEvent);
|
||||
bool IsSelectAnimNodeCollision() const;
|
||||
FDelegateHandle LimitsDataAssetPropertyDelegateHandle;
|
||||
|
||||
private:
|
||||
void RenderModifyBones(FPrimitiveDrawInterface* PDI) const;
|
||||
void RenderLimitAngle(FPrimitiveDrawInterface* PDI) const;
|
||||
|
||||
/** Render each collisions */
|
||||
void RenderSphericalLimits(FPrimitiveDrawInterface* PDI) const;
|
||||
void RenderCapsuleLimit(FPrimitiveDrawInterface* PDI) const;
|
||||
void RenderBoxLimit(FPrimitiveDrawInterface* PDI) const;
|
||||
void RenderPlanerLimit(FPrimitiveDrawInterface* PDI);
|
||||
|
||||
void RenderBoneConstraint(FPrimitiveDrawInterface* PDI) const;
|
||||
void RenderExternalForces(FPrimitiveDrawInterface* PDI) const;
|
||||
|
||||
/** Helper function for GetWidgetLocation() and joint rendering */
|
||||
FVector GetWidgetLocation(ECollisionLimitType CollisionType, int32 Index) const;
|
||||
|
||||
// methods to find a valid widget mode for gizmo because doesn't need to show gizmo when the mode is "Ignore"
|
||||
UE_WIDGET::EWidgetMode FindValidWidgetMode(UE_WIDGET::EWidgetMode InWidgetMode) const;
|
||||
|
||||
/** Checking if a collision is selected and the collision is valid */
|
||||
bool IsValidSelectCollision() const;
|
||||
|
||||
// Get Select Colliison Info
|
||||
FCollisionLimitBase* GetSelectCollisionLimitRuntime() const;
|
||||
FCollisionLimitBase* GetSelectCollisionLimitGraph() const;
|
||||
|
||||
/** Draw text func for DrawHUD */
|
||||
void DrawTextItem(const FText& Text, FCanvas* Canvas, float X, float& Y, float FontHeight);
|
||||
void Draw3DTextItem(const FText& Text, FCanvas* Canvas, const FSceneView* View, const FViewport* Viewport,
|
||||
FVector Location);
|
||||
|
||||
/** Cache the typed nodes */
|
||||
struct FAnimNode_KawaiiPhysics* RuntimeNode;
|
||||
UAnimGraphNode_KawaiiPhysics* GraphNode;
|
||||
|
||||
/** The current bone selection mode */
|
||||
ECollisionLimitType SelectCollisionType = ECollisionLimitType::None;
|
||||
int32 SelectCollisionIndex = -1;
|
||||
ECollisionSourceType SelectCollisionSourceType = ECollisionSourceType::AnimNode;
|
||||
|
||||
// storing current widget mode
|
||||
mutable UE_WIDGET::EWidgetMode CurWidgetMode;
|
||||
|
||||
// physics asset body material
|
||||
TObjectPtr<UMaterialInstanceDynamic> PhysicsAssetBodyMaterial;
|
||||
};
|
||||
Reference in New Issue
Block a user