void UMovieSceneShotTrack::FixupSurroundingShots( UMovieSceneSection& Section, bool bDelete ) { // Find the previous section and extend it to take the place of the section being deleted int32 SectionIndex = INDEX_NONE; if( SubMovieSceneSections.Find( &Section, SectionIndex ) ) { int32 PrevSectionIndex = SectionIndex - 1; if( SubMovieSceneSections.IsValidIndex( PrevSectionIndex ) ) { // Extend the previous section SubMovieSceneSections[PrevSectionIndex]->SetEndTime( bDelete ? Section.GetEndTime() : Section.GetStartTime() ); } if( !bDelete ) { int32 NextSectionIndex = SectionIndex + 1; if(SubMovieSceneSections.IsValidIndex(NextSectionIndex)) { // Shift the next shot's start time so that it starts when the new shot ends SubMovieSceneSections[NextSectionIndex]->SetStartTime(Section.GetEndTime()); } } } SortShots(); }
TOptional<FSectionHandle> FVirtualTrackArea::HitTestSection(FVector2D InPhysicalPosition) const { TSharedPtr<FSequencerDisplayNode> Node = HitTestNode(InPhysicalPosition.Y); if (Node.IsValid()) { TSharedPtr<FSequencerTrackNode> TrackNode = GetParentTrackNode(*Node); if (TrackNode.IsValid()) { float Time = PixelToTime(InPhysicalPosition.X); const auto& Sections = TrackNode->GetSections(); for (int32 Index = 0; Index < Sections.Num(); ++Index) { UMovieSceneSection* Section = Sections[Index]->GetSectionObject(); if (Section->IsTimeWithinSection(Time)) { return FSectionHandle(TrackNode.ToSharedRef(), Index); } } } } return TOptional<FSectionHandle>(); }
UMovieSceneSection* UMovieSceneSection::SplitSection(float SplitTime) { if (!IsTimeWithinSection(SplitTime)) { return nullptr; } SetFlags(RF_Transactional); if (TryModify()) { float SectionEndTime = GetEndTime(); // Trim off the right SetEndTime(SplitTime); // Create a new section UMovieSceneTrack* Track = CastChecked<UMovieSceneTrack>(GetOuter()); Track->Modify(); UMovieSceneSection* NewSection = DuplicateObject<UMovieSceneSection>(this, Track); check(NewSection); NewSection->SetStartTime(SplitTime); NewSection->SetEndTime(SectionEndTime); Track->AddSection(*NewSection); return NewSection; } return nullptr; }
void FMoveKeys::OnBeginDrag(const FVector2D& LocalMousePos, TSharedPtr<FTrackNode> SequencerNode) { check( SelectedKeys.Num() > 0 ) // Begin an editor transaction and mark the section as transactional so it's state will be saved GEditor->BeginTransaction( NSLOCTEXT("Sequencer", "MoveKeysTransaction", "Move Keys") ); TSet<UMovieSceneSection*> ModifiedSections; for( FSelectedKey SelectedKey : SelectedKeys ) { UMovieSceneSection* OwningSection = SelectedKey.Section; // Only modify sections once if( !ModifiedSections.Contains( OwningSection ) ) { OwningSection->SetFlags( RF_Transactional ); // Save the current state of the section OwningSection->Modify(); // Section has been modified ModifiedSections.Add( OwningSection ); } } }
TSharedRef<ISequencerSection> F2DTransformTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack& Track ) { check( SupportsType( SectionObject.GetOuter()->GetClass() ) ); UClass* SectionClass = SectionObject.GetOuter()->GetClass(); return MakeShareable( new F2DTransformSection( SectionObject, Track.GetTrackName() ) ); }
void FSequencerDragOperation::BeginTransaction( UMovieSceneSection& Section, const FText& TransactionDesc ) { // Begin an editor transaction and mark the section as transactional so it's state will be saved GEditor->BeginTransaction( TransactionDesc ); Section.SetFlags( RF_Transactional ); // Save the current state of the section Section.Modify(); }
TSharedRef<ISequencerSection> FMarginTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack* Track ) { check( SupportsType( SectionObject.GetOuter()->GetClass() ) ); UClass* SectionClass = SectionObject.GetOuter()->GetClass(); TSharedRef<ISequencerSection> NewSection = MakeShareable( new FMarginPropertySection( SectionObject, Track->GetTrackName() ) ); return NewSection; }
UMovieSceneSection* UMovieSceneAnimationTrack::GetAnimSectionAtTime(float Time) { for (int32 i = 0; i < AnimationSections.Num(); ++i) { UMovieSceneSection* Section = AnimationSections[i]; if( Section->IsTimeWithinSection( Time ) ) { return Section; } } return NULL; }
virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) { if (Sequencer.Pin()->IsShotFilteringOn()) { TArray< TWeakObjectPtr<UMovieSceneSection> > FilterShots = Sequencer.Pin()->GetFilteringShotSections(); // if there are filtering shots, continuously update the cached filter zones // we do this in tick, and cache it in order to make animation work properly CachedFilteredRanges.Empty(); for (int32 i = 0; i < FilterShots.Num(); ++i) { UMovieSceneSection* Filter = FilterShots[i].Get(); CachedFilteredRanges.Add(Filter->GetRange()); } } }
UMovieSceneSection* MovieSceneHelpers::FindSectionAtTime( const TArray<UMovieSceneSection*>& Sections, float Time ) { for( int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex ) { UMovieSceneSection* Section = Sections[SectionIndex]; //@todo Sequencer - There can be multiple sections overlapping in time. Returning instantly does not account for that. if( Section->IsTimeWithinSection( Time ) && Section->IsActive() ) { return Section; } } return NULL; }
TSharedRef<ISequencerSection> FParticleTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack& Track ) { check( SupportsType( SectionObject.GetOuter()->GetClass() ) ); const TSharedPtr<ISequencer> OwningSequencer = GetSequencer(); return MakeShareable( new FParticleSection( SectionObject, OwningSequencer.ToSharedRef() ) ); }
UMovieSceneSection* MovieSceneHelpers::FindNearestSectionAtTime( const TArray<UMovieSceneSection*>& Sections, float Time ) { // Only update the section if the position is within the time span of the section // Or if there are no sections at the time, the left closest section to the time // Or in the case that Time is before all sections, take the section with the earliest start time UMovieSceneSection* ClosestSection = nullptr; float ClosestSectionTime = 0.f; UMovieSceneSection* EarliestSection = nullptr; float EarliestSectionTime = 0.f; for( int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex ) { UMovieSceneSection* Section = Sections[SectionIndex]; if (Section->IsActive()) { //@todo Sequencer - There can be multiple sections overlapping in time. Returning instantly does not account for that. if( Section->IsTimeWithinSection( Time ) ) { return Section; } float EndTime = Section->GetEndTime(); if (EndTime < Time) { float ClosestTime = Time - EndTime; if (!ClosestSection || ClosestTime < ClosestSectionTime) { ClosestSection = Section; ClosestSectionTime = ClosestTime; } } float StartTime = Section->GetStartTime(); if (!EarliestSection || StartTime < EarliestSectionTime) { EarliestSection = Section; EarliestSectionTime = StartTime; } } } // if we get here, we are off of any section // if ClosestSection, then we take the closest to left of this time // else, we take the EarliestSection // if that's nullptr, then there are no sections return ClosestSection ? ClosestSection : EarliestSection; }
TSharedRef<ISequencerSection> FAnimationTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack* Track ) { check( SupportsType( SectionObject.GetOuter()->GetClass() ) ); TSharedRef<ISequencerSection> NewSection( new FAnimationSection(SectionObject) ); return NewSection; }
FReply SAnimationOutlinerTreeNode::OnAddKeyClicked() { FSequencer& Sequencer = DisplayNode->GetSequencer(); float CurrentTime = Sequencer.GetCurrentLocalTime(*Sequencer.GetFocusedMovieScene()); TSet<TSharedPtr<IKeyArea>> KeyAreas; GetAllKeyAreas(DisplayNode, KeyAreas); FScopedTransaction Transaction(LOCTEXT("AddKeys", "Add keys at current time")); for (TSharedPtr<IKeyArea> KeyArea : KeyAreas) { UMovieSceneSection* OwningSection = KeyArea->GetOwningSection(); OwningSection->SetFlags(RF_Transactional); OwningSection->Modify(); KeyArea->AddKeyUnique(CurrentTime); } return FReply::Handled(); }
void FNameCurveKeyArea::PasteKeys(const FMovieSceneClipboardKeyTrack& KeyTrack, const FMovieSceneClipboardEnvironment& SrcEnvironment, const FSequencerPasteEnvironment& DstEnvironment) { float PasteAt = DstEnvironment.CardinalTime; KeyTrack.IterateKeys([&](const FMovieSceneClipboardKey& Key){ UMovieSceneSection* Section = GetOwningSection(); if (!Section) { return true; } if (Section->TryModify()) { float Time = PasteAt + Key.GetTime(); if (Section->GetStartTime() > Time) { Section->SetStartTime(Time); } if (Section->GetEndTime() < Time) { Section->SetEndTime(Time); } FKeyHandle KeyHandle = Curve.UpdateOrAddKey(Time, Key.GetValue<FName>()); DstEnvironment.ReportPastedKey(KeyHandle, *this); } return true; }); }
TArray<UMovieSceneSection*> MovieSceneHelpers::GetTraversedSections( const TArray<UMovieSceneSection*>& Sections, float CurrentTime, float PreviousTime ) { TArray<UMovieSceneSection*> TraversedSections; bool bPlayingBackwards = CurrentTime - PreviousTime < 0.0f; float MaxTime = bPlayingBackwards ? PreviousTime : CurrentTime; float MinTime = bPlayingBackwards ? CurrentTime : PreviousTime; TRange<float> SliderRange(MinTime, MaxTime); for( int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex ) { UMovieSceneSection* Section = Sections[SectionIndex]; if( Section->GetStartTime() == CurrentTime || SliderRange.Overlaps( TRange<float>( Section->GetRange() ) ) ) { TraversedSections.Add( Section ); } } return TraversedSections; }
TSharedRef<ISequencerSection> F3DAttachTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack& Track, FGuid ObjectBinding ) { check( SupportsType( SectionObject.GetOuter()->GetClass() ) ); return MakeShareable( new F3DAttachSection( SectionObject, this ) ); }
void FMoveKeys::OnDrag( const FPointerEvent& MouseEvent, const FVector2D& LocalMousePos, const FTimeToPixel& TimeToPixelConverter, TSharedPtr<FTrackNode> SequencerNode ) { // Convert the delta position to a delta time amount that was moved float MouseTime = TimeToPixelConverter.PixelToTime(LocalMousePos.X); float SelectedKeyTime = DraggedKey.KeyArea->GetKeyTime(DraggedKey.KeyHandle.GetValue()); float DistanceMoved = MouseTime - SelectedKeyTime; if( DistanceMoved != 0.0f ) { float TimeDelta = DistanceMoved; // Snapping if ( Settings->GetIsSnapEnabled() ) { bool bSnappedToKeyTime = false; if ( Settings->GetSnapKeyTimesToKeys() ) { TArray<float> OutSnapTimes; GetKeySnapTimes(OutSnapTimes, SequencerNode); TArray<float> InitialTimes; for ( FSelectedKey SelectedKey : SelectedKeys ) { InitialTimes.Add(SelectedKey.KeyArea->GetKeyTime(SelectedKey.KeyHandle.GetValue()) + DistanceMoved); } float OutInitialTime = 0.f; float OutSnapTime = 0.f; if ( SnapToTimes( InitialTimes, OutSnapTimes, TimeToPixelConverter, OutInitialTime, OutSnapTime ) ) { bSnappedToKeyTime = true; TimeDelta = OutSnapTime - (OutInitialTime - DistanceMoved); } } if ( bSnappedToKeyTime == false && Settings->GetSnapKeyTimesToInterval() ) { TimeDelta = Settings->SnapTimeToInterval( MouseTime ) - SelectedKeyTime; } } for( FSelectedKey SelectedKey : SelectedKeys ) { UMovieSceneSection* Section = SelectedKey.Section; TSharedPtr<IKeyArea>& KeyArea = SelectedKey.KeyArea; // Tell the key area to move the key. We reset the key index as a result of the move because moving a key can change it's internal index KeyArea->MoveKey( SelectedKey.KeyHandle.GetValue(), TimeDelta ); // Update the key that moved float NewKeyTime = KeyArea->GetKeyTime( SelectedKey.KeyHandle.GetValue() ); // If the key moves outside of the section resize the section to fit the key // @todo Sequencer - Doesn't account for hitting other sections if( NewKeyTime > Section->GetEndTime() ) { Section->SetEndTime( NewKeyTime ); } else if( NewKeyTime < Section->GetStartTime() ) { Section->SetStartTime( NewKeyTime ); } } } }
TSharedRef<ISequencerSection> FFaceFXAnimationTrackEditor::MakeSectionInterface(UMovieSceneSection& SectionObject, UMovieSceneTrack& Track) { check(SupportsType(SectionObject.GetOuter()->GetClass())); return MakeShareable(new FFaceFXAnimationSection(SectionObject)); }
TSharedRef<ISequencerSection> FCameraCutTrackEditor::MakeSectionInterface(UMovieSceneSection& SectionObject, UMovieSceneTrack& Track, FGuid ObjectBinding) { check(SupportsType(SectionObject.GetOuter()->GetClass())); return MakeShareable(new FCameraCutSection(GetSequencer(), ThumbnailPool, SectionObject)); }
UMovieSceneSection* UMovieScenePropertyTrack::FindOrAddSection( float Time ) { // Find a spot for the section so that they are sorted by start time for( int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex ) { UMovieSceneSection* Section = Sections[SectionIndex]; if( Section->IsTimeWithinSection( Time ) ) { return Section; } // Check if there are no more sections that would overlap the time if( !Sections.IsValidIndex( SectionIndex+1 ) || Sections[SectionIndex+1]->GetStartTime() > Time ) { // No sections overlap the time if( SectionIndex > 0 ) { // Append and grow the previous section UMovieSceneSection* PreviousSection = Sections[ SectionIndex ? SectionIndex-1 : 0 ]; PreviousSection->SetEndTime( Time ); return PreviousSection; } else if( Sections.IsValidIndex( SectionIndex+1 ) ) { // Prepend and grow the next section because there are no sections before this one UMovieSceneSection* NextSection = Sections[SectionIndex+1]; NextSection->SetStartTime( Time ); return NextSection; } else { // SectionIndex == 0 UMovieSceneSection* PreviousSection = Sections[0]; if( PreviousSection->GetEndTime() < Time ) { // Append and grow the section PreviousSection->SetEndTime( Time ); } else { // Prepend and grow the section PreviousSection->SetStartTime( Time ); } return PreviousSection; } } } check( Sections.Num() == 0 ); // Add a new section that starts and ends at the same time UMovieSceneSection* NewSection = CreateNewSection(); NewSection->SetStartTime( Time ); NewSection->SetEndTime( Time ); Sections.Add( NewSection ); return NewSection; }
FSequencerSelectedKey FVirtualTrackArea::HitTestKey(FVector2D InPhysicalPosition) const { TSharedPtr<FSequencerDisplayNode> Node = HitTestNode(InPhysicalPosition.Y); if (!Node.IsValid()) { return FSequencerSelectedKey(); } const float KeyLeft = PixelToTime(InPhysicalPosition.X - SequencerSectionConstants::KeySize.X/2); const float KeyRight = PixelToTime(InPhysicalPosition.X + SequencerSectionConstants::KeySize.X/2); TArray<TSharedRef<IKeyArea>> KeyAreas; // First check for a key area node on the hit-tested node TSharedPtr<FSequencerSectionKeyAreaNode> KeyAreaNode; switch (Node->GetType()) { case ESequencerNode::KeyArea: KeyAreaNode = StaticCastSharedPtr<FSequencerSectionKeyAreaNode>(Node); break; case ESequencerNode::Track: KeyAreaNode = StaticCastSharedPtr<FSequencerTrackNode>(Node)->GetTopLevelKeyNode(); break; } if (KeyAreaNode.IsValid()) { for (auto& KeyArea : KeyAreaNode->GetAllKeyAreas()) { UMovieSceneSection* Section = KeyArea->GetOwningSection(); if (Section->GetStartTime() <= KeyRight && Section->GetEndTime() >= KeyLeft) { KeyAreas.Add(KeyArea); } } } // Failing that, and the node is collapsed, we check for key groupings else if (!Node->IsExpanded()) { TSharedPtr<FSequencerTrackNode> TrackNode = GetParentTrackNode(*Node); const TArray< TSharedRef<ISequencerSection> >& Sections = TrackNode->GetSections(); for ( int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex ) { UMovieSceneSection* Section = Sections[SectionIndex]->GetSectionObject(); if (Section->GetStartTime() <= KeyRight && Section->GetEndTime() >= KeyLeft) { KeyAreas.Add(Node->GetKeyGrouping(SectionIndex)); } } } // Search for any key that matches the position // todo: investigate if this would be faster as a sort + binary search, rather than linear search for (auto& KeyArea : KeyAreas) { for (FKeyHandle Key : KeyArea->GetUnsortedKeyHandles()) { const float Time = KeyArea->GetKeyTime(Key); if (Time >= KeyLeft && Time <= KeyRight) { return FSequencerSelectedKey(*KeyArea->GetOwningSection(), KeyArea, Key); } } } return FSequencerSelectedKey(); }
TSharedRef<ISequencerSection> FBytePropertyTrackEditor::MakeSectionInterface( UMovieSceneSection& SectionObject, UMovieSceneTrack* Track ) { return MakeShareable( new FBytePropertySection( SectionObject, Track->GetTrackName(), Cast<UMovieSceneByteTrack>( SectionObject.GetOuter() )->GetEnum() ) ); }