2014-12-07 19:09:38 -05:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
#include "MovieSceneToolsPrivatePCH.h"
|
|
|
|
|
#include "ScopedTransaction.h"
|
|
|
|
|
#include "MovieScene.h"
|
|
|
|
|
#include "MovieSceneSection.h"
|
2014-09-08 11:46:06 -04:00
|
|
|
#include "MovieScene3DTransformTrack.h"
|
|
|
|
|
#include "MovieScene3DTransformSection.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
#include "ISequencerObjectChangeListener.h"
|
|
|
|
|
#include "ISequencerSection.h"
|
|
|
|
|
#include "ISectionLayoutBuilder.h"
|
|
|
|
|
#include "IKeyArea.h"
|
|
|
|
|
#include "MovieSceneToolHelpers.h"
|
|
|
|
|
#include "MovieSceneTrackEditor.h"
|
|
|
|
|
#include "TransformTrackEditor.h"
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class that draws a transform section in the sequencer
|
|
|
|
|
*/
|
2014-09-08 11:46:06 -04:00
|
|
|
class F3DTransformSection : public ISequencerSection
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
public:
|
2014-09-08 11:46:06 -04:00
|
|
|
F3DTransformSection( UMovieSceneSection& InSection )
|
2014-03-14 14:13:41 -04:00
|
|
|
: Section( InSection )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** ISequencerSection interface */
|
2014-06-13 06:14:46 -04:00
|
|
|
virtual UMovieSceneSection* GetSectionObject() override
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
return &Section;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-18 14:57:34 -04:00
|
|
|
virtual FText GetDisplayName() const override
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-06-18 14:57:34 -04:00
|
|
|
return NSLOCTEXT("FTransformSection", "DisplayName", "Transform");
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-06-18 14:57:34 -04:00
|
|
|
virtual FText GetSectionTitle() const override { return FText::GetEmpty(); }
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-06-13 06:14:46 -04:00
|
|
|
virtual void GenerateSectionLayout( class ISectionLayoutBuilder& LayoutBuilder ) const override
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-09-08 11:46:06 -04:00
|
|
|
UMovieScene3DTransformSection* TransformSection = Cast<UMovieScene3DTransformSection>( &Section );
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// This generates the tree structure for the transform section
|
2014-06-18 14:57:34 -04:00
|
|
|
LayoutBuilder.PushCategory( "Location", NSLOCTEXT("FTransformSection", "LocationArea", "Location") );
|
2015-02-16 11:59:08 -05:00
|
|
|
LayoutBuilder.AddKeyArea("Location.X", NSLOCTEXT("FTransformSection", "LocXArea", "X"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetTranslationCurve( EAxis::X ), TransformSection ) ) );
|
|
|
|
|
LayoutBuilder.AddKeyArea("Location.Y", NSLOCTEXT("FTransformSection", "LocYArea", "Y"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetTranslationCurve( EAxis::Y ), TransformSection ) ) );
|
|
|
|
|
LayoutBuilder.AddKeyArea("Location.Z", NSLOCTEXT("FTransformSection", "LocZArea", "Z"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetTranslationCurve( EAxis::Z ), TransformSection ) ) );
|
2014-03-14 14:13:41 -04:00
|
|
|
LayoutBuilder.PopCategory();
|
|
|
|
|
|
2014-06-18 14:57:34 -04:00
|
|
|
LayoutBuilder.PushCategory( "Rotation", NSLOCTEXT("FTransformSection", "RotationArea", "Rotation") );
|
2015-02-16 11:59:08 -05:00
|
|
|
LayoutBuilder.AddKeyArea("Rotation.X", NSLOCTEXT("FTransformSection", "RotXArea", "X"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetRotationCurve( EAxis::X ), TransformSection ) ) );
|
|
|
|
|
LayoutBuilder.AddKeyArea("Rotation.Y", NSLOCTEXT("FTransformSection", "RotYArea", "Y"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetRotationCurve( EAxis::Y ), TransformSection ) ) );
|
|
|
|
|
LayoutBuilder.AddKeyArea("Rotation.Z", NSLOCTEXT("FTransformSection", "RotZArea", "Z"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetRotationCurve( EAxis::Z ), TransformSection ) ) );
|
2014-03-14 14:13:41 -04:00
|
|
|
LayoutBuilder.PopCategory();
|
|
|
|
|
|
2014-06-18 14:57:34 -04:00
|
|
|
LayoutBuilder.PushCategory( "Scale", NSLOCTEXT("FTransformSection", "ScaleArea", "Scale") );
|
2015-02-16 11:59:08 -05:00
|
|
|
LayoutBuilder.AddKeyArea("Scale.X", NSLOCTEXT("FTransformSection", "ScaleXArea", "X"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetScaleCurve( EAxis::X ), TransformSection ) ) );
|
|
|
|
|
LayoutBuilder.AddKeyArea("Scale.Y", NSLOCTEXT("FTransformSection", "ScaleYArea", "Y"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetScaleCurve( EAxis::Y ), TransformSection ) ) );
|
|
|
|
|
LayoutBuilder.AddKeyArea("Scale.Z", NSLOCTEXT("FTransformSection", "ScaleZArea", "Z"), MakeShareable( new FFloatCurveKeyArea ( &TransformSection->GetScaleCurve( EAxis::Z ), TransformSection ) ) );
|
2014-03-14 14:13:41 -04:00
|
|
|
LayoutBuilder.PopCategory();
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 06:14:46 -04:00
|
|
|
virtual int32 OnPaintSection( const FGeometry& AllottedGeometry, const FSlateRect& SectionClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, bool bParentEnabled ) const override
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
// Add a box for the section
|
|
|
|
|
FSlateDrawElement::MakeBox(
|
|
|
|
|
OutDrawElements,
|
|
|
|
|
LayerId,
|
|
|
|
|
AllottedGeometry.ToPaintGeometry(),
|
|
|
|
|
FEditorStyle::GetBrush("Sequencer.GenericSection.Background"),
|
|
|
|
|
SectionClippingRect
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return LayerId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
/** The section we are visualizing */
|
|
|
|
|
UMovieSceneSection& Section;
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
F3DTransformTrackEditor::F3DTransformTrackEditor( TSharedRef<ISequencer> InSequencer )
|
2014-03-14 14:13:41 -04:00
|
|
|
: FMovieSceneTrackEditor( InSequencer )
|
|
|
|
|
{
|
|
|
|
|
// Listen for actor/component movement
|
2014-09-08 11:46:06 -04:00
|
|
|
GEditor->OnBeginObjectMovement().AddRaw( this, &F3DTransformTrackEditor::OnPreTransformChanged );
|
|
|
|
|
GEditor->OnEndObjectMovement().AddRaw( this, &F3DTransformTrackEditor::OnTransformChanged );
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
F3DTransformTrackEditor::~F3DTransformTrackEditor()
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
GEditor->OnBeginObjectMovement().RemoveAll( this );
|
|
|
|
|
GEditor->OnEndObjectMovement().RemoveAll( this );
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
TSharedRef<FMovieSceneTrackEditor> F3DTransformTrackEditor::CreateTrackEditor( TSharedRef<ISequencer> InSequencer )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-09-08 11:46:06 -04:00
|
|
|
return MakeShareable( new F3DTransformTrackEditor( InSequencer ) );
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
bool F3DTransformTrackEditor::SupportsType( TSubclassOf<UMovieSceneTrack> Type ) const
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
// We support animatable transforms
|
2014-09-08 11:46:06 -04:00
|
|
|
return Type == UMovieScene3DTransformTrack::StaticClass();
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
TSharedRef<ISequencerSection> F3DTransformTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack* Track )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
check( SupportsType( SectionObject.GetOuter()->GetClass() ) );
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
TSharedRef<ISequencerSection> NewSection( new F3DTransformSection( SectionObject ) );
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
return NewSection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
void F3DTransformTrackEditor::OnPreTransformChanged( UObject& InObject )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
UMovieScene* MovieScene = GetMovieScene();
|
|
|
|
|
float AutoKeyTime = GetTimeForKey( MovieScene );
|
|
|
|
|
|
|
|
|
|
if( IsAllowedToAutoKey() )
|
|
|
|
|
{
|
|
|
|
|
USceneComponent* SceneComponent = NULL;
|
|
|
|
|
AActor* Actor = Cast<AActor>( &InObject );
|
|
|
|
|
if( Actor && Actor->GetRootComponent() )
|
|
|
|
|
{
|
|
|
|
|
SceneComponent = Actor->GetRootComponent();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If the object wasn't an actor attempt to get it directly as a scene component
|
|
|
|
|
SceneComponent = Cast<USceneComponent>( &InObject );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( SceneComponent )
|
|
|
|
|
{
|
|
|
|
|
// Cache off the existing transform so we can detect which components have changed
|
|
|
|
|
// and keys only when something has changed
|
|
|
|
|
FTransformData Transform( SceneComponent );
|
|
|
|
|
|
|
|
|
|
ObjectToExistingTransform.Add( &InObject, Transform );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Temp struct used because delegates only accept 4 or less payloads
|
|
|
|
|
* FTransformKey is immutable and would require heavy re-architecting to fit here
|
|
|
|
|
*/
|
|
|
|
|
struct FTransformDataPair
|
|
|
|
|
{
|
|
|
|
|
FTransformDataPair(FTransformData InTransformData, FTransformData InLastTransformData)
|
|
|
|
|
: TransformData(InTransformData)
|
|
|
|
|
, LastTransformData(InLastTransformData) {}
|
|
|
|
|
|
|
|
|
|
FTransformData TransformData;
|
|
|
|
|
FTransformData LastTransformData;
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
void F3DTransformTrackEditor::OnTransformChanged( UObject& InObject )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
const TSharedPtr<ISequencer> Sequencer = GetSequencer();
|
|
|
|
|
|
|
|
|
|
USceneComponent* SceneComponentThatChanged = NULL;
|
|
|
|
|
|
|
|
|
|
// The runtime binding
|
|
|
|
|
FGuid ObjectHandle;
|
|
|
|
|
|
|
|
|
|
AActor* Actor = Cast<AActor>( &InObject );
|
|
|
|
|
if( Actor && Actor->GetRootComponent() )
|
|
|
|
|
{
|
|
|
|
|
// Get a handle bound to the key/section we are adding so we know what objects to change during playback
|
|
|
|
|
ObjectHandle = Sequencer->GetHandleToObject( Actor );
|
|
|
|
|
SceneComponentThatChanged = Actor->GetRootComponent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If the object wasn't an actor attempt to get it directly as a scene component
|
|
|
|
|
SceneComponentThatChanged = Cast<USceneComponent>( &InObject );
|
|
|
|
|
if( SceneComponentThatChanged )
|
|
|
|
|
{
|
|
|
|
|
ObjectHandle = Sequencer->GetHandleToObject( SceneComponentThatChanged );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-24 23:52:28 -04:00
|
|
|
if( SceneComponentThatChanged && ObjectHandle.IsValid() )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
// Find an existing transform if possible. If one exists we will compare against the new one to decide what components of the transform need keys
|
|
|
|
|
FTransformData ExistingTransform = ObjectToExistingTransform.FindRef( &InObject );
|
|
|
|
|
|
|
|
|
|
// Remove it from the list of cached transforms.
|
|
|
|
|
// @todo sequencer livecapture: This can be made much for efficient by not removing cached state during live capture situation
|
|
|
|
|
ObjectToExistingTransform.Remove( &InObject );
|
|
|
|
|
|
|
|
|
|
// Build new transform data
|
|
|
|
|
FTransformData NewTransformData( SceneComponentThatChanged );
|
|
|
|
|
|
|
|
|
|
FTransformDataPair TransformPair(NewTransformData, ExistingTransform);
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
AnimatablePropertyChanged(UMovieScene3DTransformTrack::StaticClass(), true,
|
|
|
|
|
FOnKeyProperty::CreateRaw(this, &F3DTransformTrackEditor::OnTransformChangedInternals, &InObject, ObjectHandle, TransformPair, true));
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
void F3DTransformTrackEditor::AddKey(const FGuid& ObjectGuid, UObject* AdditionalAsset)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
TArray<UObject*> OutObjects;
|
|
|
|
|
GetSequencer()->GetRuntimeObjects( GetSequencer()->GetFocusedMovieSceneInstance(), ObjectGuid, OutObjects);
|
|
|
|
|
|
2014-06-16 16:37:22 -04:00
|
|
|
for ( UObject* Object : OutObjects )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
FGuid ObjectHandle;
|
|
|
|
|
USceneComponent* SceneComponent = NULL;
|
|
|
|
|
AActor* Actor = Cast<AActor>( Object );
|
|
|
|
|
if( Actor && Actor->GetRootComponent() )
|
|
|
|
|
{
|
|
|
|
|
ObjectHandle = GetSequencer()->GetHandleToObject( Actor );
|
|
|
|
|
SceneComponent = Actor->GetRootComponent();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If the object wasn't an actor attempt to get it directly as a scene component
|
|
|
|
|
SceneComponent = Cast<USceneComponent>( Object );
|
|
|
|
|
if( SceneComponent )
|
|
|
|
|
{
|
|
|
|
|
ObjectHandle = GetSequencer()->GetHandleToObject( SceneComponent );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( SceneComponent )
|
|
|
|
|
{
|
|
|
|
|
// Cache off the existing transform so we can detect which components have changed
|
|
|
|
|
// and keys only when something has changed
|
|
|
|
|
FTransformData CurrentTransform( SceneComponent );
|
|
|
|
|
|
|
|
|
|
FTransformDataPair TransformPair(CurrentTransform, FTransformData());
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
AnimatablePropertyChanged(UMovieScene3DTransformTrack::StaticClass(), false,
|
|
|
|
|
FOnKeyProperty::CreateRaw(this, &F3DTransformTrackEditor::OnTransformChangedInternals, Object, ObjectHandle, TransformPair, false));
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 11:46:06 -04:00
|
|
|
void F3DTransformTrackEditor::OnTransformChangedInternals(float KeyTime, UObject* InObject, FGuid ObjectHandle, FTransformDataPair TransformPair, bool bAutoKeying)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
// Only unwind rotation if we're generating keys while recording (scene is actively playing back)
|
|
|
|
|
const bool bUnwindRotation = GetSequencer()->IsRecordingLive();
|
|
|
|
|
|
|
|
|
|
FName Transform("Transform");
|
2014-06-16 16:37:22 -04:00
|
|
|
if (ObjectHandle.IsValid())
|
|
|
|
|
{
|
2014-09-08 11:46:06 -04:00
|
|
|
UMovieSceneTrack* Track = GetTrackForObject( ObjectHandle, UMovieScene3DTransformTrack::StaticClass(), Transform );
|
|
|
|
|
UMovieScene3DTransformTrack* TransformTrack = CastChecked<UMovieScene3DTransformTrack>( Track );
|
2014-07-28 15:15:42 -04:00
|
|
|
// Transform name and path are the same
|
|
|
|
|
TransformTrack->SetPropertyNameAndPath( Transform, Transform.ToString() );
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-06-16 16:37:22 -04:00
|
|
|
if (!TransformPair.LastTransformData.IsValid())
|
|
|
|
|
{
|
|
|
|
|
bool bHasTranslationKeys = false, bHasRotationKeys = false, bHasScaleKeys = false;
|
|
|
|
|
TransformPair.LastTransformData.bValid = TransformTrack->Eval(KeyTime, KeyTime, TransformPair.LastTransformData.Translation, TransformPair.LastTransformData.Rotation, TransformPair.LastTransformData.Scale, bHasTranslationKeys, bHasRotationKeys, bHasScaleKeys);
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-06-16 16:37:22 -04:00
|
|
|
FTransformKey TransformKey = FTransformKey(KeyTime, TransformPair.TransformData, TransformPair.LastTransformData);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-06-16 16:37:22 -04:00
|
|
|
bool bSuccessfulAdd = TransformTrack->AddKeyToSection( ObjectHandle, TransformKey, bUnwindRotation );
|
|
|
|
|
if (bSuccessfulAdd)
|
|
|
|
|
{
|
|
|
|
|
TransformTrack->SetAsShowable();
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|