void SAnimationSegmentViewport::InitSkeleton() { UObject *Object = NULL; AnimRefPropertyHandle->GetValue(Object); UAnimSequenceBase *AnimSequence = Cast<UAnimSequenceBase>(Object); USkeleton *Skeleton = NULL; if(AnimSequence != NULL) { Skeleton = AnimSequence->GetSkeleton(); } if( PreviewComponent != NULL && Skeleton != NULL ) { USkeletalMesh* PreviewMesh = Skeleton->GetAssetPreviewMesh(AnimSequence); if (PreviewMesh) { UAnimSingleNodeInstance * Preview = PreviewComponent->PreviewInstance; if((Preview == NULL || Preview->GetCurrentAsset() != AnimSequence) || (PreviewComponent->SkeletalMesh != PreviewMesh)) { PreviewComponent->SetSkeletalMesh(PreviewMesh); PreviewComponent->EnablePreview(true, AnimSequence, NULL); PreviewComponent->PreviewInstance->SetLooping(true); //Place the camera at a good viewer position FVector NewPosition = LevelViewportClient->GetViewLocation(); NewPosition.Normalize(); LevelViewportClient->SetViewLocation(NewPosition * (PreviewMesh->GetImportedBounds().SphereRadius*1.5f)); } } } TargetSkeleton = Skeleton; }
void FAnimMontageSegmentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& SegmentCategory = DetailBuilder.EditCategory("Animation Segment", LOCTEXT("AnimationSegmentCategoryTitle", "Animation Segment") ); SegmentCategory.AddProperty("AnimSegment.AnimReference").DisplayName( LOCTEXT("AnimationReferenceLabel", "Animation Reference") ); SegmentCategory.AddProperty("AnimSegment.AnimStartTime").DisplayName( LOCTEXT("StartTimeLabel", "Start Time") ); SegmentCategory.AddProperty("AnimSegment.AnimEndTime").DisplayName( LOCTEXT("EndTimeLabel", "End Time") ); SegmentCategory.AddProperty("AnimSegment.AnimPlayRate").DisplayName( LOCTEXT("PlayRateLabel", "Play Rate") ); SegmentCategory.AddProperty("AnimSegment.LoopingCount").DisplayName( LOCTEXT("LoopCountLabel", "Loop Count") ); TSharedPtr<IPropertyHandle> InPropertyHandle = DetailBuilder.GetProperty("AnimSegment.AnimReference"); UObject *Object = NULL; InPropertyHandle->GetValue(Object); UAnimSequenceBase *AnimRef = Cast<UAnimSequenceBase>(Object); USkeleton *Skeleton = NULL; if(AnimRef != NULL) { Skeleton = AnimRef->GetSkeleton(); } SegmentCategory.AddCustomRow(FText::GetEmpty(), false) [ SNew(SAnimationSegmentViewport) .Skeleton(Skeleton) .AnimRef(AnimRef) .AnimRefPropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimReference")) .StartTimePropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimStartTime")) .EndTimePropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimEndTime")) ]; }
void UAnimSingleNodeInstance::SetPosition(float InPosition, bool bFireNotifies) { float PreviousTime = CurrentTime; CurrentTime = FMath::Clamp<float>(InPosition, 0.f, GetLength()); if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance()) { CurMontageInstance->SetPosition(CurrentTime); } // Handle notifies // the way AnimInstance handles notifies doesn't work for single node because this does not tick or anything // this will need to handle manually, emptying, it and collect it, and trigger them at once. if (bFireNotifies) { UAnimSequenceBase * SequenceBase = Cast<UAnimSequenceBase> (CurrentAsset); if (SequenceBase) { AnimNotifies.Empty(); TArray<const FAnimNotifyEvent*> Notifies; SequenceBase->GetAnimNotifiesFromDeltaPositions(PreviousTime, CurrentTime, Notifies); if ( Notifies.Num() > 0 ) { // single node instance only has 1 asset at a time AddAnimNotifies(Notifies, 1.0f); } TriggerAnimNotifies(0.f); } } }
FString SAnimSegmentsPanel::GetAnimSegmentName(int32 AnimSegmentIndex) const { if (ValidIndex(AnimSegmentIndex)) { UAnimSequenceBase* AnimReference = AnimTrack->AnimSegments[AnimSegmentIndex].AnimReference; if(AnimReference) { return AnimReference->GetName(); } } return FString(); }
FString SAnimSegmentsPanel::GetAnimSegmentDetailedInfo(int32 AnimSegmentIndex) const { if (ValidIndex(AnimSegmentIndex)) { FAnimSegment& AnimSegment = AnimTrack->AnimSegments[AnimSegmentIndex]; UAnimSequenceBase * Anim = AnimSegment.AnimReference; if ( Anim != NULL ) { return FString::Printf(TEXT("%s %.2f"), *Anim->GetName(), AnimSegment.GetLength() ); } } return FString(); }
void UAnimSingleNodeInstance::SetPosition(float InPosition, bool bFireNotifies) { float PreviousTime = CurrentTime; CurrentTime = FMath::Clamp<float>(InPosition, 0.f, GetLength()); if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance()) { CurMontageInstance->SetPosition(CurrentTime); } // Handle notifies // the way AnimInstance handles notifies doesn't work for single node because this does not tick or anything // this will need to handle manually, emptying, it and collect it, and trigger them at once. if (bFireNotifies) { UAnimSequenceBase * SequenceBase = Cast<UAnimSequenceBase> (CurrentAsset); if (SequenceBase) { AnimNotifies.Empty(); TArray<const FAnimNotifyEvent*> Notifies; SequenceBase->GetAnimNotifiesFromDeltaPositions(PreviousTime, CurrentTime, Notifies); if ( Notifies.Num() > 0 ) { // single node instance only has 1 asset at a time AddAnimNotifies(Notifies, 1.0f); } TriggerAnimNotifies(0.f); // since this is singlenode instance, if position changes, we can't keep old morphtarget curves // we clear it and evaluate curve here with new asset. MorphTargetCurves.Empty(); MaterialParameterCurves.Empty(); // Evaluate Curve data now - even if time did not move, we still need to return curve if it exists SequenceBase->EvaluateCurveData(this, CurrentTime, 1.0); } } }
void FAnimMontageSegmentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& SegmentCategory = DetailBuilder.EditCategory("Animation Segment", LOCTEXT("AnimationSegmentCategoryTitle", "Animation Segment") ); TSharedRef<IPropertyHandle> TargetPropertyHandle = DetailBuilder.GetProperty("AnimSegment.AnimReference"); UProperty* TargetProperty = TargetPropertyHandle->GetProperty(); const UObjectPropertyBase* ObjectProperty = CastChecked<const UObjectPropertyBase>(TargetProperty); IDetailPropertyRow& PropertyRow = SegmentCategory.AddProperty(TargetPropertyHandle); PropertyRow.DisplayName(LOCTEXT("AnimationReferenceLabel", "Animation Reference")); TSharedPtr<SWidget> NameWidget; TSharedPtr<SWidget> ValueWidget; FDetailWidgetRow Row; PropertyRow.GetDefaultWidgets(NameWidget, ValueWidget, Row); bool bAllowClear = !(ObjectProperty->PropertyFlags & CPF_NoClear); SAssignNew(ValueWidget, SObjectPropertyEntryBox) .PropertyHandle(TargetPropertyHandle) .AllowedClass(ObjectProperty->PropertyClass) .AllowClear(bAllowClear) .OnShouldFilterAsset(FOnShouldFilterAsset::CreateSP(this, &FAnimMontageSegmentDetails::OnShouldFilterAnimAsset)); PropertyRow.CustomWidget() .NameContent() .MinDesiredWidth(Row.NameWidget.MinWidth) .MaxDesiredWidth(Row.NameWidget.MaxWidth) [ NameWidget.ToSharedRef() ] .ValueContent() .MinDesiredWidth(Row.ValueWidget.MinWidth) .MaxDesiredWidth(Row.ValueWidget.MaxWidth) [ ValueWidget.ToSharedRef() ]; SegmentCategory.AddProperty("AnimSegment.AnimStartTime").DisplayName( LOCTEXT("StartTimeLabel", "Start Time") ); SegmentCategory.AddProperty("AnimSegment.AnimEndTime").DisplayName( LOCTEXT("EndTimeLabel", "End Time") ); SegmentCategory.AddProperty("AnimSegment.AnimPlayRate").DisplayName( LOCTEXT("PlayRateLabel", "Play Rate") ); SegmentCategory.AddProperty("AnimSegment.LoopingCount").DisplayName( LOCTEXT("LoopCountLabel", "Loop Count") ); TSharedPtr<IPropertyHandle> InPropertyHandle = DetailBuilder.GetProperty("AnimSegment.AnimReference"); UObject *Object = NULL; InPropertyHandle->GetValue(Object); UAnimSequenceBase *AnimRef = Cast<UAnimSequenceBase>(Object); USkeleton *Skeleton = NULL; if(AnimRef != NULL) { Skeleton = AnimRef->GetSkeleton(); } SegmentCategory.AddCustomRow(FText::GetEmpty(), false) [ SNew(SAnimationSegmentViewport) .Skeleton(Skeleton) .AnimRef(AnimRef) .AnimRefPropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimReference")) .StartTimePropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimStartTime")) .EndTimePropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimEndTime")) ]; }