This commit is contained in:
2026-05-17 22:44:49 +03:00
commit 26fedadcd8
9071 changed files with 44364 additions and 0 deletions
@@ -0,0 +1,29 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.19.0",
"FriendlyName": "KawaiiPhysics",
"Description": "Simple Bone Physics for UnrealEngine4 & 5",
"Category": "Animation",
"CreatedBy": "pafuhana1213",
"CreatedByURL": "https://twitter.com/pafuhana1213",
"DocsURL": "https://github.com/pafuhana1213/KawaiiPhysics",
"MarketplaceURL": "",
"SupportURL": "https://github.com/pafuhana1213/KawaiiPhysics/issues",
"CanContainContent": false,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "KawaiiPhysics",
"Type": "Runtime",
"LoadingPhase": "PostConfigInit"
},
{
"Name": "KawaiiPhysicsEd",
"Type": "UncookedOnly",
"LoadingPhase": "PreDefault"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,81 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "AnimNotifyState_KawaiiPhysics.h"
#include "KawaiiPhysicsLibrary.h"
#include "Misc/UObjectToken.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNotifyState_KawaiiPhysics)
#define LOCTEXT_NAMESPACE "KawaiiPhysics_AnimNotifyState"
UAnimNotifyState_KawaiiPhysicsAddExternalForce::UAnimNotifyState_KawaiiPhysicsAddExternalForce(
const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
#if WITH_EDITORONLY_DATA
NotifyColor = FColor(255, 170, 0, 255);
#endif // WITH_EDITORONLY_DATA
}
FString UAnimNotifyState_KawaiiPhysicsAddExternalForce::GetNotifyName_Implementation() const
{
return FString(TEXT("KP: Add ExternalForce"));
}
void UAnimNotifyState_KawaiiPhysicsAddExternalForce::NotifyBegin(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
float TotalDuration,
const FAnimNotifyEventReference& EventReference)
{
UKawaiiPhysicsLibrary::AddExternalForcesToComponent(MeshComp, AdditionalExternalForces, this,
FilterTags, bFilterExactMatch);
Super::NotifyBegin(MeshComp, Animation, TotalDuration, EventReference);
}
void UAnimNotifyState_KawaiiPhysicsAddExternalForce::NotifyEnd(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference)
{
UKawaiiPhysicsLibrary::RemoveExternalForcesFromComponent(MeshComp, this, FilterTags, bFilterExactMatch);
Super::NotifyEnd(MeshComp, Animation, EventReference);
}
#if WITH_EDITOR
void UAnimNotifyState_KawaiiPhysicsAddExternalForce::ValidateAssociatedAssets()
{
static const FName NAME_AssetCheck("AssetCheck");
if (const UAnimSequenceBase* ContainingAsset = Cast<UAnimSequenceBase>(GetContainingAsset()))
{
for (auto& ForceInstancedStruct : AdditionalExternalForces)
{
if (!ForceInstancedStruct.IsValid())
{
FMessageLog AssetCheckLog(NAME_AssetCheck);
const FText MessageLooping = FText::Format(
NSLOCTEXT("AnimNotify", "ExternalForce_ShouldSet",
" AnimNotifyState(KawaiiPhysics_AddExternalForce) doesn't have a valid ExternalForce in {0}"),
FText::AsCultureInvariant(ContainingAsset->GetPathName()));
AssetCheckLog.Warning()
->AddToken(FUObjectToken::Create(ContainingAsset))
->AddToken(FTextToken::Create(MessageLooping));
if (GIsEditor)
{
constexpr bool bForce = true;
AssetCheckLog.Notify(MessageLooping, EMessageSeverity::Warning, bForce);
}
}
//const auto& ExternalForce = ForceInstancedStruct.Get<FKawaiiPhysics_ExternalForce>();
}
}
}
#endif
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,90 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "GameplayTagContainer.h"
#include "Animation/AnimNotifies/AnimNotifyState.h"
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
#include "StructUtils/InstancedStruct.h"
#else
#include "InstancedStruct.h"
#endif
#include "AnimNotifyState_KawaiiPhysics.generated.h"
/**
* UAnimNotifyState_KawaiiPhysicsAddExternalForce
*
* This class represents an animation notify state that adds external forces to a skeletal mesh component
* during an animation sequence. It inherits from UAnimNotifyState and provides functionality to add and remove
* external forces at the beginning and end of the animation notify state.
*/
UCLASS(Blueprintable, meta = (DisplayName = "KawaiiPhyiscs: Add ExternalForce"))
class KAWAIIPHYSICS_API UAnimNotifyState_KawaiiPhysicsAddExternalForce : public UAnimNotifyState
{
GENERATED_BODY()
public:
/**
* Constructor for UAnimNotifyState_KawaiiPhysicsAddExternalForce.
*
* @param ObjectInitializer - The object initializer for this class.
*/
UAnimNotifyState_KawaiiPhysicsAddExternalForce(const FObjectInitializer& ObjectInitializer);
/**
* Gets the name of the notify state.
*
* @return The name of the notify state as a string.
*/
virtual FString GetNotifyName_Implementation() const override;
/**
* Called when the animation notify state begins.
*
* @param MeshComp - The skeletal mesh component.
* @param Animation - The animation sequence.
* @param TotalDuration - The total duration of the notify state.
* @param EventReference - The event reference.
*/
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration,
const FAnimNotifyEventReference& EventReference) override;
/**
* Called when the animation notify state ends.
*
* @param MeshComp - The skeletal mesh component.
* @param Animation - The animation sequence.
* @param EventReference - The event reference.
*/
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference) override;
/**
* Additional external forces to be applied to the skeletal mesh component.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExternalForce",
meta = (BaseStruct = "/Script/KawaiiPhysics.KawaiiPhysics_ExternalForce", ExcludeBaseStruct))
TArray<FInstancedStruct> AdditionalExternalForces;
/**
* Tags used to filter which external forces are applied.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExternalForce")
FGameplayTagContainer FilterTags;
/**
* Whether to filter tags to exact matches (if False, parent tags will also be included).
* Tagのフィルタリングにて完全一致にするか否か(Falseの場合は親Tagも含めます)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExternalForce")
bool bFilterExactMatch;
#if WITH_EDITOR
/**
* Validates the associated assets in the editor.
*/
virtual void ValidateAssociatedAssets() override;
#endif
};
@@ -0,0 +1,69 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "AnimNotify_KawaiiPhysics.h"
#include "KawaiiPhysicsLibrary.h"
#include "Misc/UObjectToken.h"
#include "Logging/MessageLog.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNotify_KawaiiPhysics)
#define LOCTEXT_NAMESPACE "KawaiiPhysics_AnimNotify"
UAnimNotify_KawaiiPhysicsAddExternalForce::UAnimNotify_KawaiiPhysicsAddExternalForce(
const FObjectInitializer& ObjectInitializer)
{
#if WITH_EDITORONLY_DATA
NotifyColor = FColor(255, 170, 0, 255);
#endif // WITH_EDITORONLY_DATA
}
FString UAnimNotify_KawaiiPhysicsAddExternalForce::GetNotifyName_Implementation() const
{
return FString(TEXT("KP: Add ExternalForce"));
}
void UAnimNotify_KawaiiPhysicsAddExternalForce::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference)
{
UKawaiiPhysicsLibrary::AddExternalForcesToComponent(MeshComp, AdditionalExternalForces, this,
FilterTags, bFilterExactMatch, true);
Super::Notify(MeshComp, Animation, EventReference);
}
#if WITH_EDITOR
void UAnimNotify_KawaiiPhysicsAddExternalForce::ValidateAssociatedAssets()
{
static const FName NAME_AssetCheck("AssetCheck");
if (const UAnimSequenceBase* ContainingAsset = Cast<UAnimSequenceBase>(GetContainingAsset()))
{
for (auto& ForceInstancedStruct : AdditionalExternalForces)
{
if (!ForceInstancedStruct.IsValid())
{
FMessageLog AssetCheckLog(NAME_AssetCheck);
const FText MessageLooping = FText::Format(
NSLOCTEXT("AnimNotify", "ExternalForce_ShouldSet",
" AnimNotify(KawaiiPhysics_AddExternalForce) doesn't have a valid ExternalForce in {0}"),
FText::AsCultureInvariant(ContainingAsset->GetPathName()));
AssetCheckLog.Warning()
->AddToken(FUObjectToken::Create(ContainingAsset))
->AddToken(FTextToken::Create(MessageLooping));
if (GIsEditor)
{
constexpr bool bForce = true;
AssetCheckLog.Notify(MessageLooping, EMessageSeverity::Warning, bForce);
}
}
//const auto& ExternalForce = ForceInstancedStruct.Get<FKawaiiPhysics_ExternalForce>();
}
}
}
#endif
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,80 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "GameplayTagContainer.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5
#include "StructUtils/InstancedStruct.h"
#else
#include "InstancedStruct.h"
#endif
#include "AnimNotify_KawaiiPhysics.generated.h"
/**
* UAnimNotify_KawaiiPhysicsAddExternalForce
*
* This class represents an animation notify that adds external forces to a skeletal mesh component
* during an animation sequence. It inherits from UAnimNotify and provides functionality to add and remove
* external forces when the notify is triggered.
*/
UCLASS(Blueprintable, meta = (DisplayName = "KawaiiPhyiscs: Add ExternalForce"))
class KAWAIIPHYSICS_API UAnimNotify_KawaiiPhysicsAddExternalForce : public UAnimNotify
{
GENERATED_BODY()
public:
/**
* Constructor for UAnimNotify_KawaiiPhysicsAddExternalForce.
*
* @param ObjectInitializer - The object initializer for this class.
*/
UAnimNotify_KawaiiPhysicsAddExternalForce(const FObjectInitializer& ObjectInitializer);
/**
* Gets the name of the notify.
*
* @return The name of the notify as a string.
*/
virtual FString GetNotifyName_Implementation() const override;
/**
* Called when the animation notify is triggered.
*
* @param MeshComp - The skeletal mesh component.
* @param Animation - The animation sequence.
* @param EventReference - The event reference.
*/
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference) override;
public:
/**
* Additional external forces to be applied to the skeletal mesh component.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExternalForce",
meta = (BaseStruct = "/Script/KawaiiPhysics.KawaiiPhysics_ExternalForce", ExcludeBaseStruct))
TArray<FInstancedStruct> AdditionalExternalForces;
/**
* Tags used to filter which external forces are applied.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExternalForce")
FGameplayTagContainer FilterTags;
/**
* Whether to filter tags to exact matches (if False, parent tags will also be included).
* Tagのフィルタリングにて完全一致にするか否か(Falseの場合は親Tagも含めます)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExternalForce")
bool bFilterExactMatch;
#if WITH_EDITOR
/**
* Validates the associated assets in the editor.
*/
virtual void ValidateAssociatedAssets() override;
#endif
};
@@ -0,0 +1,41 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
using UnrealBuildTool;
public class KawaiiPhysics : ModuleRules
{
public KawaiiPhysics(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new[]
{
"Core",
"AnimGraphRuntime",
"GameplayTags"
}
);
// StructUtils plugin has been integrated into the engine starting from 5.5
if (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion <= 4)
{
PublicDependencyModuleNames.Add("StructUtils");
}
PrivateDependencyModuleNames.AddRange(
new[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore"
}
);
if (Target.bBuildEditor)
{
PublicDependencyModuleNames.Add("UnrealEd");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "KawaiiPhysics.h"
#include "Modules/ModuleManager.h"
#define LOCTEXT_NAMESPACE "FKawaiiPhysicsModule"
void FKawaiiPhysicsModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FKawaiiPhysicsModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FKawaiiPhysicsModule, KawaiiPhysics)
@@ -0,0 +1,173 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "KawaiiPhysicsBoneConstraintsDataAsset.h"
#include "KawaiiPhysics.h"
#include "Internationalization/Regex.h"
#if WITH_EDITOR
#include "Editor.h"
#endif
#include UE_INLINE_GENERATED_CPP_BY_NAME(KawaiiPhysicsBoneConstraintsDataAsset)
struct FBoneConstraintDataCustomVersion
{
enum Type
{
// FNameからFBoneReferenceに移行
ChangeToBoneReference = 0,
// ------------------------------------------------------
VersionPlusOne,
LatestVersion = VersionPlusOne - 1
};
// The GUID for this custom version number
const static FGuid GUID;
private:
FBoneConstraintDataCustomVersion()
{
}
};
const FGuid FBoneConstraintDataCustomVersion::GUID(0xA1C4D3F6, 0x5B2E7A8D, 0x9F6E4B3C, 0xD7E1A8B2);
FCustomVersionRegistration GRegisterBoneConstraintDataCustomVersion(FBoneConstraintDataCustomVersion::GUID,
FBoneConstraintDataCustomVersion::LatestVersion,
TEXT("BoneConstraintData"));
void FModifyBoneConstraintData::Update(const FModifyBoneConstraint& BoneConstraint)
{
BoneReference1 = BoneConstraint.Bone1;
BoneReference2 = BoneConstraint.Bone2;
bOverrideCompliance = BoneConstraint.bOverrideCompliance;
ComplianceType = BoneConstraint.ComplianceType;
}
TArray<FModifyBoneConstraint> UKawaiiPhysicsBoneConstraintsDataAsset::GenerateBoneConstraints()
{
TArray<FModifyBoneConstraint> BoneConstraints;
for (const FModifyBoneConstraintData& BoneConstraintData : BoneConstraintsData)
{
FModifyBoneConstraint BoneConstraint;
BoneConstraint.Bone1 = BoneConstraintData.BoneReference1;
BoneConstraint.Bone2 = BoneConstraintData.BoneReference2;
BoneConstraint.bOverrideCompliance = BoneConstraintData.bOverrideCompliance;
BoneConstraint.ComplianceType = BoneConstraintData.ComplianceType;
BoneConstraints.Add(BoneConstraint);
}
return BoneConstraints;
}
void UKawaiiPhysicsBoneConstraintsDataAsset::Serialize(FStructuredArchiveRecord Record)
{
Super::Serialize(Record);
Record.GetUnderlyingArchive().UsingCustomVersion(FBoneConstraintDataCustomVersion::GUID);
}
void UKawaiiPhysicsBoneConstraintsDataAsset::PostLoad()
{
Super::PostLoad();
if (GetLinkerCustomVersion(FBoneConstraintDataCustomVersion::GUID) <
FBoneConstraintDataCustomVersion::ChangeToBoneReference)
{
for (auto& Data : BoneConstraintsData)
{
Data.BoneReference1 = FBoneReference(Data.BoneName1);
Data.BoneReference2 = FBoneReference(Data.BoneName2);
}
#if WITH_EDITOR
UpdatePreviewBoneList();
#endif
UE_LOG(LogKawaiiPhysics, Log, TEXT("Update : BoneName -> BoneReference (%s)"), *this->GetName());
}
}
USkeleton* UKawaiiPhysicsBoneConstraintsDataAsset::GetSkeleton(bool& bInvalidSkeletonIsError,
const IPropertyHandle* PropertyHandle)
{
#if WITH_EDITOR
return PreviewSkeleton.LoadSynchronous();
#else
return nullptr;
#endif
}
#if WITH_EDITOR
#define LOCTEXT_NAMESPACE "KawaiiPhysicsBoneConstraintsDataAsset"
void UKawaiiPhysicsBoneConstraintsDataAsset::ApplyRegex()
{
GEditor->BeginTransaction(FText::FromString("ApplyRegex"));
Modify();
UpdatePreviewBoneList();
for (FRegexPatternBoneSet& Pattern : RegexPatternList)
{
const FRegexPattern Pattern1 = FRegexPattern(Pattern.RegexPatternBone1);
const FRegexPattern Pattern2 = FRegexPattern(Pattern.RegexPatternBone2);
FRegexMatcher Matcher1(Pattern1, PreviewBoneListString);
FRegexMatcher Matcher2(Pattern2, PreviewBoneListString);
while (Matcher1.FindNext() && Matcher2.FindNext())
{
FModifyBoneConstraintData BoneConstraintData;
BoneConstraintData.BoneReference1 = FBoneReference(FName(*Matcher1.GetCaptureGroup(0)));
BoneConstraintData.BoneReference2 = FBoneReference(FName(*Matcher2.GetCaptureGroup(0)));
BoneConstraintsData.Add(BoneConstraintData);
}
}
GEditor->EndTransaction();
}
void UKawaiiPhysicsBoneConstraintsDataAsset::UpdatePreviewBoneList()
{
PreviewBoneList.Empty();
PreviewBoneListString.Empty();
if (!PreviewSkeleton.IsValid())
{
PreviewSkeleton.LoadSynchronous();
}
if (PreviewSkeleton.IsValid())
{
const FReferenceSkeleton& RefSkeleton = PreviewSkeleton->GetReferenceSkeleton();
const TArray<FMeshBoneInfo>& RefBoneInfo = RefSkeleton.GetRefBoneInfo();
for (const FMeshBoneInfo& BoneInfo : RefBoneInfo)
{
PreviewBoneList.Add(BoneInfo.Name);
PreviewBoneListString.Append(BoneInfo.Name.ToString());
PreviewBoneListString.Append(TEXT(", "));
}
}
}
void UKawaiiPhysicsBoneConstraintsDataAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
const FName PropertyName = PropertyChangedEvent.MemberProperty
? PropertyChangedEvent.MemberProperty->GetFName()
: NAME_None;
if (PropertyName == FName(TEXT("PreviewSkeleton")))
{
UpdatePreviewBoneList();
}
}
#undef LOCTEXT_NAMESPACE
#endif
@@ -0,0 +1,5 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "KawaiiPhysicsCustomExternalForce.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(KawaiiPhysicsCustomExternalForce)
@@ -0,0 +1,322 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "KawaiiPhysicsExternalForce.h"
#include "SceneInterface.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(KawaiiPhysicsExternalForce)
DECLARE_CYCLE_STAT(TEXT("KawaiiPhysics_ExternalForce_Basic_Apply"), STAT_KawaiiPhysics_ExternalForce_Basic_Apply,
STATGROUP_Anim);
DECLARE_CYCLE_STAT(TEXT("KawaiiPhysics_ExternalForce_Gravity_Apply"), STAT_KawaiiPhysics_ExternalForce_Gravity_Apply,
STATGROUP_Anim);
DECLARE_CYCLE_STAT(TEXT("KawaiiPhysics_ExternalForce_Curve_Apply"), STAT_KawaiiPhysics_ExternalForce_Curve_Apply,
STATGROUP_Anim);
DECLARE_CYCLE_STAT(TEXT("KawaiiPhysics_ExternalForce_Wind_Apply"), STAT_KawaiiPhysics_ExternalForce_Wind_Apply,
STATGROUP_Anim);
///
/// Basic
///
void FKawaiiPhysics_ExternalForce_Basic::PreApply(FAnimNode_KawaiiPhysics& Node,
const USkeletalMeshComponent* SkelComp)
{
Super::PreApply(Node, SkelComp);
PrevTime = Time;
Time += Node.DeltaTime;
if (Interval > 0.0f)
{
if (Time > Interval)
{
Force = ForceDir * RandomizedForceScale;
Time = FMath::Fmod(Time, Interval);
}
else
{
Force = FVector::ZeroVector;
}
}
else
{
Force = ForceDir * RandomizedForceScale;
}
if (ExternalForceSpace == EExternalForceSpace::WorldSpace)
{
Force = ComponentTransform.InverseTransformVector(Force);
}
}
void FKawaiiPhysics_ExternalForce_Basic::Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext, const FTransform& BoneTM)
{
if (!CanApply(Bone))
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_ExternalForce_Basic_Apply);
float ForceRate = 1.0f;
if (const auto Curve = ForceRateByBoneLengthRate.GetRichCurve(); !Curve->IsEmpty())
{
ForceRate = Curve->Eval(Bone.LengthRateFromRoot);
}
if (ExternalForceSpace == EExternalForceSpace::BoneSpace)
{
const FVector BoneForce = BoneTM.TransformVector(Force);
Bone.Location += BoneForce * ForceRate * Node.DeltaTime;
#if ENABLE_ANIM_DEBUG
BoneForceMap.Add(Bone.BoneRef.BoneName, BoneForce);
#endif
}
else
{
Bone.Location += Force * ForceRate * Node.DeltaTime;
#if ENABLE_ANIM_DEBUG
BoneForceMap.Add(Bone.BoneRef.BoneName, Force * ForceRate);
#endif
}
}
///
/// Gravity
///
void FKawaiiPhysics_ExternalForce_Gravity::PreApply(FAnimNode_KawaiiPhysics& Node,
const USkeletalMeshComponent* SkelComp)
{
Super::PreApply(Node, SkelComp);
Force = bUseOverrideGravityDirection ? OverrideGravityDirection : FVector(0, 0, -1.0f);
// For Character's Custom Gravity Direction
if (const ACharacter* Character = Cast<ACharacter>(SkelComp->GetOwner()))
{
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
if (bUseCharacterGravityDirection)
{
Force = Character->GetGravityDirection();
}
#endif
if (bUseCharacterGravityScale)
{
if (const UCharacterMovementComponent* CharacterMovementComponent = Character->
GetCharacterMovement())
{
Force *= CharacterMovementComponent->GetGravityZ();
}
}
}
Force *= RandomizedForceScale;
Force = ComponentTransform.InverseTransformVector(Force);
}
void FKawaiiPhysics_ExternalForce_Gravity::Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext,
const FTransform& BoneTM)
{
if (!CanApply(Bone))
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_ExternalForce_Gravity_Apply);
float ForceRate = 1.0f;
if (const auto Curve = ForceRateByBoneLengthRate.GetRichCurve(); !Curve->IsEmpty())
{
ForceRate = Curve->Eval(Bone.LengthRateFromRoot);
}
Bone.Location += 0.5f * Force * ForceRate * Node.DeltaTime * Node.DeltaTime;
#if ENABLE_ANIM_DEBUG
BoneForceMap.Add(Bone.BoneRef.BoneName, Force * ForceRate);
AnimDrawDebug(Bone, Node, PoseContext);
#endif
}
///
/// Curve
///
void FKawaiiPhysics_ExternalForce_Curve::InitMaxCurveTime()
{
if (const FRichCurve* CurveX = ForceCurve.GetRichCurve(0); CurveX && !CurveX->IsEmpty())
{
MaxCurveTime = FMath::Max(MaxCurveTime, CurveX->GetLastKey().Time);
}
if (const FRichCurve* CurveY = ForceCurve.GetRichCurve(1); CurveY && !CurveY->IsEmpty())
{
MaxCurveTime = FMath::Max(MaxCurveTime, CurveY->GetLastKey().Time);
}
if (const FRichCurve* CurveZ = ForceCurve.GetRichCurve(2); CurveZ && !CurveZ->IsEmpty())
{
MaxCurveTime = FMath::Max(MaxCurveTime, CurveZ->GetLastKey().Time);
}
}
void FKawaiiPhysics_ExternalForce_Curve::Initialize(const FAnimationInitializeContext& Context)
{
FKawaiiPhysics_ExternalForce::Initialize(Context);
InitMaxCurveTime();
}
void FKawaiiPhysics_ExternalForce_Curve::PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp)
{
Super::PreApply(Node, SkelComp);
#if WITH_EDITOR
InitMaxCurveTime();
#endif
PrevTime = Time;
if (CurveEvaluateType == EExternalForceCurveEvaluateType::Single)
{
Time += Node.DeltaTime * TimeScale;
if (MaxCurveTime > 0 && Time > MaxCurveTime)
{
Time = FMath::Fmod(Time, MaxCurveTime);
}
Force = ForceCurve.GetValue(Time) * RandomizedForceScale;
}
else
{
TArray<FVector> CurveValues;
const float SubStep = Node.DeltaTime * TimeScale / SubstepCount;
for (int i = 0; i < SubstepCount; ++i)
{
Time += SubStep;
if (MaxCurveTime > 0 && Time > MaxCurveTime)
{
Time = FMath::Fmod(Time, MaxCurveTime);
}
CurveValues.Add(ForceCurve.GetValue(Time));
}
Force = FVector::ZeroVector;
switch (CurveEvaluateType)
{
case EExternalForceCurveEvaluateType::Average:
for (const auto& CurveValue : CurveValues)
{
Force += CurveValue;
}
Force /= static_cast<float>(SubstepCount);
break;
case EExternalForceCurveEvaluateType::Max:
Force = FVector(FLT_MIN);
for (const auto& CurveValue : CurveValues)
{
Force = FVector::Max(Force, CurveValue);
}
break;
case EExternalForceCurveEvaluateType::Min:
Force = FVector(FLT_MAX);
for (const auto& CurveValue : CurveValues)
{
Force = FVector::Min(Force, CurveValue);
}
break;
default:
break;
}
Force *= RandomizedForceScale;
}
if (ExternalForceSpace == EExternalForceSpace::WorldSpace)
{
Force = ComponentTransform.InverseTransformVector(Force);
}
}
void FKawaiiPhysics_ExternalForce_Curve::Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext, const FTransform& BoneTM)
{
if (!CanApply(Bone))
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_ExternalForce_Curve_Apply);
float ForceRate = 1.0f;
if (const auto Curve = ForceRateByBoneLengthRate.GetRichCurve(); !Curve->IsEmpty())
{
ForceRate = Curve->Eval(Bone.LengthRateFromRoot);
}
if (ExternalForceSpace == EExternalForceSpace::BoneSpace)
{
const FVector BoneForce = BoneTM.TransformVector(Force);
Bone.Location += BoneForce * ForceRate * Node.DeltaTime;
#if ENABLE_ANIM_DEBUG
BoneForceMap.Add(Bone.BoneRef.BoneName, BoneForce * ForceRate);
#endif
}
else
{
Bone.Location += Force * ForceRate * Node.DeltaTime;
#if ENABLE_ANIM_DEBUG
BoneForceMap.Add(Bone.BoneRef.BoneName, Force * ForceRate);
#endif
}
#if ENABLE_ANIM_DEBUG
AnimDrawDebug(Bone, Node, PoseContext);
#endif
}
void FKawaiiPhysics_ExternalForce_Wind::PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp)
{
Super::PreApply(Node, SkelComp);
World = SkelComp ? SkelComp->GetWorld() : nullptr;
}
void FKawaiiPhysics_ExternalForce_Wind::Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext, const FTransform& BoneTM)
{
const FSceneInterface* Scene = World && World->Scene ? World->Scene : nullptr;
if (!CanApply(Bone) || !Scene)
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_ExternalForce_Wind_Apply);
float ForceRate = 1.0f;
if (const auto Curve = ForceRateByBoneLengthRate.GetRichCurve(); !Curve->IsEmpty())
{
ForceRate = Curve->Eval(Bone.LengthRateFromRoot);
}
FVector WindDirection = FVector::ZeroVector;
float WindSpeed, WindMinGust, WindMaxGust = 0.0f;
Scene->GetWindParameters(ComponentTransform.TransformPosition(Bone.PoseLocation), WindDirection,
WindSpeed, WindMinGust, WindMaxGust);
WindDirection = ComponentTransform.InverseTransformVector(WindDirection);
WindDirection *= WindSpeed;
Bone.Location += WindDirection * ForceRate * RandomizedForceScale * Node.DeltaTime;
#if ENABLE_ANIM_DEBUG
BoneForceMap.Add(Bone.BoneRef.BoneName, WindDirection * ForceRate * RandomizedForceScale);
AnimDrawDebug(Bone, Node, PoseContext);
#endif
}
@@ -0,0 +1,348 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "KawaiiPhysicsLibrary.h"
#include "AnimNode_KawaiiPhysics.h"
#include "BlueprintGameplayTagLibrary.h"
#include "KawaiiPhysicsExternalForce.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(KawaiiPhysicsLibrary)
DEFINE_LOG_CATEGORY_STATIC(LogKawaiiPhysicsLibrary, Verbose, All);
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::ConvertToKawaiiPhysics(const FAnimNodeReference& Node,
EAnimNodeReferenceConversionResult& Result)
{
return FAnimNodeReference::ConvertToType<FKawaiiPhysicsReference>(Node, Result);
}
bool UKawaiiPhysicsLibrary::CollectKawaiiPhysicsNodes(TArray<FKawaiiPhysicsReference>& Nodes,
UAnimInstance* AnimInstance,
const FGameplayTagContainer& FilterTags, bool bFilterExactMatch)
{
if (!ensure(AnimInstance && AnimInstance->GetClass()))
{
return false;
}
bool bResult = false;
if (const IAnimClassInterface* AnimClassInterface =
IAnimClassInterface::GetFromClass((AnimInstance->GetClass())))
{
const TArray<FStructProperty*>& AnimNodeProperties = AnimClassInterface->GetAnimNodeProperties();
for (int i = 0; i < AnimNodeProperties.Num(); ++i)
{
if (AnimNodeProperties[i]->Struct->
IsChildOf(FKawaiiPhysicsReference::FInternalNodeType::StaticStruct()))
{
EAnimNodeReferenceConversionResult Result;
FKawaiiPhysicsReference KawaiiPhysicsReference = ConvertToKawaiiPhysics(
FAnimNodeReference(AnimInstance, i), Result);
if (Result == EAnimNodeReferenceConversionResult::Succeeded)
{
auto& Tag = KawaiiPhysicsReference.GetAnimNode<FAnimNode_KawaiiPhysics>().KawaiiPhysicsTag;
if (FilterTags.IsEmpty() || UBlueprintGameplayTagLibrary::MatchesAnyTags(
Tag, FilterTags, bFilterExactMatch))
{
Nodes.Add(KawaiiPhysicsReference);
bResult = true;
}
}
}
}
}
return bResult;
}
bool UKawaiiPhysicsLibrary::CollectKawaiiPhysicsNodes(TArray<FKawaiiPhysicsReference>& Nodes,
USkeletalMeshComponent* MeshComp,
const FGameplayTagContainer& FilterTags, bool bFilterExactMatch)
{
if (!ensure(MeshComp))
{
return false;
}
const int NodeNum = Nodes.Num();
if (UAnimInstance* AnimInstance = MeshComp->GetAnimInstance())
{
CollectKawaiiPhysicsNodes(Nodes, AnimInstance, FilterTags,
bFilterExactMatch);
}
const TArray<UAnimInstance*>& LinkedInstances =
const_cast<const USkeletalMeshComponent*>(MeshComp)->GetLinkedAnimInstances();
for (UAnimInstance* LinkedInstance : LinkedInstances)
{
CollectKawaiiPhysicsNodes(Nodes, LinkedInstance, FilterTags,
bFilterExactMatch);
}
if (UAnimInstance* PostProcessAnimInstance = MeshComp->GetPostProcessInstance())
{
CollectKawaiiPhysicsNodes(Nodes, PostProcessAnimInstance, FilterTags,
bFilterExactMatch);
}
return NodeNum != Nodes.Num();
}
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::ResetDynamics(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("ResetDynamics"),
[](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
InKawaiiPhysics.ResetDynamics(ETeleportType::ResetPhysics);
});
return KawaiiPhysics;
}
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::SetRootBoneName(const FKawaiiPhysicsReference& KawaiiPhysics,
FName& RootBoneName)
{
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("SetRootBoneName"),
[RootBoneName](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
InKawaiiPhysics.RootBone = FBoneReference(RootBoneName);
});
return KawaiiPhysics;
}
FName UKawaiiPhysicsLibrary::GetRootBoneName(const FKawaiiPhysicsReference& KawaiiPhysics)
{
FName RootBoneName;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("GetRootBoneName"),
[&RootBoneName](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
RootBoneName = InKawaiiPhysics.RootBone.BoneName;
});
return RootBoneName;
}
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::SetExcludeBoneNames(const FKawaiiPhysicsReference& KawaiiPhysics,
TArray<FName>& ExcludeBoneNames)
{
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("SetExcludeBoneNames"),
[&ExcludeBoneNames](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
InKawaiiPhysics.ExcludeBones.Empty();
for (auto& ExcludeBoneName : ExcludeBoneNames)
{
InKawaiiPhysics.ExcludeBones.Add(FBoneReference(ExcludeBoneName));
}
});
return KawaiiPhysics;
}
TArray<FName> UKawaiiPhysicsLibrary::GetExcludeBoneNames(const FKawaiiPhysicsReference& KawaiiPhysics)
{
TArray<FName> ExcludeBoneNames;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("GetExcludeBoneNames"),
[&ExcludeBoneNames](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
for (auto& ExcludeBone : InKawaiiPhysics.ExcludeBones)
{
ExcludeBoneNames.Add(ExcludeBone.BoneName);
}
});
return ExcludeBoneNames;
}
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::AddExternalForceWithExecResult(
EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
FInstancedStruct& ExternalForce, UObject* Owner)
{
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
if (AddExternalForce(KawaiiPhysics, ExternalForce, Owner))
{
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
return KawaiiPhysics;
}
bool UKawaiiPhysicsLibrary::AddExternalForce(const FKawaiiPhysicsReference& KawaiiPhysics,
FInstancedStruct& ExternalForce, UObject* Owner, bool bIsOneShot)
{
bool bResult = false;
if (ExternalForce.IsValid())
{
if (auto* ExternalForcePtr = ExternalForce.GetMutablePtr<FKawaiiPhysics_ExternalForce>())
{
ExternalForcePtr->ExternalOwner = Owner;
ExternalForcePtr->bIsOneShot = bIsOneShot;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("AddExternalForce"),
[&](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
InKawaiiPhysics.ExternalForces.Add(ExternalForce);
});
bResult = true;
}
}
return bResult;
}
bool UKawaiiPhysicsLibrary::AddExternalForcesToComponent(USkeletalMeshComponent* MeshComp,
TArray<FInstancedStruct>& ExternalForces,
UObject* Owner,
FGameplayTagContainer& FilterTags,
bool bFilterExactMatch, bool bIsOneShot)
{
bool bResult = false;
TArray<FKawaiiPhysicsReference> KawaiiPhysicsReferences;
CollectKawaiiPhysicsNodes(KawaiiPhysicsReferences, MeshComp, FilterTags, bFilterExactMatch);
for (auto& KawaiiPhysicsReference : KawaiiPhysicsReferences)
{
for (auto& AExternalForce : ExternalForces)
{
if (AExternalForce.IsValid())
{
if (AddExternalForce(KawaiiPhysicsReference, AExternalForce, Owner, bIsOneShot))
{
bResult = true;
}
}
}
}
return bResult;
}
bool UKawaiiPhysicsLibrary::RemoveExternalForcesFromComponent(USkeletalMeshComponent* MeshComp, UObject* Owner,
FGameplayTagContainer& FilterTags, bool bFilterExactMatch)
{
bool bResult = false;
TArray<FKawaiiPhysicsReference> KawaiiPhysicsReferences;
CollectKawaiiPhysicsNodes(KawaiiPhysicsReferences, MeshComp, FilterTags, bFilterExactMatch);
for (auto& KawaiiPhysicsReference : KawaiiPhysicsReferences)
{
KawaiiPhysicsReference.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("RemoveExternalForce"),
[&](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
const int32 NumRemoved = InKawaiiPhysics.ExternalForces.RemoveAll([&](FInstancedStruct& InstancedStruct)
{
const auto* ExternalForcePtr = InstancedStruct.GetMutablePtr<FKawaiiPhysics_ExternalForce>();
return ExternalForcePtr && ExternalForcePtr->ExternalOwner == Owner;
});
if (NumRemoved > 0)
{
bResult = true;
}
});
}
return bResult;
}
DEFINE_FUNCTION(UKawaiiPhysicsLibrary::execSetExternalForceWildcardProperty)
{
P_GET_ENUM_REF(EKawaiiPhysicsAccessExternalForceResult, ExecResult);
P_GET_STRUCT_REF(FKawaiiPhysicsReference, KawaiiPhysics);
P_GET_PROPERTY(FIntProperty, ExternalForceIndex);
P_GET_STRUCT_REF(FName, PropertyName);
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
// Read wildcard Value input.
Stack.MostRecentPropertyAddress = nullptr;
Stack.MostRecentPropertyContainer = nullptr;
Stack.StepCompiledIn<FStructProperty>(nullptr);
const FProperty* ValueProp = CastField<FProperty>(Stack.MostRecentProperty);
void* ValuePtr = Stack.MostRecentPropertyAddress;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("GetExternalForceWildcardProperty"),
[&ExecResult, &ExternalForceIndex, &PropertyName, &ValuePtr](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
if (InKawaiiPhysics.ExternalForces.IsValidIndex(ExternalForceIndex) &&
InKawaiiPhysics.ExternalForces[ExternalForceIndex].IsValid())
{
const auto* ScriptStruct = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetScriptStruct();
auto& Force = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetMutable<
FKawaiiPhysics_ExternalForce>();
if (const FProperty* Property = FindFProperty<FProperty>(ScriptStruct, PropertyName))
{
Property->CopyCompleteValue(Property->ContainerPtrToValuePtr<uint8>(&Force), ValuePtr);
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
}
});
P_FINISH;
}
DEFINE_FUNCTION(UKawaiiPhysicsLibrary::execGetExternalForceWildcardProperty)
{
P_GET_ENUM_REF(EKawaiiPhysicsAccessExternalForceResult, ExecResult);
P_GET_STRUCT_REF(FKawaiiPhysicsReference, KawaiiPhysics);
P_GET_PROPERTY(FIntProperty, ExternalForceIndex);
P_GET_STRUCT_REF(FName, PropertyName);
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
// Read wildcard Value input.
Stack.MostRecentPropertyAddress = nullptr;
Stack.MostRecentPropertyContainer = nullptr;
Stack.StepCompiledIn<FStructProperty>(nullptr);
const FProperty* ValueProp = CastField<FProperty>(Stack.MostRecentProperty);
void* ValuePtr = Stack.MostRecentPropertyAddress;
void* Result = nullptr;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("GetExternalForceWildcardProperty"),
[&Result, &ExecResult, &ExternalForceIndex, &PropertyName](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
if (InKawaiiPhysics.ExternalForces.IsValidIndex(ExternalForceIndex) &&
InKawaiiPhysics.ExternalForces[ExternalForceIndex].IsValid())
{
const auto* ScriptStruct = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetScriptStruct();
auto& Force = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetMutable<
FKawaiiPhysics_ExternalForce>();
if (const FProperty* Property = FindFProperty<FProperty>(ScriptStruct, PropertyName))
{
Result = Property->ContainerPtrToValuePtr<void>(&Force);
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
}
});
P_FINISH;
if (ValuePtr && Result)
{
P_NATIVE_BEGIN;
ValueProp->CopyCompleteValue(ValuePtr, Result);
P_NATIVE_END;
}
}
@@ -0,0 +1,203 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#include "KawaiiPhysicsLimitsDataAsset.h"
#include "AnimNode_KawaiiPhysics.h"
#include "KawaiiPhysics.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(KawaiiPhysicsLimitsDataAsset)
DEFINE_LOG_CATEGORY(LogKawaiiPhysics);
struct FCollisionLimitDataCustomVersion
{
enum Type
{
// FNameからFBoneReferenceに移行
ChangeToBoneReference = 0,
DeprecateLimitData,
// ------------------------------------------------------
VersionPlusOne,
LatestVersion = VersionPlusOne - 1
};
// The GUID for this custom version number
const static FGuid GUID;
private:
FCollisionLimitDataCustomVersion()
{
}
};
const FGuid FCollisionLimitDataCustomVersion::GUID(0x3A1F7B2E, 0x7B9D6E8C, 0x4C2A9F1D, 0x85B3E4F1);
FCustomVersionRegistration GRegisterCollisionLimitDataCustomVersion(FCollisionLimitDataCustomVersion::GUID,
FCollisionLimitDataCustomVersion::LatestVersion,
TEXT("CollisionLimitData"));
#if WITH_EDITOR
template <typename CollisionLimitType>
void UpdateCollisionLimit(TArray<CollisionLimitType>& CollisionLimitsData, const CollisionLimitType& NewLimit)
{
for (auto& LimitData : CollisionLimitsData)
{
if (LimitData.Guid == NewLimit.Guid)
{
LimitData = NewLimit;
break;
}
}
}
void UKawaiiPhysicsLimitsDataAsset::UpdateLimit(FCollisionLimitBase* Limit)
{
switch (Limit->Type)
{
case ECollisionLimitType::Spherical:
UpdateCollisionLimit(SphericalLimits, *static_cast<FSphericalLimit*>(Limit));
break;
case ECollisionLimitType::Capsule:
UpdateCollisionLimit(CapsuleLimits, *static_cast<FCapsuleLimit*>(Limit));
break;
case ECollisionLimitType::Box:
UpdateCollisionLimit(BoxLimits, *static_cast<FBoxLimit*>(Limit));
break;
case ECollisionLimitType::Planar:
UpdateCollisionLimit(PlanarLimits, *static_cast<FPlanarLimit*>(Limit));
break;
case ECollisionLimitType::None:
break;
default:
break;
}
MarkPackageDirty();
}
template <typename CollisionLimitDataType, typename CollisionLimitType>
void SyncCollisionLimits(const TArray<CollisionLimitDataType>& CollisionLimitData,
TArray<CollisionLimitType>& CollisionLimits)
{
CollisionLimits.Empty();
for (const auto& Data : CollisionLimitData)
{
CollisionLimits.Add(Data.Convert());
}
}
void UKawaiiPhysicsLimitsDataAsset::Sync()
{
SyncCollisionLimits(SphericalLimitsData, SphericalLimits);
SyncCollisionLimits(CapsuleLimitsData, CapsuleLimits);
SyncCollisionLimits(BoxLimitsData, BoxLimits);
SyncCollisionLimits(PlanarLimitsData, PlanarLimits);
}
void UKawaiiPhysicsLimitsDataAsset::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
{
Super::PostEditChangeChainProperty(PropertyChangedEvent);
FName ArrayPropertyName = PropertyChangedEvent.MemberProperty
? PropertyChangedEvent.MemberProperty->GetFName()
: NAME_None;
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet &&
PropertyChangedEvent.PropertyChain.GetActiveMemberNode())
{
ArrayPropertyName = PropertyChangedEvent.PropertyChain.GetActiveMemberNode()->GetValue()->GetFName();
}
auto UpdateLimits = [&](auto& Limits)
{
int32 ArrayIndex = PropertyChangedEvent.GetArrayIndex(ArrayPropertyName.ToString());
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd ||
PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet)
{
Limits[ArrayIndex].SourceType = ECollisionSourceType::DataAsset;
}
else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Duplicate)
{
Limits[ArrayIndex].Guid = FGuid::NewGuid();
}
};
if (ArrayPropertyName == GET_MEMBER_NAME_CHECKED(UKawaiiPhysicsLimitsDataAsset, SphericalLimits))
{
UpdateLimits(SphericalLimits);
}
else if (ArrayPropertyName == GET_MEMBER_NAME_CHECKED(UKawaiiPhysicsLimitsDataAsset, CapsuleLimits))
{
UpdateLimits(CapsuleLimits);
}
else if (ArrayPropertyName == GET_MEMBER_NAME_CHECKED(UKawaiiPhysicsLimitsDataAsset, BoxLimits))
{
UpdateLimits(BoxLimits);
}
else if (ArrayPropertyName == GET_MEMBER_NAME_CHECKED(UKawaiiPhysicsLimitsDataAsset, PlanarLimits))
{
UpdateLimits(PlanarLimits);
}
OnLimitsChanged.Broadcast(PropertyChangedEvent);
}
#endif
#if WITH_EDITORONLY_DATA
void UKawaiiPhysicsLimitsDataAsset::Serialize(FStructuredArchiveRecord Record)
{
Super::Serialize(Record);
Record.GetUnderlyingArchive().UsingCustomVersion(FCollisionLimitDataCustomVersion::GUID);
}
#endif
USkeleton* UKawaiiPhysicsLimitsDataAsset::GetSkeleton(bool& bInvalidSkeletonIsError,
const IPropertyHandle* PropertyHandle)
{
#if WITH_EDITORONLY_DATA
return Skeleton;
#else
return nullptr;
#endif
}
void UKawaiiPhysicsLimitsDataAsset::PostLoad()
{
Super::PostLoad();
if (GetLinkerCustomVersion(FCollisionLimitDataCustomVersion::GUID) <
FCollisionLimitDataCustomVersion::ChangeToBoneReference)
{
#if WITH_EDITORONLY_DATA
for (auto& Data : SphericalLimitsData)
{
Data.DrivingBoneReference = FBoneReference(Data.DrivingBoneName);
}
for (auto& Data : CapsuleLimitsData)
{
Data.DrivingBoneReference = FBoneReference(Data.DrivingBoneName);
}
for (auto& Data : BoxLimitsData)
{
Data.DrivingBoneReference = FBoneReference(Data.DrivingBoneName);
}
for (auto& Data : PlanarLimitsData)
{
Data.DrivingBoneReference = FBoneReference(Data.DrivingBoneName);
}
UE_LOG(LogKawaiiPhysics, Log, TEXT("Update : BoneName -> BoneReference (%s)"), *this->GetName());
#endif
}
if (GetLinkerCustomVersion(FCollisionLimitDataCustomVersion::GUID) <
FCollisionLimitDataCustomVersion::DeprecateLimitData)
{
#if WITH_EDITORONLY_DATA
Sync();
UE_LOG(LogKawaiiPhysics, Log, TEXT("Update : Deprecate LimitData (%s)"), *this->GetName());
#endif
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleInterface.h"
DECLARE_LOG_CATEGORY_EXTERN(LogKawaiiPhysics, Log, All);
class FKawaiiPhysicsModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
@@ -0,0 +1,124 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "AnimNode_KawaiiPhysics.h"
#include "Engine/DataAsset.h"
#include "Interfaces/Interface_BoneReferenceSkeletonProvider.h"
#include "KawaiiPhysicsBoneConstraintsDataAsset.generated.h"
/**
* Struct representing the data for modifying bone constraints in KawaiiPhysics.
*/
USTRUCT(BlueprintType)
struct KAWAIIPHYSICS_API FModifyBoneConstraintData
{
GENERATED_BODY()
/** Name of the first bone (deprecated) */
UPROPERTY()
FName BoneName1;
/** Name of the second bone (deprecated) */
UPROPERTY()
FName BoneName2;
/** Reference to the first bone */
UPROPERTY(EditAnywhere, category = "KawaiiPhysics")
FBoneReference BoneReference1;
/** Reference to the second bone */
UPROPERTY(EditAnywhere, category = "KawaiiPhysics")
FBoneReference BoneReference2;
/** Whether to override the compliance type */
UPROPERTY(EditAnywhere, category = "KawaiiPhysics", meta=(InlineEditConditionToggle))
bool bOverrideCompliance = false;
/** The compliance type to use if overriding */
UPROPERTY(EditAnywhere, category = "KawaiiPhysics", meta=(EditCondition="bOverrideCompliance"))
EXPBDComplianceType ComplianceType = EXPBDComplianceType::Leather;
/**
* Updates the bone constraint data with the given constraint.
* @param BoneConstraint The bone constraint to update from.
*/
void Update(const FModifyBoneConstraint& BoneConstraint);
};
/**
* Struct representing a set of regex patterns for bones in KawaiiPhysics.
*/
USTRUCT(BlueprintType)
struct FRegexPatternBoneSet
{
GENERATED_BODY()
/** Regex pattern for the first bone */
UPROPERTY(EditAnywhere, Category="Helper")
FString RegexPatternBone1;
/** Regex pattern for the second bone */
UPROPERTY(EditAnywhere, Category="Helper")
FString RegexPatternBone2;
};
/**
* Data asset for managing bone constraints in KawaiiPhysics.
*/
UCLASS(Blueprintable)
class KAWAIIPHYSICS_API UKawaiiPhysicsBoneConstraintsDataAsset : public UDataAsset,
public IBoneReferenceSkeletonProvider
{
GENERATED_BODY()
public:
/** Array of bone constraint data */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bone Constraint (Experimental)",
meta=(TitleProperty="{BoneReference1} - {BoneReference2}"))
TArray<FModifyBoneConstraintData> BoneConstraintsData;
#if WITH_EDITORONLY_DATA
/** List of regex patterns for bones */
UPROPERTY(EditAnywhere, Category="Helper")
TArray<FRegexPatternBoneSet> RegexPatternList;
/** Preview skeleton for editor */
UPROPERTY(EditAnywhere, Category = "Skeleton")
TSoftObjectPtr<USkeleton> PreviewSkeleton;
/** List of preview bones */
UPROPERTY(VisibleAnywhere, Category = "Skeleton", meta= (EditCondition=false))
TArray<FBoneReference> PreviewBoneList;
/** String representation of the preview bone list */
UPROPERTY()
FString PreviewBoneListString;
#endif
// Begin UObject Interface.
virtual void Serialize(FStructuredArchiveRecord Record) override;
virtual void PostLoad() override;
// End UObject Interface.
// IBoneReferenceSkeletonProvider interface
virtual USkeleton* GetSkeleton(bool& bInvalidSkeletonIsError, const IPropertyHandle* PropertyHandle) override;
/** Generates bone constraints based on the current data */
TArray<FModifyBoneConstraint> GenerateBoneConstraints();
#if WITH_EDITOR
/** Applies regex patterns to the bone constraints */
UFUNCTION(BlueprintCallable, CallInEditor, Category="Helper")
void ApplyRegex();
/** Updates the preview bone list */
void UpdatePreviewBoneList();
/** Handles property changes in the editor */
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};
@@ -0,0 +1,50 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "AnimNode_KawaiiPhysics.h"
#include "KawaiiPhysicsCustomExternalForce.generated.h"
UCLASS(Abstract, Blueprintable, EditInlineNew, CollapseCategories)
class KAWAIIPHYSICS_API UKawaiiPhysics_CustomExternalForce : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(DisplayPriority=1), Category="KawaiiPhysics|CustomExternalForce")
bool bIsEnabled = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(DisplayPriority=1), Category="KawaiiPhysics|CustomExternalForce")
bool bDrawDebug = false;
public:
UFUNCTION(BlueprintNativeEvent)
void PreApply(UPARAM(ref) FAnimNode_KawaiiPhysics& Node,
const USkeletalMeshComponent* SkelComp);
virtual void PreApply_Implementation(
UPARAM(ref) FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp)PURE_VIRTUAL(,);
UFUNCTION(BlueprintNativeEvent)
void Apply(UPARAM(ref) FAnimNode_KawaiiPhysics& Node, int32 ModifyBoneIndex,
const USkeletalMeshComponent* SkelComp, const FTransform& BoneTransform);
virtual void Apply_Implementation(
UPARAM(ref) FAnimNode_KawaiiPhysics& Node, int32 ModifyBoneIndex, const USkeletalMeshComponent* SkelComp,
const FTransform& BoneTransform)
{
}
UFUNCTION(BlueprintCallable, Category="KawaiiPhysics|CustomExternalForce")
virtual bool IsDebugEnabled()
{
#if ENABLE_ANIM_DEBUG
if (CVarAnimNodeKawaiiPhysicsDebug.GetValueOnAnyThread())
{
return bDrawDebug;
}
#endif
return false;
}
};
@@ -0,0 +1,448 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "AnimNode_KawaiiPhysics.h"
#include "SceneManagement.h"
#include "Curves/CurveVector.h"
#include "KawaiiPhysicsExternalForce.generated.h"
/**
* Enum representing the space in which external forces are simulated.
*/
UENUM(BlueprintType)
enum class EExternalForceSpace : uint8
{
/** Simulate in component space. Moving the entire skeletal mesh will have no affect on velocities */
ComponentSpace,
/** Simulate in world space. Moving the skeletal mesh will generate velocity changes */
WorldSpace,
/** Simulate in another bone space. Moving the entire skeletal mesh and individually modifying the base bone will have no affect on velocities */
BoneSpace,
};
/**
* Enum representing the evaluation type for external force curves.
*/
UENUM(BlueprintType)
enum class EExternalForceCurveEvaluateType : uint8
{
/** Evaluate the curve at a single point */
Single,
/** Evaluate the curve by averaging multiple points */
Average,
/** Evaluate the curve by taking the maximum value from multiple points */
Max,
/** Evaluate the curve by taking the minimum value from multiple points */
Min
};
///
/// Base
///
USTRUCT(BlueprintType)
struct KAWAIIPHYSICS_API FKawaiiPhysics_ExternalForce
{
GENERATED_BODY()
/** Whether the external force is enabled */
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(DisplayPriority=1), Category="KawaiiPhysics|ExternalForce")
bool bIsEnabled = true;
/** Whether to draw debug information for the external force */
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(DisplayPriority=1), Category="KawaiiPhysics|ExternalForce")
bool bDrawDebug = false;
/**
* 外力を適応するボーンを指定(=指定しなかったボーンには適応しない)
* Specify the bones to which the external force will be applied (= the force will not be applied to bones that are not specified)
*/
UPROPERTY(EditAnywhere, meta=(DisplayPriority=1), Category="KawaiiPhysics|ExternalForce")
TArray<FBoneReference> ApplyBoneFilter;
/**
* 外力を適応しないボーンを指定
* Specify the bones to which the external force will be NOT applied
*/
UPROPERTY(EditAnywhere, meta=(DisplayPriority=1), Category="KawaiiPhysics|ExternalForce")
TArray<FBoneReference> IgnoreBoneFilter;
/** The space in which the external force is simulated */
UPROPERTY(EditAnywhere, meta=(DisplayPriority=1, EditCondition=bCanSelectForceSpace, EditConditionHides),
Category="KawaiiPhysics|ExternalForce")
EExternalForceSpace ExternalForceSpace = EExternalForceSpace::WorldSpace;
/** Range for randomizing the force scale */
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(DisplayPriority=1), Category="KawaiiPhysics|ExternalForce")
FFloatInterval RandomForceScaleRange = FFloatInterval(1.0f, 1.0f);
/** Owner of the external force */
UPROPERTY()
TObjectPtr<UObject> ExternalOwner;
/** Whether the external force is applied only once */
UPROPERTY()
bool bIsOneShot = false;
#if ENABLE_ANIM_DEBUG
/** Length of the debug arrow */
float DebugArrowLength = 5.0f;
/** Size of the debug arrow */
float DebugArrowSize = 1.0f;
/** Offset for the debug arrow */
FVector DebugArrowOffset = FVector::Zero();
/** Map of bone names to forces for debugging */
TMap<FName, FVector> BoneForceMap;
#endif
protected:
/** Randomized scale of the force */
UPROPERTY()
float RandomizedForceScale = 0.0f;
/** The force vector */
UPROPERTY()
FVector Force = FVector::Zero();
/** Transform of the component */
UPROPERTY()
FTransform ComponentTransform;
/** Whether the force space can be selected */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
bool bCanSelectForceSpace = true;
public:
virtual ~FKawaiiPhysics_ExternalForce() = default;
virtual void Initialize(const FAnimationInitializeContext& Context)
{
}
/** Prepares the external force before applying it */
virtual void PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp)
{
ComponentTransform = SkelComp->GetComponentTransform();
RandomizedForceScale = FMath::RandRange(RandomForceScaleRange.Min, RandomForceScaleRange.Max);
}
/** Applies the external force to a bone */
virtual void Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext, const FTransform& BoneTM = FTransform::Identity)
{
}
/** Finalizes the external force after applying it */
virtual void PostApply(FAnimNode_KawaiiPhysics& Node)
{
if (bIsOneShot)
{
Node.ExternalForces.RemoveAll([&](FInstancedStruct& InstancedStruct)
{
const auto* ExternalForcePtr = InstancedStruct.GetMutablePtr<FKawaiiPhysics_ExternalForce>();
return ExternalForcePtr == this;
});
}
}
/** Checks if debug information should be drawn */
virtual bool IsDebugEnabled(bool bInPersona = false)
{
if (bInPersona)
{
return bDrawDebug && bIsEnabled;
}
#if ENABLE_ANIM_DEBUG
if (CVarAnimNodeKawaiiPhysicsDebug.GetValueOnAnyThread())
{
return bDrawDebug && bIsEnabled;
}
#endif
return false;
}
#if ENABLE_ANIM_DEBUG
/** Draws debug information for the external force */
virtual void AnimDrawDebug(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext)
{
if (IsDebugEnabled() && !Force.IsZero())
{
const auto AnimInstanceProxy = PoseContext.AnimInstanceProxy;
const FVector ModifyRootBoneLocationWS = AnimInstanceProxy->GetComponentTransform().TransformPosition(
Bone.Location);
AnimInstanceProxy->AnimDrawDebugDirectionalArrow(
ModifyRootBoneLocationWS + DebugArrowOffset,
ModifyRootBoneLocationWS + DebugArrowOffset + BoneForceMap.Find(Bone.BoneRef.BoneName)->GetSafeNormal()
*
DebugArrowLength,
DebugArrowSize, FColor::Red, false, 0.f, 2);
}
}
#endif
#if WITH_EDITOR
/** Draws debug information for the external force in edit mode */
virtual void AnimDrawDebugForEditMode(const FKawaiiPhysicsModifyBone& ModifyBone,
const FAnimNode_KawaiiPhysics& Node, FPrimitiveDrawInterface* PDI)
{
if (IsDebugEnabled(true) && CanApply(ModifyBone) && !Force.IsNearlyZero() && BoneForceMap.Contains(
ModifyBone.BoneRef.BoneName))
{
const FTransform ArrowTransform = FTransform(
BoneForceMap.Find(ModifyBone.BoneRef.BoneName)->GetSafeNormal().ToOrientationRotator(),
ModifyBone.Location + DebugArrowOffset);
DrawDirectionalArrow(PDI, ArrowTransform.ToMatrixNoScale(), FColor::Red, DebugArrowLength, DebugArrowSize,
SDPG_Foreground, 1.0f);
}
}
#endif
protected:
/** Checks if the external force can be applied to a bone */
bool CanApply(const FKawaiiPhysicsModifyBone& Bone) const
{
if (!ApplyBoneFilter.IsEmpty() && !ApplyBoneFilter.Contains(Bone.BoneRef))
{
return false;
}
if (!IgnoreBoneFilter.IsEmpty() && IgnoreBoneFilter.Contains(Bone.BoneRef))
{
return false;
}
return true;
}
};
///
/// Basic
///
USTRUCT(BlueprintType, DisplayName = "Basic")
struct KAWAIIPHYSICS_API FKawaiiPhysics_ExternalForce_Basic : public FKawaiiPhysics_ExternalForce
{
GENERATED_BODY()
/** Direction of the force */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
FVector ForceDir = FVector::Zero();
/**
* 各ボーンに適用するForce Rateを補正。
* 「RootBoneから特定のボーンまでの長さ / RootBoneから末端のボーンまでの長さ」(0.0~1.0)の値におけるカーブの値をForceRateに乗算
* Corrects the Force Rate applied to each bone.
* Multiplies the ForceRate by the curve value for "Length from RootBone to specific bone / Length from RootBone to end bone" (0.0~1.0)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
FRuntimeFloatCurve ForceRateByBoneLengthRate;
/** Interval for applying the force */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
float Interval = 0.0f;
virtual void PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp) override;
virtual void Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext,
const FTransform& BoneTM = FTransform::Identity) override;
private:
/** Current time */
UPROPERTY()
float Time = 0.0f;
/** Previous time */
UPROPERTY()
float PrevTime = 0.0f;
};
///
/// Gravity
///
USTRUCT(BlueprintType, DisplayName = "Gravity")
struct KAWAIIPHYSICS_API FKawaiiPhysics_ExternalForce_Gravity : public FKawaiiPhysics_ExternalForce
{
GENERATED_BODY()
FKawaiiPhysics_ExternalForce_Gravity()
{
bCanSelectForceSpace = false;
ExternalForceSpace = EExternalForceSpace::WorldSpace;
}
/**
* 各ボーンに適用するForce Rateを補正。
* 「RootBoneから特定のボーンまでの長さ / RootBoneから末端のボーンまでの長さ」(0.0~1.0)の値におけるカーブの値をForceRateに乗算
* Corrects the Force Rate applied to each bone.
* Multiplies the ForceRate by the curve value for "Length from RootBone to specific bone / Length from RootBone to end bone" (0.0~1.0)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
FRuntimeFloatCurve ForceRateByBoneLengthRate;
/**
* Character側で設定されたCustomGravityDirectionを使用するフラグ(UE5.4以降)
* Flag to use CustomGravityDirection set on the Character side (UE5.4 and later)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
bool bUseCharacterGravityDirection = false;
/**
* Character側で設定されたGravityScaleを使用するフラグ
* Flag to use GravityScale set on the Character side
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
bool bUseCharacterGravityScale = false;
/**
* Direction to override the gravity.
* This direction is used when bUseOverrideGravityDirection is true.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite,
meta = (EditCondition = "bUseOverrideGravityDirection"), Category="KawaiiPhysics|ExternalForce")
FVector OverrideGravityDirection = FVector::Zero();
/**
* Flag to determine whether to use the override gravity direction.
* If true, the gravity direction will be overridden by OverrideGravityDirection.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (InlineEditConditionToggle),
Category="KawaiiPhysics|ExternalForce")
bool bUseOverrideGravityDirection = false;
virtual void PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp) override;
virtual void Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext,
const FTransform& BoneTM = FTransform::Identity) override;
};
///
/// Curve
///
USTRUCT(BlueprintType, DisplayName = "Curve")
struct KAWAIIPHYSICS_API FKawaiiPhysics_ExternalForce_Curve : public FKawaiiPhysics_ExternalForce
{
GENERATED_BODY()
/**
* 時間に応じて変化する外力をカーブで設定。X軸:Time Y軸:Force
* Set the external force that changes over time using a curve. X-axis: Time Y-axis: Force
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (XAxisName="Time", YAxisName="Force"),
Category="KawaiiPhysics|ExternalForce")
FRuntimeVectorCurve ForceCurve;
/**
* カーブの評価方式。
* Single以外に設定した場合:前フレームからの経過時間をSubstepCountで分割し、
* 分割後の各時間におけるカーブの値の平均・最大値・最小値を外力として使用
* Curve evaluation method
* If set to anything other than Single: The time elapsed from the previous frame is divided by SubstepCount,
* and the Average, Maximum, or Minimum values of the curve at each time point after division are used as external forces.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
EExternalForceCurveEvaluateType CurveEvaluateType = EExternalForceCurveEvaluateType::Single;
/**
* 経過時間の分割数
* Number of divisions of elapsed time
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite,
meta=(EditCondition="CurveEvaluateType!=EExternalForceCurveEvaluateType::Single"),
Category="KawaiiPhysics|ExternalForce")
int SubstepCount = 10;
/**
* Scale factor for the time.
* This value is used to scale the time for the external force.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category ="KawaiiPhysics|ExternalForce")
float TimeScale = 1.0f;
/**
* 各ボーンに適用するForce Rateを補正。
* 「RootBoneから特定のボーンまでの長さ / RootBoneから末端のボーンまでの長さ」(0.0~1.0)の値におけるカーブの値をForceRateに乗算
* Corrects the Force Rate applied to each bone.
* Multiplies the ForceRate by the curve value for "Length from RootBone to specific bone / Length from RootBone to end bone" (0.0~1.0)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
FRuntimeFloatCurve ForceRateByBoneLengthRate;
private:
/**
* Current time.
* This value is used to track the current time for the external force.
*/
UPROPERTY()
float Time = 0.0f;
/**
* Previous time.
* This value is used to track the previous time for the external force.
*/
UPROPERTY()
float PrevTime = 0.0f;
/**
* Maximum curve time.
* This value is used to track the maximum time for the force curve.
*/
UPROPERTY()
float MaxCurveTime = 0.0f;
public:
/**
* Initializes the maximum curve time.
* This function calculates the maximum time value from the ForceCurve and sets it to MaxCurveTime.
*/
void InitMaxCurveTime();
virtual void Initialize(const FAnimationInitializeContext& Context) override;
virtual void PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp) override;
virtual void Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext,
const FTransform& BoneTM = FTransform::Identity) override;
};
///
/// Wind
///
USTRUCT(BlueprintType, DisplayName = "Wind")
struct KAWAIIPHYSICS_API FKawaiiPhysics_ExternalForce_Wind : public FKawaiiPhysics_ExternalForce
{
GENERATED_BODY()
FKawaiiPhysics_ExternalForce_Wind()
{
bCanSelectForceSpace = false;
ExternalForceSpace = EExternalForceSpace::WorldSpace;
}
/**
* 各ボーンに適用するForce Rateを補正。
* 「RootBoneから特定のボーンまでの長さ / RootBoneから末端のボーンまでの長さ」(0.0~1.0)の値におけるカーブの値をForceRateに乗算
* Corrects the Force Rate applied to each bone.
* Multiplies the ForceRate by the curve value for "Length from RootBone to specific bone / Length from RootBone to end bone" (0.0~1.0)
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="KawaiiPhysics|ExternalForce")
FRuntimeFloatCurve ForceRateByBoneLengthRate;
private:
/**
* Pointer to the world.
* This is used to access the world context for the external force.
*/
UPROPERTY()
TObjectPtr<UWorld> World;
public:
virtual void PreApply(FAnimNode_KawaiiPhysics& Node, const USkeletalMeshComponent* SkelComp) override;
virtual void Apply(FKawaiiPhysicsModifyBone& Bone, FAnimNode_KawaiiPhysics& Node,
const FComponentSpacePoseContext& PoseContext,
const FTransform& BoneTM = FTransform::Identity) override;
};
@@ -0,0 +1,584 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "AnimNode_KawaiiPhysics.h"
#include "KawaiiPhysicsExternalForce.h"
#include "Animation/AnimNodeReference.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "KawaiiPhysicsLibrary.generated.h"
UENUM()
enum class EKawaiiPhysicsAccessExternalForceResult : uint8
{
Valid,
NotValid,
};
#define KAWAIIPHYSICS_VALUE_SETTER(PropertyType, PropertyName) \
{ \
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>( \
TEXT("Set" #PropertyName), \
[PropertyName](FAnimNode_KawaiiPhysics& InKawaiiPhysics) { \
InKawaiiPhysics.PropertyName = PropertyName; \
}); \
return KawaiiPhysics; \
}
#define KAWAIIPHYSICS_VALUE_GETTER(PropertyType, PropertyName) \
{ \
PropertyType Value; \
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>( \
TEXT("Get" #PropertyName), \
[&Value](FAnimNode_KawaiiPhysics& InKawaiiPhysics) { \
Value = InKawaiiPhysics.PropertyName; \
}); \
return Value; \
}
USTRUCT(BlueprintType)
struct FKawaiiPhysicsReference : public FAnimNodeReference
{
GENERATED_BODY()
using FInternalNodeType = FAnimNode_KawaiiPhysics;
};
/**
* Exposes operations to be performed on a blend space anim node.
*/
UCLASS()
class KAWAIIPHYSICS_API UKawaiiPhysicsLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/** Get a KawaiiPhysics from an anim node */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta = (BlueprintThreadSafe, ExpandEnumAsExecs = "Result"))
static FKawaiiPhysicsReference ConvertToKawaiiPhysics(const FAnimNodeReference& Node,
EAnimNodeReferenceConversionResult& Result);
/** Get a KawaiiPhysics from an anim node (pure). */
UFUNCTION(BlueprintPure, Category = "Kawaii Physics",
meta = (BlueprintThreadSafe, DisplayName = "Convert to Kawaii Physics (Pure)"))
static void ConvertToKawaiiPhysicsPure(const FAnimNodeReference& Node, FKawaiiPhysicsReference& KawaiiPhysics,
bool& Result)
{
EAnimNodeReferenceConversionResult ConversionResult;
KawaiiPhysics = ConvertToKawaiiPhysics(Node, ConversionResult);
Result = (ConversionResult == EAnimNodeReferenceConversionResult::Succeeded);
}
/** Collect KawaiiPhysics Node References from AnimInstance(ABP) */
static bool CollectKawaiiPhysicsNodes(TArray<FKawaiiPhysicsReference>& Nodes,
UAnimInstance* AnimInstance, const FGameplayTagContainer& FilterTags,
bool bFilterExactMatch);
/** Collect KawaiiPhysics Node References from SkeletalMeshComponent */
static bool CollectKawaiiPhysicsNodes(TArray<FKawaiiPhysicsReference>& Nodes,
USkeletalMeshComponent* MeshComp, const FGameplayTagContainer& FilterTags,
bool bFilterExactMatch);
/** ResetDynamics */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference ResetDynamics(const FKawaiiPhysicsReference& KawaiiPhysics);
/** Set RootBone */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetRootBoneName(const FKawaiiPhysicsReference& KawaiiPhysics,
UPARAM(ref) FName& RootBoneName);
/** Get RootBone */
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FName GetRootBoneName(const FKawaiiPhysicsReference& KawaiiPhysics);
/** Set ExcludeBones */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetExcludeBoneNames(const FKawaiiPhysicsReference& KawaiiPhysics,
UPARAM(ref) TArray<FName>& ExcludeBoneNames);
/** Get ExcludeBones */
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static TArray<FName> GetExcludeBoneNames(const FKawaiiPhysicsReference& KawaiiPhysics);
// PhysicsSettings
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetPhysicsSettings(const FKawaiiPhysicsReference& KawaiiPhysics,
UPARAM(ref) FKawaiiPhysicsSettings& PhysicsSettings)
{
KAWAIIPHYSICS_VALUE_SETTER(FKawaiiPhysicsSettings, PhysicsSettings);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsSettings GetPhysicsSettings(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(FKawaiiPhysicsSettings, PhysicsSettings);
}
// DummyBoneLength
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetDummyBoneLength(const FKawaiiPhysicsReference& KawaiiPhysics,
float DummyBoneLength)
{
KAWAIIPHYSICS_VALUE_SETTER(float, DummyBoneLength);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static float GetDummyBoneLength(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(float, DummyBoneLength);
}
/** TeleportDistanceThreshold */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetTeleportDistanceThreshold(const FKawaiiPhysicsReference& KawaiiPhysics,
float TeleportDistanceThreshold)
{
KAWAIIPHYSICS_VALUE_SETTER(float, TeleportDistanceThreshold);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static float GetTeleportDistanceThreshold(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(float, TeleportDistanceThreshold);
}
/** TeleportRotationThreshold */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetTeleportRotationThreshold(const FKawaiiPhysicsReference& KawaiiPhysics,
float TeleportRotationThreshold)
{
KAWAIIPHYSICS_VALUE_SETTER(float, TeleportRotationThreshold);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static float GetTeleportRotationThreshold(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(float, TeleportRotationThreshold);
}
/** Gravity */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetGravity(const FKawaiiPhysicsReference& KawaiiPhysics, FVector Gravity)
{
KAWAIIPHYSICS_VALUE_SETTER(FVector, Gravity);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FVector GetGravity(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(FVector, Gravity);
}
/** EnableWind */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetEnableWind(const FKawaiiPhysicsReference& KawaiiPhysics, bool bEnableWind)
{
KAWAIIPHYSICS_VALUE_SETTER(bool, bEnableWind);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static bool GetEnableWind(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(bool, bEnableWind);
}
/** WindScale */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetWindScale(const FKawaiiPhysicsReference& KawaiiPhysics, float WindScale)
{
KAWAIIPHYSICS_VALUE_SETTER(float, WindScale);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static float GetWindScale(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(float, WindScale);
}
/** AllowWorldCollision */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetAllowWorldCollision(const FKawaiiPhysicsReference& KawaiiPhysics,
bool bAllowWorldCollision)
{
KAWAIIPHYSICS_VALUE_SETTER(bool, bAllowWorldCollision);
}
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static bool GetAllowWorldCollision(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(bool, bAllowWorldCollision);
}
/** NeedWarmUp */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetNeedWarmUp(const FKawaiiPhysicsReference& KawaiiPhysics, bool bNeedWarmUp)
{
KAWAIIPHYSICS_VALUE_SETTER(bool, bNeedWarmUp);
}
/** NeedWarmUp */
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static bool GetNeedWarmUp(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(bool, bNeedWarmUp);
}
/** LimitsDataAsset */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static FKawaiiPhysicsReference SetLimitsDataAsset(const FKawaiiPhysicsReference& KawaiiPhysics,
UKawaiiPhysicsLimitsDataAsset* LimitsDataAsset)
{
KAWAIIPHYSICS_VALUE_SETTER(TObjectPtr<UKawaiiPhysicsLimitsDataAsset>, LimitsDataAsset);
}
/** LimitsDataAsset */
UFUNCTION(BlueprintPure, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static UKawaiiPhysicsLimitsDataAsset* GetLimitsDataAsset(const FKawaiiPhysicsReference& KawaiiPhysics)
{
KAWAIIPHYSICS_VALUE_GETTER(TObjectPtr<UKawaiiPhysicsLimitsDataAsset>, LimitsDataAsset);
}
/** Add ExternalForce With ExecResult */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference AddExternalForceWithExecResult(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
FInstancedStruct& ExternalForce, UObject* Owner);
/** Add ExternalForce */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static bool AddExternalForce(const FKawaiiPhysicsReference& KawaiiPhysics,
FInstancedStruct& ExternalForce, UObject* Owner, bool bIsOneShot = false);
/** Add ExternalForces to SkeletalMeshComponent */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static bool AddExternalForcesToComponent(USkeletalMeshComponent* MeshComp,
UPARAM(ref) TArray<FInstancedStruct>& ExternalForces, UObject* Owner,
UPARAM(ref) FGameplayTagContainer& FilterTags,
bool bFilterExactMatch = false,
bool bIsOneShot = false);
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics", meta=(BlueprintThreadSafe))
static bool RemoveExternalForcesFromComponent(USkeletalMeshComponent* MeshComp, UObject* Owner,
UPARAM(ref) FGameplayTagContainer& FilterTags,
bool bFilterExactMatch = false);
/** Set ExternalForceParameter template */
template <typename ValueType, typename PropertyType>
static FKawaiiPhysicsReference SetExternalForceProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
ValueType Value);
/** Get ExternalForceParameter template */
template <typename ValueType>
static ValueType GetExternalForceProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName);
/** Set ExternalForceParameter template struct */
template <typename ValueType>
static FKawaiiPhysicsReference SetExternalForceStructProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
ValueType Value);
/** Get ExternalForceParameter template struct */
template <typename ValueType>
static ValueType GetExternalForceStructProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex,
FName PropertyName);
/** Set ExternalForceParameter bool */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference SetExternalForceBoolProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
bool Value)
{
return SetExternalForceProperty<bool, FBoolProperty>(ExecResult, KawaiiPhysics, ExternalForceIndex,
PropertyName, Value);
}
/** Get ExternalForceParameter bool */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static bool GetExternalForceBoolProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName)
{
return GetExternalForceProperty<bool>(ExecResult, KawaiiPhysics, ExternalForceIndex, PropertyName);
}
/** Set ExternalForceParameter int */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference SetExternalForceIntProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
int32 Value)
{
return SetExternalForceProperty<int32, FIntProperty>(ExecResult, KawaiiPhysics, ExternalForceIndex,
PropertyName, Value);
}
/** Get ExternalForceParameter int */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static int32 GetExternalForceIntProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName)
{
return GetExternalForceProperty<int32>(ExecResult, KawaiiPhysics, ExternalForceIndex, PropertyName);
}
/** Set ExternalForceParameter float */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference SetExternalForceFloatProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
float Value)
{
return SetExternalForceProperty<float, FFloatProperty>(ExecResult, KawaiiPhysics, ExternalForceIndex,
PropertyName, Value);
}
/** Get ExternalForceParameter float */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static float GetExternalForceFloatProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName)
{
return GetExternalForceProperty<float>(ExecResult, KawaiiPhysics, ExternalForceIndex, PropertyName);
}
/** Get ExternalForceParameter Vector */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference SetExternalForceVectorProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
FVector Value)
{
return SetExternalForceStructProperty<FVector>(ExecResult, KawaiiPhysics, ExternalForceIndex,
PropertyName, Value);
}
/** Get ExternalForceParameter Vector */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FVector GetExternalForceVectorProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName)
{
return GetExternalForceStructProperty<FVector>(ExecResult, KawaiiPhysics, ExternalForceIndex, PropertyName);
}
/** Get ExternalForceParameter Rotator */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference SetExternalForceRotatorProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
FRotator Value)
{
return SetExternalForceStructProperty<FRotator>(ExecResult, KawaiiPhysics, ExternalForceIndex,
PropertyName, Value);
}
/** Get ExternalForceParameter Rotator */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FRotator GetExternalForceRotatorProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex,
FName PropertyName)
{
return GetExternalForceStructProperty<FRotator>(ExecResult, KawaiiPhysics, ExternalForceIndex, PropertyName);
}
/** Get ExternalForceParameter Transform */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FKawaiiPhysicsReference SetExternalForceTransformProperty(
EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName,
FTransform Value)
{
return SetExternalForceStructProperty<FTransform>(ExecResult, KawaiiPhysics, ExternalForceIndex,
PropertyName, Value);
}
/** Get ExternalForceParameter Transform */
UFUNCTION(BlueprintCallable, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult"))
static FTransform GetExternalForceTransformProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex,
FName PropertyName)
{
return GetExternalForceStructProperty<FTransform>(ExecResult, KawaiiPhysics, ExternalForceIndex, PropertyName);
}
/** Set ExternalForceParameter Wildcard */
UFUNCTION(BlueprintCallable, CustomThunk, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult", CustomStructureParam = "Value"))
static void SetExternalForceWildcardProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName, const int32& Value)
{
checkNoEntry();
}
/** Get ExternalForceParameter Wildcard */
UFUNCTION(BlueprintCallable, CustomThunk, Category = "Kawaii Physics",
meta=(BlueprintThreadSafe, ExpandEnumAsExecs = "ExecResult", CustomStructureParam = "Value"))
static void GetExternalForceWildcardProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics, int ExternalForceIndex,
FName PropertyName, int32& Value)
{
checkNoEntry();
}
private:
DECLARE_FUNCTION(execSetExternalForceWildcardProperty);
DECLARE_FUNCTION(execGetExternalForceWildcardProperty);
};
template <typename ValueType, typename PropertyType>
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::SetExternalForceProperty(
EKawaiiPhysicsAccessExternalForceResult& ExecResult, const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName, ValueType Value)
{
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("SetExternalForceProperty"),
[&ExecResult, &ExternalForceIndex, &PropertyName, &Value](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
if (InKawaiiPhysics.ExternalForces.IsValidIndex(ExternalForceIndex) &&
InKawaiiPhysics.ExternalForces[ExternalForceIndex].IsValid())
{
const auto* ScriptStruct = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetScriptStruct();
auto& Force = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetMutable<
FKawaiiPhysics_ExternalForce>();
if (const PropertyType* Property = FindFProperty<PropertyType>(ScriptStruct, PropertyName))
{
if (void* ValuePtr = Property->template ContainerPtrToValuePtr<uint8>(&Force))
{
Property->SetPropertyValue(ValuePtr, Value);
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
}
}
});
return KawaiiPhysics;
}
template <typename ValueType>
ValueType UKawaiiPhysicsLibrary::GetExternalForceProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName)
{
ValueType Result;
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("GetExternalForceProperty"),
[&Result, &ExecResult, &ExternalForceIndex, &PropertyName](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
if (InKawaiiPhysics.ExternalForces.IsValidIndex(ExternalForceIndex) &&
InKawaiiPhysics.ExternalForces[ExternalForceIndex].IsValid())
{
const auto* ScriptStruct = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetScriptStruct();
const auto& Force = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetMutable<
FKawaiiPhysics_ExternalForce>();
if (const FProperty* Property = FindFProperty<FProperty>(ScriptStruct, PropertyName))
{
Result = *(Property->ContainerPtrToValuePtr<ValueType>(&Force));
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
}
});
return Result;
}
template <typename ValueType>
FKawaiiPhysicsReference UKawaiiPhysicsLibrary::SetExternalForceStructProperty(
EKawaiiPhysicsAccessExternalForceResult& ExecResult, const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName, ValueType Value)
{
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("SetExternalForceStructProperty"),
[&ExecResult, &ExternalForceIndex, &PropertyName, &Value](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
if (InKawaiiPhysics.ExternalForces.IsValidIndex(ExternalForceIndex) &&
InKawaiiPhysics.ExternalForces[ExternalForceIndex].IsValid())
{
const auto* ScriptStruct = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetScriptStruct();
auto& Force = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetMutable<
FKawaiiPhysics_ExternalForce>();
if (const FStructProperty* StructProperty = FindFProperty<FStructProperty>(
ScriptStruct, PropertyName))
{
if (StructProperty->Struct == TBaseStructure<ValueType>::Get())
{
if (void* ValuePtr = StructProperty->ContainerPtrToValuePtr<uint8>(&Force))
{
StructProperty->CopyCompleteValue(ValuePtr, &Value);
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
}
}
}
});
return KawaiiPhysics;
}
template <typename ValueType>
ValueType UKawaiiPhysicsLibrary::GetExternalForceStructProperty(EKawaiiPhysicsAccessExternalForceResult& ExecResult,
const FKawaiiPhysicsReference& KawaiiPhysics,
int ExternalForceIndex, FName PropertyName)
{
ValueType Result;
ExecResult = EKawaiiPhysicsAccessExternalForceResult::NotValid;
KawaiiPhysics.CallAnimNodeFunction<FAnimNode_KawaiiPhysics>(
TEXT("GetExternalForceStructProperty"),
[&Result, &ExecResult, &ExternalForceIndex, &PropertyName](FAnimNode_KawaiiPhysics& InKawaiiPhysics)
{
if (InKawaiiPhysics.ExternalForces.IsValidIndex(ExternalForceIndex) &&
InKawaiiPhysics.ExternalForces[ExternalForceIndex].IsValid())
{
const auto* ScriptStruct = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetScriptStruct();
const auto& Force = InKawaiiPhysics.ExternalForces[ExternalForceIndex].GetMutable<
FKawaiiPhysics_ExternalForce>();
if (const FStructProperty* StructProperty = FindFProperty<FStructProperty>(
ScriptStruct, PropertyName))
{
if (StructProperty->Struct == TBaseStructure<ValueType>::Get())
{
Result = *(StructProperty->ContainerPtrToValuePtr<ValueType>(&Force));
ExecResult = EKawaiiPhysicsAccessExternalForceResult::Valid;
}
}
}
});
return Result;
}
@@ -0,0 +1,197 @@
// KawaiiPhysics : Copyright (c) 2019-2024 pafuhana1213, MIT License
#pragma once
#include "AnimNode_KawaiiPhysics.h"
#include "Engine/DataAsset.h"
#include "Interfaces/Interface_BoneReferenceSkeletonProvider.h"
#include "KawaiiPhysicsLimitsDataAsset.generated.h"
DECLARE_MULTICAST_DELEGATE_OneParam(FOnLimitsChanged, struct FPropertyChangedEvent&);
// Deprecated
USTRUCT()
struct FCollisionLimitDataBase
{
GENERATED_BODY()
UPROPERTY(meta=(DeprecatedProperty))
FBoneReference DrivingBoneReference;
UPROPERTY(meta=(DeprecatedProperty))
FName DrivingBoneName;
UPROPERTY(meta=(DeprecatedProperty))
FVector OffsetLocation = FVector::ZeroVector;
UPROPERTY(meta=(DeprecatedProperty))
FRotator OffsetRotation = FRotator::ZeroRotator;
UPROPERTY(meta=(DeprecatedProperty))
FVector Location = FVector::ZeroVector;
UPROPERTY(meta=(DeprecatedProperty))
FQuat Rotation = FQuat::Identity;
UPROPERTY(meta=(DeprecatedProperty, IgnoreForMemberInitializationTest))
FGuid Guid = FGuid::NewGuid();
protected:
void ConvertBase(FCollisionLimitBase& Limit) const
{
Limit.DrivingBone.BoneName = DrivingBoneReference.BoneName;
Limit.OffsetLocation = OffsetLocation;
Limit.OffsetRotation = OffsetRotation;
Limit.Location = Location;
Limit.Rotation = Rotation;
#if WITH_EDITORONLY_DATA
Limit.SourceType = ECollisionSourceType::DataAsset;
Limit.Guid = Guid;
#endif
}
};
// Deprecated
USTRUCT()
struct FSphericalLimitData : public FCollisionLimitDataBase
{
GENERATED_BODY()
/** Radius of the sphere */
UPROPERTY(meta=(DeprecatedProperty))
float Radius = 5.0f;
/** Whether to lock bodies inside or outside of the sphere */
UPROPERTY(meta=(DeprecatedProperty))
ESphericalLimitType LimitType = ESphericalLimitType::Outer;
FSphericalLimit Convert() const
{
FSphericalLimit Limit;
ConvertBase(Limit);
Limit.Radius = Radius;
Limit.LimitType = LimitType;
return Limit;
}
};
// Deprecated
USTRUCT()
struct FCapsuleLimitData : public FCollisionLimitDataBase
{
GENERATED_BODY()
UPROPERTY(meta=(DeprecatedProperty))
float Radius = 5.0f;
UPROPERTY(meta=(DeprecatedProperty))
float Length = 10.0f;
FCapsuleLimit Convert() const
{
FCapsuleLimit Limit;
ConvertBase(Limit);
Limit.Radius = Radius;
Limit.Length = Length;
return Limit;
}
};
// Deprecated
USTRUCT()
struct FBoxLimitData : public FCollisionLimitDataBase
{
GENERATED_BODY()
UPROPERTY(meta=(DeprecatedProperty))
FVector Extent = FVector(5.0f, 5.0f, 5.0f);
FBoxLimit Convert() const
{
FBoxLimit Limit;
ConvertBase(Limit);
Limit.Extent = Extent;
return Limit;
}
};
// Deprecated
USTRUCT()
struct FPlanarLimitData : public FCollisionLimitDataBase
{
GENERATED_BODY()
UPROPERTY(meta=(DeprecatedProperty))
FPlane Plane = FPlane(0, 0, 0, 0);
FPlanarLimit Convert() const
{
FPlanarLimit Limit;
ConvertBase(Limit);
Limit.Plane = Plane;
return Limit;
}
};
/**
*
*/
UCLASS(Blueprintable)
class KAWAIIPHYSICS_API UKawaiiPhysicsLimitsDataAsset : public UDataAsset, public IBoneReferenceSkeletonProvider
{
GENERATED_BODY()
public:
#if WITH_EDITORONLY_DATA
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skeleton")
TObjectPtr<USkeleton> Skeleton;
// Deprecated
UPROPERTY(meta=(DeprecatedProperty))
TArray<FSphericalLimitData> SphericalLimitsData;
UPROPERTY(meta=(DeprecatedProperty))
TArray<FCapsuleLimitData> CapsuleLimitsData;
UPROPERTY(meta=(DeprecatedProperty))
TArray<FBoxLimitData> BoxLimitsData;
UPROPERTY(meta=(DeprecatedProperty))
TArray<FPlanarLimitData> PlanarLimitsData;
#endif
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spherical Limits")
TArray<FSphericalLimit> SphericalLimits;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capsule Limits")
TArray<FCapsuleLimit> CapsuleLimits;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Box Limits")
TArray<FBoxLimit> BoxLimits;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Planar Limits")
TArray<FPlanarLimit> PlanarLimits;
// Begin UObject Interface.
#if WITH_EDITORONLY_DATA
virtual void Serialize(FStructuredArchiveRecord Record) override;
#endif
virtual void PostLoad() override;
// End UObject Interface.
// IBoneReferenceSkeletonProvider interface
virtual USkeleton* GetSkeleton(bool& bInvalidSkeletonIsError, const IPropertyHandle* PropertyHandle) override;
#if WITH_EDITOR
void UpdateLimit(FCollisionLimitBase* Limit);
FOnLimitsChanged OnLimitsChanged;
virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override;
#endif
private:
#if WITH_EDITOR
void Sync();
#endif
};
@@ -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;
};