You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira UE-104234
#rb Thomas.Sarkanen, Martin.Wilson, Alexis.Matte, Michael.Zyracki
+ Introduced UAnimDataModel, this currently represents the source data for bone and curve animation. It contains:
+ Bone animation tracks (FBoneAnimationTrack)
+ Key data (coarse)
+ Name
+ Bone tree index
+ Curve data (FAnimationCurveData)
+ Float Curves
+ Transform Curves
+ Play length
+ Sampling rate
+ Used for deriving the expected number of keys/frames when combined with the PlayLength
+ Transient data for supporting backwards compatibility APIs
+ (Dynamic) delegate which broadcasts the mutation Notifies
+ Introduced UAnimDataController, this has sole authority over mutating data contained by UAnimDataModel
+ API functionality allows to transform the contained data in all ways currently expected in the engine.
+ Any mutation will add an undo/redo-able FChange object into the transaction buffer
+ FChangeTransactor helper object allows for keeping track of (compounded) FChange's and inserting them into GUndo
+ Broadcasts change notifies alongside a payload object containing details about the change
+ Interested systems/objects can register to these changes through UAnimDataModel::OnModelModified event
+ Almost all functionality is exposed to both Blueprint and Python scripting
+ Allows for opening 'Brackets', these define the beginning and end of a chain of mutations. Allowing anything registered to OnModelModified to halt responding to the notifies until the (top-level) bracket is closed
+ Undo/redo actions
+ Each mutation to the UAnimDataModel is covered by a 'Action' which is based of FChange and is used to insert a undo/redo-able operation into the transactions buffer
+ Introduced EAnimDataModelNotifType and per-notify payload types. These are used to update views, and any model derived data
+ Introduced FAnimationCurveIdentifier, exposed to scripting, this is used for referencing a curve by name and type when passed to the controller
+ Allows for targetting a specific RichCurve as part of a TransformCurve
+ Introduced FAnimationDataNotifyCollectorused for keeping track of which notifies of type EAnimDataModelNotifType are broadcasted between top-level EAnimDataModelNotifType::BracketOpened and EAnimDataModelNotifType::BracketClosed notifies
+ Added CanTransact to UEngine, returns whether or not a transaction can be made
+ Added UAnimCompositeBase::SetCompositeLength, used for runtime changing of play length value
+ Animation Sequence helpers, containing 'helper' functionality for both AnimSequence and AnimDataModel
* Animation Sequence Base
+ virtual PopulateModel, called during upgrade path for converting existing (legacy) data to the new UAnimDataModel data
+ virtual OnModelModified, registered to the Model's OnModified event to handle any Notifies and payloads
+ Added UAnimDataModel sub-object (editor only)
+ Added UAnimDataController instance (transient and editor only)
+ FAnimationDataNotifyCollector to keep track of model bracketed notifies
* Deprecated
* SetSequenceLength()
* RefreshCurveData()
* MarkRawDataAsModified()
* RegisterOnAnimCurvesChanged()
* UnregisterOnAnimCurvesChanged()
* UnregisterOnAnimTrackCurvesChanged()
* RegisterOnAnimTrackCurvesChanged()
* Public access to RawCurveData
* Animation Sequence
+ Implements PopulateModel/OnModelModified for AnimationSequence related data
+ Added TargetFrameRate, currently locked to the initial sampling rate, but allows for resampling the UAnimDataModel data according to the set rate
+ Added NumberOfSampledKeys (editor only), populated by resampling process
+ Added NumberOfSampledFrames (editor only), populated by resampling process
+ Added ResampledAnimationTrackData (editor only and transient), populated by resampling process
+ Added bBlockCompressionRequests (editor only), used for blocking compression during automation tests
* Deprecated
* SetNumberOfSampledKeys()
* MarkRawDataAsModified()
* GetRawAnimationData()
* GetAnimationTrackNames()
* AddNewRawTrack()
* GetRawTrackToSkeletonMapTable()
* GetRawAnimationTrack()
* GetRawAnimationTrack()
* UpdateFrameRate()
* ExtractBoneTransform()
* CompressRawAnimData()
* GetSkeletonIndexFromRawDataTrackIndex()
* RecycleAnimSequence()
* CleanAnimSequenceForImport()
* CopyNotifies()
* PostProcessSequence()
* AddLoopingInterpolation()
* RemoveAllTracks()
* DoesContainTransformCurves()
* InsertFramesToRawAnimData()
* CropRawAnimData()
* RemoveTrack()
* InsertTrack()
* ResizeSequence()
* Non-editor access to GetUncompressedRawSize()
* Non-editor access to GetApproxRawSize()
* Public access to UpdateCompressedCurveName()
* NumberOfKeys
* SamplingFrameRate
* TrackToSkeletonMapTable
* RawAnimationData
* AnimationTrackNames
* Animation Streamable
* Model from source Sequence is duplicated rather than copying animation data
* Deprecated ERawCurveTrackTypes::RCT_Vector (marked as hidden)
+ Added automated test for
+ All script exposed controller functionality
+ Undo/redo actions
* Updated existing AnimSequence tests with deprecation and new expected data
+ Base data for compression is now populated from the 'resampled' version stored on the AnimSequence
+ Data cleanup and validation is now performed during compression
+ Track sanitization is now performed during compression (normalizing Quats and clamping near-zero values to zero)
+ Key reduction
+ Invalid track (missing bone from skeleton) removal
+ Curve name validation against skeleton
* Replaced RawCurves with Float curves
* Updated DDC key
- Removed all Guid generation related functionality (now part of the model)
* AnimSequenceBase model registers to AnimModel's notify event, used for refreshing the tracks
* Replaces the in-place calling of RefreshTracks()
* All curve tracks now hold a const CurveType* rather than a CurveType*/& for introspection of its data
+ Any mutations now go through the controller API
+ Uses AnimationCurveIdentifier to set Transform's per-channel tracks
* RichCurveEditorModelNamed conformed to model/controller
+ Any modifications are applied to an temporary CurveType which always contains a copy of the const-ptr after any previous mutation
+ Registers to the outer AnimModel's notify event, used for updating the cached curve data
+ Registers to CurveModifiedDelegate, used for copying the temp curve data to the model using SetCurveKeys
* Not-ideal but point that I got to for V1
* Would be replaced with either refactoring FRichCurve to be MVC like, or by calling UAnimDataController functionality from an implementation of FRichCurveEditorModel
* Mark FChange overrides as final for swap/command change permutations
* Changed FCompoundChange GetDescription to return concatenation of all sub-changes
* Added GetDescription to FTransaction which returns the ToString() value of any contained FChange entries.
* Entries in SUndoHistory tab now have a ToolTip showing the GetDescription() value
+ FChangeTransactor helper object allows for keeping track of (compounded) FChange's and inserting them into GUndo
* Deprecation handling, conforming code to new APIs and exposing structure/objects/properties to scripting
* Conforming AnimSequence importing from FBX to Model/Controller changes
[CL 15106211 by Jurre deBaare in ue5-main branch]
261 lines
7.7 KiB
C++
261 lines
7.7 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimationModifier.h"
|
|
#include "Animation/AnimSequence.h"
|
|
#include "Animation/Skeleton.h"
|
|
#include "ModifierOutputFilter.h"
|
|
#include "Editor/Transactor.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
|
|
#include "UObject/ReleaseObjectVersion.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Editor/Transactor.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "UObject/AnimObjectVersion.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "AnimationModifier"
|
|
|
|
UAnimationModifier::UAnimationModifier()
|
|
: PreviouslyAppliedModifier(nullptr)
|
|
{
|
|
}
|
|
|
|
void UAnimationModifier::ApplyToAnimationSequence(class UAnimSequence* InAnimationSequence)
|
|
{
|
|
FEditorScriptExecutionGuard ScriptGuard;
|
|
|
|
checkf(InAnimationSequence, TEXT("Invalid Animation Sequence supplied"));
|
|
CurrentAnimSequence = InAnimationSequence;
|
|
CurrentSkeleton = InAnimationSequence->GetSkeleton();
|
|
|
|
// Filter to check for warnings / errors thrown from animation blueprint library (rudimentary approach for now)
|
|
FCategoryLogOutputFilter OutputLog;
|
|
OutputLog.SetAutoEmitLineTerminator(true);
|
|
OutputLog.AddCategoryName("LogAnimationBlueprintLibrary");
|
|
|
|
GLog->AddOutputDevice(&OutputLog);
|
|
|
|
// Transact the modifier to prevent instance variables/data to change during applying
|
|
FTransaction ModifierTransaction;
|
|
ModifierTransaction.SaveObject(this);
|
|
|
|
FTransaction AnimationDataTransaction;
|
|
AnimationDataTransaction.SaveObject(CurrentAnimSequence);
|
|
AnimationDataTransaction.SaveObject(CurrentSkeleton);
|
|
|
|
/** In case this modifier has been previously applied, revert it using the serialised out version at the time */
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
PreviouslyAppliedModifier->Modify();
|
|
PreviouslyAppliedModifier->OnRevert(CurrentAnimSequence);
|
|
}
|
|
|
|
UAnimDataController* Controller = CurrentAnimSequence->GetController();
|
|
|
|
{
|
|
UAnimDataController::FScopedBracket ScopedBracket(Controller, LOCTEXT("ApplyModifierBracket", "Applying Animation Modifier"));
|
|
/** Reverting and applying, populates the log with possible warnings and or errors to notify the user about */
|
|
OnApply(CurrentAnimSequence);
|
|
}
|
|
|
|
// Apply transaction
|
|
ModifierTransaction.BeginOperation();
|
|
ModifierTransaction.Apply();
|
|
ModifierTransaction.EndOperation();
|
|
|
|
GLog->RemoveOutputDevice(&OutputLog);
|
|
|
|
// Check if warnings or errors have occurred and show dialog to user to inform her about this
|
|
const bool bWarningsOrErrors = OutputLog.ContainsWarnings() || OutputLog.ContainsErrors();
|
|
bool bShouldRevert = false;
|
|
if (bWarningsOrErrors)
|
|
{
|
|
static const FText WarningMessageFormat = FText::FromString("Modifier has generated warnings during a test run:\n\n{0}\nAre you sure you want to Apply it?");
|
|
static const FText ErrorMessageFormat = FText::FromString("Modifier has generated errors (and warnings) during a test run:\n\n{0}\nResolve the Errors before trying to Apply!");
|
|
|
|
EAppMsgType::Type MessageType = OutputLog.ContainsErrors() ? EAppMsgType::Ok : EAppMsgType::YesNo;
|
|
const FText& MessageFormat = OutputLog.ContainsErrors() ? ErrorMessageFormat : WarningMessageFormat;
|
|
const FText MessageTitle = FText::FromString("Modifier has Generated Warnings/Errors");
|
|
bShouldRevert = (FMessageDialog::Open(MessageType, FText::FormatOrdered(MessageFormat, FText::FromString(OutputLog)), &MessageTitle) != EAppReturnType::Yes);
|
|
}
|
|
|
|
// Revert changes if necessary, otherwise post edit and refresh animation data
|
|
if (bShouldRevert)
|
|
{
|
|
AnimationDataTransaction.BeginOperation();
|
|
AnimationDataTransaction.Apply();
|
|
AnimationDataTransaction.EndOperation();
|
|
CurrentAnimSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
/** Mark the previous modifier pending kill, as it will be replaced with the current modifier state */
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
PreviouslyAppliedModifier->MarkPendingKill();
|
|
}
|
|
|
|
PreviouslyAppliedModifier = DuplicateObject(this, GetOuter());
|
|
|
|
CurrentAnimSequence->PostEditChange();
|
|
CurrentSkeleton->PostEditChange();
|
|
CurrentAnimSequence->RefreshCacheData();
|
|
|
|
UpdateStoredRevisions();
|
|
}
|
|
|
|
// Finished
|
|
CurrentAnimSequence = nullptr;
|
|
CurrentSkeleton = nullptr;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateCompressedAnimationData()
|
|
{
|
|
if (CurrentAnimSequence->DoesNeedRecompress())
|
|
{
|
|
CurrentAnimSequence->RequestSyncAnimRecompression(false);
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::RevertFromAnimationSequence(class UAnimSequence* InAnimationSequence)
|
|
{
|
|
FEditorScriptExecutionGuard ScriptGuard;
|
|
|
|
/** Can only revert if previously applied, which means there should be a previous modifier */
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
checkf(InAnimationSequence, TEXT("Invalid Animation Sequence supplied"));
|
|
CurrentAnimSequence = InAnimationSequence;
|
|
CurrentSkeleton = InAnimationSequence->GetSkeleton();
|
|
|
|
// Transact the modifier to prevent instance variables/data to change during reverting
|
|
FTransaction Transaction;
|
|
Transaction.SaveObject(this);
|
|
|
|
PreviouslyAppliedModifier->Modify();
|
|
|
|
UAnimDataController* Controller = CurrentAnimSequence->GetController();
|
|
|
|
{
|
|
UAnimDataController::FScopedBracket ScopedBracket(Controller, LOCTEXT("RevertModifierBracket", "Reverting Animation Modifier"));
|
|
PreviouslyAppliedModifier->OnRevert(CurrentAnimSequence);
|
|
}
|
|
|
|
// Apply transaction
|
|
Transaction.BeginOperation();
|
|
Transaction.Apply();
|
|
Transaction.EndOperation();
|
|
|
|
CurrentAnimSequence->PostEditChange();
|
|
CurrentSkeleton->PostEditChange();
|
|
CurrentAnimSequence->RefreshCacheData();
|
|
|
|
ResetStoredRevisions();
|
|
|
|
// Finished
|
|
CurrentAnimSequence = nullptr;
|
|
CurrentSkeleton = nullptr;
|
|
|
|
PreviouslyAppliedModifier->MarkPendingKill();
|
|
PreviouslyAppliedModifier = nullptr;
|
|
}
|
|
}
|
|
|
|
bool UAnimationModifier::IsLatestRevisionApplied() const
|
|
{
|
|
return (AppliedGuid == RevisionGuid);
|
|
}
|
|
|
|
void UAnimationModifier::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
UpdateNativeRevisionGuid();
|
|
|
|
// Ensure we always have a valid guid
|
|
if (!RevisionGuid.IsValid())
|
|
{
|
|
UpdateRevisionGuid(GetClass());
|
|
MarkPackageDirty();
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
Ar.UsingCustomVersion(FReleaseObjectVersion::GUID);
|
|
|
|
/** Back-wards compatibility, assume the current modifier as previously applied */
|
|
if (Ar.CustomVer(FReleaseObjectVersion::GUID) < FReleaseObjectVersion::SerializeAnimModifierState)
|
|
{
|
|
PreviouslyAppliedModifier = DuplicateObject(this, GetOuter());
|
|
}
|
|
}
|
|
|
|
const USkeleton* UAnimationModifier::GetSkeleton()
|
|
{
|
|
return CurrentSkeleton;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateRevisionGuid(UClass* ModifierClass)
|
|
{
|
|
RevisionGuid = FGuid::NewGuid();
|
|
|
|
// Native classes are more difficult?
|
|
for (TObjectIterator<UAnimationModifier> It; It; ++It)
|
|
{
|
|
if (*It != this && It->GetClass() == ModifierClass)
|
|
{
|
|
It->SetInstanceRevisionGuid(RevisionGuid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::UpdateNativeRevisionGuid()
|
|
{
|
|
UClass* Class = GetClass();
|
|
// Check if this is the class default object
|
|
if (this == GetDefault<UAnimationModifier>(Class))
|
|
{
|
|
// If so check whether or not the config stored revision matches the natively defined one
|
|
if (StoredNativeRevision != GetNativeClassRevision())
|
|
{
|
|
// If not update the blueprint revision GUID
|
|
UpdateRevisionGuid(Class);
|
|
StoredNativeRevision = GetNativeClassRevision();
|
|
|
|
MarkPackageDirty();
|
|
|
|
// Save the new native revision to config files
|
|
SaveConfig();
|
|
UpdateDefaultConfigFile();
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 UAnimationModifier::GetNativeClassRevision() const
|
|
{
|
|
// Overriden in derrived classes to perform native revisioning
|
|
return 0;
|
|
}
|
|
|
|
const UAnimSequence* UAnimationModifier::GetAnimationSequence()
|
|
{
|
|
return CurrentAnimSequence;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateStoredRevisions()
|
|
{
|
|
AppliedGuid = RevisionGuid;
|
|
}
|
|
|
|
void UAnimationModifier::ResetStoredRevisions()
|
|
{
|
|
AppliedGuid.Invalidate();
|
|
}
|
|
|
|
void UAnimationModifier::SetInstanceRevisionGuid(FGuid Guid)
|
|
{
|
|
RevisionGuid = Guid;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |