2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "AnimGraphPrivatePCH.h"
2014-04-24 08:49:31 -04:00
# include "AnimGraphNode_TwoBoneIK.h"
2014-03-14 14:13:41 -04:00
2014-09-22 09:45:11 -04:00
// for customization details
# include "../../PropertyEditor/Public/PropertyHandle.h"
# include "../../PropertyEditor/Public/DetailLayoutBuilder.h"
# include "../../PropertyEditor/Public/DetailCategoryBuilder.h"
# include "../../PropertyEditor/Public/DetailWidgetRow.h"
# include "../../PropertyEditor/Public/IDetailPropertyRow.h"
# define LOCTEXT_NAMESPACE "A3Nodes"
/////////////////////////////////////////////////////
// FTwoBoneIKDelegate
class FTwoBoneIKDelegate : public TSharedFromThis < FTwoBoneIKDelegate >
{
public :
void UpdateLocationSpace ( class IDetailLayoutBuilder * DetailBuilder )
{
if ( DetailBuilder )
{
DetailBuilder - > ForceRefreshDetails ( ) ;
}
}
} ;
2014-03-14 14:13:41 -04:00
/////////////////////////////////////////////////////
// UAnimGraphNode_TwoBoneIK
2014-04-23 18:30:37 -04:00
2014-03-14 14:13:41 -04:00
UAnimGraphNode_TwoBoneIK : : UAnimGraphNode_TwoBoneIK ( const FPostConstructInitializeProperties & PCIP )
: Super ( PCIP )
{
}
2014-04-23 18:30:37 -04:00
FText UAnimGraphNode_TwoBoneIK : : GetControllerDescription ( ) const
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:30:37 -04:00
return LOCTEXT ( " TwoBoneIK " , " Two Bone IK " ) ;
2014-03-14 14:13:41 -04:00
}
2014-09-03 18:14:09 -04:00
FText UAnimGraphNode_TwoBoneIK : : GetTooltipText ( ) const
2014-03-14 14:13:41 -04:00
{
2014-09-03 18:14:09 -04:00
return LOCTEXT ( " AnimGraphNode_TwoBoneIK_Tooltip " , " The Two Bone IK control applies an inverse kinematic (IK) solver to a 3 - joint chain , such as the limbs of a character . " ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:30:37 -04:00
FText UAnimGraphNode_TwoBoneIK : : GetNodeTitle ( ENodeTitleType : : Type TitleType ) const
2014-03-14 14:13:41 -04:00
{
2014-09-16 15:01:38 -04:00
if ( ( TitleType = = ENodeTitleType : : ListView | | TitleType = = ENodeTitleType : : MenuTitle ) & & ( Node . IKBone . BoneName = = NAME_None ) )
2014-09-04 11:25:05 -04:00
{
return GetControllerDescription ( ) ;
}
2014-09-24 14:15:13 -04:00
// @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
else //if (!CachedNodeTitles.IsTitleCached(TitleType))
2014-09-04 11:25:05 -04:00
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " ControllerDescription " ) , GetControllerDescription ( ) ) ;
Args . Add ( TEXT ( " BoneName " ) , FText : : FromName ( Node . IKBone . BoneName ) ) ;
2014-04-23 18:30:37 -04:00
2014-09-04 11:25:05 -04:00
// FText::Format() is slow, so we cache this to save on performance
2014-09-16 15:01:38 -04:00
if ( TitleType = = ENodeTitleType : : ListView | | TitleType = = ENodeTitleType : : MenuTitle )
2014-08-21 18:50:33 -04:00
{
2014-09-04 11:25:05 -04:00
CachedNodeTitles . SetCachedTitle ( TitleType , FText : : Format ( LOCTEXT ( " AnimGraphNode_IKBone_ListTitle " , " {ControllerDescription} - Bone: {BoneName} " ) , Args ) ) ;
}
else
{
CachedNodeTitles . SetCachedTitle ( TitleType , FText : : Format ( LOCTEXT ( " AnimGraphNode_IKBone_Title " , " {ControllerDescription} \n Bone: {BoneName} " ) , Args ) ) ;
2014-08-21 18:50:33 -04:00
}
2014-04-23 18:30:37 -04:00
}
2014-09-04 11:25:05 -04:00
return CachedNodeTitles [ TitleType ] ;
2014-04-23 18:30:37 -04:00
}
2014-03-14 14:13:41 -04:00
void UAnimGraphNode_TwoBoneIK : : Draw ( FPrimitiveDrawInterface * PDI , USkeletalMeshComponent * SkelMeshComp ) const
{
2014-09-22 09:45:11 -04:00
if ( SkelMeshComp & & SkelMeshComp - > SkeletalMesh & & SkelMeshComp - > SkeletalMesh - > Skeleton )
2014-03-14 14:13:41 -04:00
{
2014-10-01 14:45:23 -04:00
USkeleton * Skeleton = SkelMeshComp - > SkeletalMesh - > Skeleton ;
2014-03-14 14:13:41 -04:00
2014-09-22 09:45:11 -04:00
DrawTargetLocation ( PDI , SkelMeshComp , Skeleton , Node . EffectorLocationSpace , Node . EffectorSpaceBoneName , Node . EffectorLocation , FColor ( 255 , 128 , 128 ) , FColor ( 180 , 128 , 128 ) ) ;
DrawTargetLocation ( PDI , SkelMeshComp , Skeleton , Node . JointTargetLocationSpace , Node . JointTargetSpaceBoneName , Node . JointTargetLocation , FColor ( 128 , 255 , 128 ) , FColor ( 128 , 180 , 128 ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-10-01 14:45:23 -04:00
void UAnimGraphNode_TwoBoneIK : : DrawTargetLocation ( FPrimitiveDrawInterface * PDI , USkeletalMeshComponent * SkelComp , USkeleton * Skeleton , uint8 SpaceBase , FName SpaceBoneName , const FVector & TargetLocation , const FColor & TargetColor , const FColor & BoneColor ) const
2014-03-14 14:13:41 -04:00
{
const bool bInBoneSpace = ( SpaceBase = = BCS_ParentBoneSpace ) | | ( SpaceBase = = BCS_BoneSpace ) ;
const int32 SpaceBoneIndex = bInBoneSpace ? Skeleton - > GetReferenceSkeleton ( ) . FindBoneIndex ( SpaceBoneName ) : INDEX_NONE ;
// Transform EffectorLocation from EffectorLocationSpace to ComponentSpace.
FTransform TargetTransform = FTransform ( TargetLocation ) ;
FTransform CSTransform ;
ConvertToComponentSpaceTransform ( SkelComp , Skeleton , TargetTransform , CSTransform , SpaceBoneIndex , SpaceBase ) ;
FTransform WorldTransform = CSTransform * SkelComp - > ComponentToWorld ;
DrawCoordinateSystem ( PDI , WorldTransform . GetLocation ( ) , WorldTransform . GetRotation ( ) . Rotator ( ) , 20.f , SDPG_Foreground ) ;
DrawWireDiamond ( PDI , WorldTransform . ToMatrixWithScale ( ) , 2.f , TargetColor , SDPG_Foreground ) ;
if ( bInBoneSpace )
{
ConvertToComponentSpaceTransform ( SkelComp , Skeleton , FTransform : : Identity , CSTransform , SpaceBoneIndex , SpaceBase ) ;
WorldTransform = CSTransform * SkelComp - > ComponentToWorld ;
DrawCoordinateSystem ( PDI , WorldTransform . GetLocation ( ) , WorldTransform . GetRotation ( ) . Rotator ( ) , 20.f , SDPG_Foreground ) ;
DrawWireDiamond ( PDI , WorldTransform . ToMatrixWithScale ( ) , 2.f , BoneColor , SDPG_Foreground ) ;
}
}
2014-10-01 14:45:23 -04:00
void UAnimGraphNode_TwoBoneIK : : ConvertToComponentSpaceTransform ( USkeletalMeshComponent * SkelComp , USkeleton * Skeleton , const FTransform & InTransform , FTransform & OutCSTransform , int32 BoneIndex , uint8 Space ) const
2014-03-14 14:13:41 -04:00
{
switch ( Space )
{
case BCS_WorldSpace :
{
OutCSTransform = InTransform ;
OutCSTransform . SetToRelativeTransform ( SkelComp - > ComponentToWorld ) ;
}
break ;
case BCS_ComponentSpace :
{
// Component Space, no change.
OutCSTransform = InTransform ;
}
break ;
case BCS_ParentBoneSpace :
if ( BoneIndex ! = INDEX_NONE )
{
const int32 ParentIndex = Skeleton - > GetReferenceSkeleton ( ) . GetParentIndex ( BoneIndex ) ;
if ( ParentIndex ! = INDEX_NONE )
{
const int32 MeshParentIndex = Skeleton - > GetMeshBoneIndexFromSkeletonBoneIndex ( SkelComp - > SkeletalMesh , ParentIndex ) ;
if ( MeshParentIndex ! = INDEX_NONE )
{
const FTransform ParentTM = SkelComp - > GetBoneTransform ( MeshParentIndex ) ;
OutCSTransform = InTransform * ParentTM ;
}
else
{
OutCSTransform = InTransform ;
}
}
}
break ;
case BCS_BoneSpace :
if ( BoneIndex ! = INDEX_NONE )
{
const int32 MeshBoneIndex = Skeleton - > GetMeshBoneIndexFromSkeletonBoneIndex ( SkelComp - > SkeletalMesh , BoneIndex ) ;
if ( MeshBoneIndex ! = INDEX_NONE )
{
const FTransform BoneTM = SkelComp - > GetBoneTransform ( MeshBoneIndex ) ;
OutCSTransform = InTransform * BoneTM ;
}
else
{
OutCSTransform = InTransform ;
}
}
break ;
default :
if ( SkelComp - > SkeletalMesh )
{
UE_LOG ( LogAnimation , Warning , TEXT ( " ConvertToComponentSpaceTransform: Unknown BoneSpace %d for Mesh: %s " ) , Space , * SkelComp - > SkeletalMesh - > GetFName ( ) . ToString ( ) ) ;
}
else
{
UE_LOG ( LogAnimation , Warning , TEXT ( " ConvertToComponentSpaceTransform: Unknown BoneSpace %d for Skeleton: %s " ) , Space , * Skeleton - > GetFName ( ) . ToString ( ) ) ;
}
break ;
}
}
2014-04-23 18:30:37 -04:00
2014-09-22 09:45:11 -04:00
// just for refreshing UIs when bone space was changed
TSharedPtr < FTwoBoneIKDelegate > TwoBoneIKDelegate ;
void UAnimGraphNode_TwoBoneIK : : CustomizeDetails ( class IDetailLayoutBuilder & DetailBuilder )
{
// initialize just once
if ( ! TwoBoneIKDelegate . IsValid ( ) )
{
TwoBoneIKDelegate = MakeShareable ( new FTwoBoneIKDelegate ( ) ) ;
}
EBoneControlSpace Space = Node . EffectorLocationSpace ;
if ( Space = = BCS_BoneSpace | | Space = = BCS_ParentBoneSpace )
{
IDetailCategoryBuilder & IKCategory = DetailBuilder . EditCategory ( " IK " ) ;
IDetailCategoryBuilder & EffectorCategory = DetailBuilder . EditCategory ( " EndEffector " ) ;
TSharedPtr < IPropertyHandle > PropertyHandle ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.bTakeRotationFromEffectorSpace " ) , GetClass ( ) ) ;
EffectorCategory . AddProperty ( PropertyHandle ) ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.bMaintainEffectorRelRot " ) , GetClass ( ) ) ;
EffectorCategory . AddProperty ( PropertyHandle ) ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.EffectorSpaceBoneName " ) , GetClass ( ) ) ;
EffectorCategory . AddProperty ( PropertyHandle ) ;
}
else // hide all properties in EndEffector category
{
TSharedPtr < IPropertyHandle > PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.EffectorLocation " ) , GetClass ( ) ) ;
DetailBuilder . HideProperty ( PropertyHandle ) ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.bTakeRotationFromEffectorSpace " ) , GetClass ( ) ) ;
DetailBuilder . HideProperty ( PropertyHandle ) ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.bMaintainEffectorRelRot " ) , GetClass ( ) ) ;
DetailBuilder . HideProperty ( PropertyHandle ) ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.EffectorSpaceBoneName " ) , GetClass ( ) ) ;
DetailBuilder . HideProperty ( PropertyHandle ) ;
}
Space = Node . JointTargetLocationSpace ;
if ( Space = = BCS_BoneSpace | | Space = = BCS_ParentBoneSpace )
{
IDetailCategoryBuilder & IKCategory = DetailBuilder . EditCategory ( " IK " ) ;
IDetailCategoryBuilder & EffectorCategory = DetailBuilder . EditCategory ( " JointTarget " ) ;
TSharedPtr < IPropertyHandle > PropertyHandle ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.JointTargetSpaceBoneName " ) , GetClass ( ) ) ;
EffectorCategory . AddProperty ( PropertyHandle ) ;
SetPinsVisibility ( true ) ;
}
else // hide all properties in JointTarget category except for JointTargetLocationSpace
{
TSharedPtr < IPropertyHandle > PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.JointTargetLocation " ) , GetClass ( ) ) ;
DetailBuilder . HideProperty ( PropertyHandle ) ;
PropertyHandle = DetailBuilder . GetProperty ( FName ( " Node.JointTargetSpaceBoneName " ) , GetClass ( ) ) ;
DetailBuilder . HideProperty ( PropertyHandle ) ;
SetPinsVisibility ( false ) ;
}
// refresh UIs when bone space is changed
TSharedRef < IPropertyHandle > EffectorLocHandle = DetailBuilder . GetProperty ( FName ( " Node.EffectorLocationSpace " ) , GetClass ( ) ) ;
if ( EffectorLocHandle - > IsValidHandle ( ) )
{
FSimpleDelegate UpdateEffectorSpaceDelegate = FSimpleDelegate : : CreateSP ( TwoBoneIKDelegate . Get ( ) , & FTwoBoneIKDelegate : : UpdateLocationSpace , & DetailBuilder ) ;
EffectorLocHandle - > SetOnPropertyValueChanged ( UpdateEffectorSpaceDelegate ) ;
}
TSharedRef < IPropertyHandle > JointTragetLocHandle = DetailBuilder . GetProperty ( FName ( " Node.JointTargetLocationSpace " ) , GetClass ( ) ) ;
if ( JointTragetLocHandle - > IsValidHandle ( ) )
{
FSimpleDelegate UpdateJointSpaceDelegate = FSimpleDelegate : : CreateSP ( TwoBoneIKDelegate . Get ( ) , & FTwoBoneIKDelegate : : UpdateLocationSpace , & DetailBuilder ) ;
JointTragetLocHandle - > SetOnPropertyValueChanged ( UpdateJointSpaceDelegate ) ;
}
// reconstruct node for showing/hiding Pins
ReconstructNode ( ) ;
}
void UAnimGraphNode_TwoBoneIK : : SetPinsVisibility ( bool bShow )
{
for ( FOptionalPinFromProperty & Pin : ShowPinForProperties )
{
if ( Pin . PropertyName = = " JointTargetLocation " )
{
PreEditChange ( NULL ) ;
Pin . bShowPin = bShow ;
break ;
}
}
}
2014-04-23 18:30:37 -04:00
# undef LOCTEXT_NAMESPACE