void UAnimationGraphSchema::GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const { UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets); if ((Asset == NULL) || (HoverPin == NULL)) { OutTooltipText = TEXT(""); OutOkIcon = false; return; } // this one only should happen when there is an Anim Blueprint UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForNode(HoverPin->GetOwningNode())); const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton()); const bool bTypeMatch = UAnimationGraphSchema::IsLocalSpacePosePin(HoverPin->PinType); const bool bDirectionMatch = HoverPin->Direction == EGPD_Input; if (bSkelMatch && bTypeMatch && bDirectionMatch) { OutOkIcon = true; OutTooltipText = FString::Printf(TEXT("Play %s and feed to %s"), *(Asset->GetName()), *HoverPin->PinName); } else { OutOkIcon = false; OutTooltipText = FString::Printf(TEXT("Type or direction mismatch; must be wired to a pose input")); } }
void USkeleton::HandleSkeletonHierarchyChange() { MarkPackageDirty(); RegenerateGuid(); // Clear exiting MeshLinkUp tables. ClearCacheData(); // Fix up loaded animations (any animations that aren't loaded will be fixed on load) for(TObjectIterator<UAnimationAsset> It; It; ++It) { UAnimationAsset* CurrentAnimation = *It; if (CurrentAnimation->GetSkeleton() == this) { CurrentAnimation->ValidateSkeleton(); } } #if WITH_EDITORONLY_DATA RefreshAllRetargetSources(); #endif OnSkeletonHierarchyChanged.Broadcast(); }
void UAnimationGraphSchema::GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const { UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets); if ((Asset == NULL) || (HoverNode == NULL)) { OutTooltipText = TEXT(""); OutOkIcon = false; return; } const UK2Node* PlayerNodeUnderCursor = NULL; if (Asset->IsA(UAnimSequence::StaticClass())) { PlayerNodeUnderCursor = Cast<const UAnimGraphNode_SequencePlayer>(HoverNode); } else if (Asset->IsA(UBlendSpace::StaticClass())) { UBlendSpace* BlendSpace = CastChecked<UBlendSpace>(Asset); if (IsAimOffsetBlendSpace(BlendSpace)) { PlayerNodeUnderCursor = Cast<const UAnimGraphNode_RotationOffsetBlendSpace>(HoverNode); } else { PlayerNodeUnderCursor = Cast<const UAnimGraphNode_BlendSpacePlayer>(HoverNode); } } // this one only should happen when there is an Anim Blueprint UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForNode(HoverNode)); const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton()); if (!bSkelMatch) { OutOkIcon = false; OutTooltipText = FString::Printf(TEXT("Skeletons are not compatible")); } else if (PlayerNodeUnderCursor != NULL) { OutOkIcon = true; OutTooltipText = FString::Printf(TEXT("Change node to play %s"), *(Asset->GetName())); } else { OutOkIcon = false; OutTooltipText = FString::Printf(TEXT("Cannot replace '%s' with a sequence player"), *(HoverNode->GetName())); } }
PyObject *py_ue_anim_get_skeleton(ue_PyUObject * self, PyObject * args) { ue_py_check(self); UAnimationAsset *anim = ue_py_check_type<UAnimationAsset>(self); if (!anim) return PyErr_Format(PyExc_Exception, "UObject is not a UAnimationAsset."); USkeleton *skeleton = anim->GetSkeleton(); if (!skeleton) { Py_RETURN_NONE; } Py_RETURN_UOBJECT((UObject *)skeleton); }
void UAnimationStateMachineSchema::DroppedAssetsOnGraph(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const { UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets); if(Asset != NULL) { // Spawn new state UAnimStateNode* NewStateNode = FEdGraphSchemaAction_NewStateNode::SpawnNodeFromTemplate<UAnimStateNode>(Graph, NewObject<UAnimStateNode>(), GraphPosition); // Try to name the state close to the asset FEdGraphUtilities::RenameGraphToNameOrCloseToName(NewStateNode->BoundGraph, Asset->GetName()); // Change the current graph context to the inner graph, so that the rest of the drag drop happens inside it FVector2D NewGraphPosition = FVector2D(-300.0f, 0.0f); UAnimationGraphSchema::SpawnNodeFromAsset(Asset, NewGraphPosition, NewStateNode->BoundGraph, NewStateNode->GetPoseSinkPinInsideState()); } }
void USkeleton::HandleSkeletonHierarchyChange() { MarkPackageDirty(); RegenerateGuid(); // Fix up loaded animations (any animations that aren't loaded will be fixed on load) for(TObjectIterator<UAnimationAsset> It; It; ++It) { UAnimationAsset* CurrentAnimation = *It; CurrentAnimation->ValidateSkeleton(); } RefreshAllRetargetSources(); OnSkeletonHierarchyChanged.Broadcast(); }
PyObject *py_ue_anim_set_skeleton(ue_PyUObject * self, PyObject * args) { ue_py_check(self); PyObject *py_skeleton; if (!PyArg_ParseTuple(args, "O:anim_set_skeleton", &py_skeleton)) return nullptr; UAnimationAsset *anim = ue_py_check_type<UAnimationAsset>(self); if (!anim) return PyErr_Format(PyExc_Exception, "UObject is not a UAnimationAsset."); USkeleton *skeleton = ue_py_check_type<USkeleton>(py_skeleton); if (!skeleton) return PyErr_Format(PyExc_Exception, "argument is not a USkeleton."); anim->SetSkeleton(skeleton); Py_RETURN_NONE; }
FText UK2Node_TransitionRuleGetter::GetNodeTitle(ENodeTitleType::Type TitleType) const { // @TODO: FText::Format() is slow... consider caching this tooltip like // we do for a lot of the BP nodes now (unfamiliar with this // node's purpose, so hesitant to muck with this at this time). if (AssociatedAnimAssetPlayerNode != NULL) { UAnimationAsset* BoundAsset = AssociatedAnimAssetPlayerNode->GetAnimationAsset(); if (BoundAsset) { FFormatNamedArguments Args; Args.Add(TEXT("BoundAsset"), FText::FromString(BoundAsset->GetName())); return FText::Format(LOCTEXT("AnimationAssetInfoGetterTitle", "{BoundAsset} Asset"), Args); } } else if (AssociatedStateNode != NULL) { if (UAnimStateNode* State = Cast<UAnimStateNode>(AssociatedStateNode)) { const FString OwnerName = State->GetOuter()->GetName(); FFormatNamedArguments Args; Args.Add(TEXT("OwnerName"), FText::FromString(OwnerName)); Args.Add(TEXT("StateName"), FText::FromString(State->GetStateName())); return FText::Format(LOCTEXT("StateInfoGetterTitle", "{OwnerName}.{StateName} State"), Args); } } else if (GetterType == ETransitionGetter::CurrentTransitionDuration) { return LOCTEXT("TransitionDuration", "Transition"); } else if (GetterType == ETransitionGetter::CurrentState_ElapsedTime || GetterType == ETransitionGetter::CurrentState_GetBlendWeight) { return LOCTEXT("CurrentState", "Current State"); } return Super::GetNodeTitle(TitleType); }
void UAnimationStateMachineSchema::GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const { UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets); if ((Asset == NULL) || (HoverNode == NULL)) { OutTooltipText = TEXT(""); OutOkIcon = false; return; } const UAnimStateNode* StateNodeUnderCursor = Cast<const UAnimStateNode>(HoverNode); if (StateNodeUnderCursor != NULL) { OutOkIcon = true; OutTooltipText = FString::Printf(TEXT("Change node to play %s"), *(Asset->GetName())); } else { OutTooltipText = TEXT(""); OutOkIcon = false; } }
void UAnimationGraphSchema::GetAssetsGraphHoverMessage(const TArray<FAssetData>& Assets, const UEdGraph* HoverGraph, FString& OutTooltipText, bool& OutOkIcon) const { UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets); if (Asset) { UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(HoverGraph)); const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton()); if (!bSkelMatch) { OutOkIcon = false; OutTooltipText = FString::Printf(TEXT("Skeletons are not compatible")); } } }
void FAssetTypeActions_AnimationAsset::RetargetAssets(TArray<UObject*> InAnimAssets, bool bDuplicateAssets, bool bOpenEditor, TSharedPtr<class IToolkitHost> EditWithinLevelEditor) { bool bRemapReferencedAssets = false; USkeleton* NewSkeleton = NULL; USkeleton* OldSkeleton = NULL; if(InAnimAssets.Num() > 0) { UAnimationAsset * AnimAsset = CastChecked<UAnimationAsset>(InAnimAssets[0]); OldSkeleton = AnimAsset->GetSkeleton(); } const FText Message = LOCTEXT("SelectSkeletonToRemap", "Select the skeleton to remap this asset to."); auto AnimAssets = GetTypedWeakObjectPtrs<UObject>(InAnimAssets); if (bOpenEditor) { SAnimationRemapSkeleton::ShowWindow(OldSkeleton, Message, FOnRetargetAnimation::CreateSP(this, &FAssetTypeActions_AnimationAsset::RetargetNonSkeletonAnimationHandler, bDuplicateAssets, AnimAssets, TWeakPtr<class IToolkitHost> (EditWithinLevelEditor)) ); } else { SAnimationRemapSkeleton::ShowWindow(OldSkeleton, Message, FOnRetargetAnimation::CreateSP(this, &FAssetTypeActions_AnimationAsset::RetargetAnimationHandler, bDuplicateAssets, AnimAssets) ); } }
void UK2Node_TransitionRuleGetter::GetStateSpecificAnimTransitionSchemaMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar, const UAnimBlueprint* AnimBlueprint, UAnimStateNode* StateNode) const { // Offer options from the source state // Sequence player positions ETransitionGetter::Type SequenceSpecificGetters[] = { ETransitionGetter::AnimationAsset_GetCurrentTime, ETransitionGetter::AnimationAsset_GetLength, ETransitionGetter::AnimationAsset_GetCurrentTimeFraction, ETransitionGetter::AnimationAsset_GetTimeFromEnd, ETransitionGetter::AnimationAsset_GetTimeFromEndFraction }; // Using the State Machine's graph, find all asset players nodes TArray<UK2Node*> AssetPlayers; StateNode->BoundGraph->GetNodesOfClassEx<UAnimGraphNode_Base, UK2Node>(/*out*/ AssetPlayers); for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(SequenceSpecificGetters); ++TypeIndex) { for (auto NodeIt = AssetPlayers.CreateConstIterator(); NodeIt; ++NodeIt) { UAnimGraphNode_Base* AnimNode = CastChecked<UAnimGraphNode_Base>(*NodeIt); if (AnimNode->DoesSupportTimeForTransitionGetter()) { UAnimationAsset * AnimAsset = AnimNode->GetAnimationAsset(); if (AnimAsset) { auto UiSpecOverride = [](const FBlueprintActionContext& /*Context*/, const IBlueprintNodeBinder::FBindingSet& Bindings, FBlueprintActionUiSpec* UiSpecOut, FString AssetName, TEnumAsByte<ETransitionGetter::Type> InGetterType) { UiSpecOut->Category = LOCTEXT("AssetPlayer", "Asset Player"); FFormatNamedArguments Args; Args.Add(TEXT("NodeName"), UK2Node_TransitionRuleGetter::GetFriendlyName(InGetterType)); Args.Add(TEXT("AssetName"), FText::FromString(AssetName)); FText Title = FText::Format(LOCTEXT("TransitionFor", "{NodeName} for '{AssetName}'"), Args); UiSpecOut->MenuName = Title; }; auto PostSpawnSetupLambda = [](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, UAnimGraphNode_Base* InAssociatedAnimAssetPlayerNode, TEnumAsByte<ETransitionGetter::Type> InGetterType) { UK2Node_TransitionRuleGetter* NewNodeTyped = CastChecked<UK2Node_TransitionRuleGetter>(NewNode); NewNodeTyped->AssociatedAnimAssetPlayerNode = InAssociatedAnimAssetPlayerNode; NewNodeTyped->GetterType = InGetterType; }; // Prepare the node spawner UAnimGraphNode_Base* AssociatedAnimNode = AnimNode; const FString AssetName = AnimAsset->GetName(); TEnumAsByte<ETransitionGetter::Type> TransitionGetterType = SequenceSpecificGetters[TypeIndex]; UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create( UK2Node_TransitionRuleGetter::StaticClass(), nullptr, UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(PostSpawnSetupLambda, AssociatedAnimNode, TransitionGetterType) ); Spawner->DynamicUiSignatureGetter = UBlueprintNodeSpawner::FUiSpecOverrideDelegate::CreateStatic(UiSpecOverride, AssetName, TransitionGetterType); ActionRegistrar.AddBlueprintAction( AnimBlueprint, Spawner ); } } } } }
void UAnimationTransitionSchema::GetSourceStateActions(FGraphContextMenuBuilder& ContextMenuBuilder) const { if ((ContextMenuBuilder.FromPin == NULL) || ((ContextMenuBuilder.FromPin->Direction == EGPD_Input) && (ContextMenuBuilder.FromPin->PinType.PinCategory == PC_Float))) { // Find the source state associated with this transition UAnimBlueprint* Blueprint = CastChecked<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(ContextMenuBuilder.CurrentGraph)); if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Blueprint->GetAnimBlueprintSkeletonClass()) { if (UAnimStateTransitionNode* TransNode = GetTransitionNodeFromGraph(AnimBlueprintClass->GetAnimBlueprintDebugData(), ContextMenuBuilder.CurrentGraph)) { if (UAnimStateNode* SourceStateNode = Cast<UAnimStateNode>(TransNode->GetPreviousState())) { // Offer options from the source state // Sequence player positions ETransitionGetter::Type SequenceSpecificGetters[] = { ETransitionGetter::AnimationAsset_GetCurrentTime, ETransitionGetter::AnimationAsset_GetLength, ETransitionGetter::AnimationAsset_GetCurrentTimeFraction, ETransitionGetter::AnimationAsset_GetTimeFromEnd, ETransitionGetter::AnimationAsset_GetTimeFromEndFraction }; TArray<UK2Node*> AssetPlayers; SourceStateNode->BoundGraph->GetNodesOfClassEx<UAnimGraphNode_Base, UK2Node>(/*out*/ AssetPlayers); const FString Category_AssetPlayer(TEXT("Asset Player")); for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(SequenceSpecificGetters); ++TypeIndex) { for (auto NodeIt = AssetPlayers.CreateConstIterator(); NodeIt; ++NodeIt) { UAnimGraphNode_Base* AnimNode = CastChecked<UAnimGraphNode_Base>(*NodeIt); if (AnimNode->DoesSupportTimeForTransitionGetter()) { UK2Node_TransitionRuleGetter* NodeTemplate = ContextMenuBuilder.CreateTemplateNode<UK2Node_TransitionRuleGetter>(); FString AssetName; UAnimationAsset * AnimAsset = AnimNode->GetAnimationAsset(); if (AnimAsset) { NodeTemplate->AssociatedAnimAssetPlayerNode = AnimNode; AssetName = AnimAsset->GetName(); } NodeTemplate->GetterType = SequenceSpecificGetters[TypeIndex]; FFormatNamedArguments Args; Args.Add(TEXT("NodeName"), UK2Node_TransitionRuleGetter::GetFriendlyName(NodeTemplate->GetterType)); Args.Add(TEXT("AssetName"), FText::FromString(AssetName)); FText Title = FText::Format(LOCTEXT("TransitionFor", "{NodeName} for '{AssetName}'"), Args); TSharedPtr<FEdGraphSchemaAction_K2NewNode> Action = FK2ActionMenuBuilder::AddNewNodeAction(ContextMenuBuilder, Category_AssetPlayer, Title, NodeTemplate->GetTooltipText().ToString(), 0, NodeTemplate->GetKeywords()); Action->NodeTemplate = NodeTemplate; } } } // Non-sequence specific ones ETransitionGetter::Type NonSpecificGetters[] = { ETransitionGetter::CurrentTransitionDuration, ETransitionGetter::CurrentState_ElapsedTime, ETransitionGetter::CurrentState_GetBlendWeight }; for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(NonSpecificGetters); ++TypeIndex) { FString Category_Transition(TEXT("Transition")); UK2Node_TransitionRuleGetter* NodeTemplate = ContextMenuBuilder.CreateTemplateNode<UK2Node_TransitionRuleGetter>(); NodeTemplate->GetterType = NonSpecificGetters[TypeIndex]; FText Title = UK2Node_TransitionRuleGetter::GetFriendlyName(NodeTemplate->GetterType); TSharedPtr<FEdGraphSchemaAction_K2NewNode> Action = FK2ActionMenuBuilder::AddNewNodeAction(ContextMenuBuilder, Category_Transition, Title, NodeTemplate->GetTooltipText().ToString(), 0, NodeTemplate->GetKeywords()); Action->NodeTemplate = NodeTemplate; } } } } } }