// Copyright Epic Games, Inc. All Rights Reserved. #include "Tools/SequencerEntityVisitor.h" #include "Containers/Array.h" #include "Containers/ArrayView.h" #include "IKeyArea.h" #include "MVVM/Extensions/IGeometryExtension.h" #include "MVVM/Extensions/IOutlinerExtension.h" #include "MVVM/Extensions/ITrackAreaExtension.h" #include "MVVM/Extensions/ITrackLaneExtension.h" #include "MVVM/ViewModels/CategoryModel.h" #include "MVVM/ViewModels/ChannelModel.h" #include "MVVM/ViewModels/FolderModel.h" #include "MVVM/ViewModels/ObjectBindingModel.h" #include "MVVM/ViewModels/TrackModel.h" #include "MVVM/ViewModels/ViewModel.h" #include "MVVM/ViewModels/ViewModelHierarchy.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "Misc/FrameTime.h" #include "MovieSceneSection.h" #include "SequencerCoreFwd.h" FSequencerEntityRange::FSequencerEntityRange(const TRange& InRange, FFrameRate InTickResolution) : TickResolution(InTickResolution) , Range(InRange) { } FSequencerEntityRange::FSequencerEntityRange(FVector2D TopLeft, FVector2D BottomRight, FFrameRate InTickResolution) : TickResolution(InTickResolution) , Range(TopLeft.X, BottomRight.X) , VerticalTop(TopLeft.Y), VerticalBottom(BottomRight.Y) { } bool FSequencerEntityRange::IntersectSection(const UMovieSceneSection* InSection) const { // Test horizontal bounds return (InSection->GetRange() / TickResolution).Overlaps(Range); } bool FSequencerEntityRange::IntersectKeyArea(TSharedPtr InNode, float VirtualKeyHeight) const { using namespace UE::Sequencer; IGeometryExtension* GeometryExtension = InNode->CastThis(); if (VerticalTop.IsSet() && GeometryExtension) { const FVirtualGeometry VirtualGeometry = GeometryExtension->GetVirtualGeometry(); const float NodeCenter = VirtualGeometry.GetTop() + VirtualGeometry.GetHeight()/2; return NodeCenter + VirtualKeyHeight/2 > VerticalTop.GetValue() && NodeCenter - VirtualKeyHeight/2 < VerticalBottom.GetValue(); } return true; } bool FSequencerEntityRange::IntersectVertical(float Top, float Bottom) const { if (VerticalTop.IsSet()) { return Top <= VerticalBottom.GetValue() && Bottom >= VerticalTop.GetValue(); } return true; } FSequencerEntityWalker::FSequencerEntityWalker(const FSequencerEntityRange& InRange, FVector2D InVirtualKeySize) : Range(InRange), VirtualKeySize(InVirtualKeySize) {} void FSequencerEntityWalker::Traverse(const ISequencerEntityVisitor& Visitor, TSharedPtr Item) { using namespace UE::Sequencer; IOutlinerExtension* OutlinerItem = Item->CastThis(); if (!OutlinerItem || !OutlinerItem->IsFilteredOut()) { ConditionallyIntersectModel(Visitor, Item); } } void FSequencerEntityWalker::ConditionallyIntersectModel(const ISequencerEntityVisitor& Visitor, const TSharedPtr& DataModel) { using namespace UE::Sequencer; const ITrackAreaExtension* TrackArea = DataModel->CastThis(); const IGeometryExtension* GeometryExtension = DataModel->CastThis(); if (GeometryExtension && TrackArea) { const FTrackAreaParameters TrackAreaParameters = TrackArea->GetTrackAreaParameters(); const FVirtualGeometry VirtualGeometry = GeometryExtension->GetVirtualGeometry(); const float Height = TrackAreaParameters.LaneType == ETrackAreaLaneType::Nested ? VirtualGeometry.NestedBottom - VirtualGeometry.Top : VirtualGeometry.Height; if (Range.IntersectVertical(VirtualGeometry.Top, VirtualGeometry.Top + Height)) { for (const TViewModelPtr& TrackLane : DataModel->GetChildrenOfType(EViewModelListType::TrackArea)) { FTrackLaneVirtualAlignment Alignment = TrackLane->ArrangeVirtualTrackLaneView(); if (!Alignment.IsVisible()) { continue; } FTrackLaneVerticalArrangement VerticalArrange = Alignment.VerticalAlignment.ArrangeWithin(Height); if (Range.IntersectVertical( VirtualGeometry.Top + VerticalArrange.Offset, VirtualGeometry.Top + VerticalArrange.Offset + VerticalArrange.Height) ) { if (Range.Range.Overlaps(Alignment.Range / Range.TickResolution)) { Visitor.VisitDataModel(TrackLane.AsModel().Get()); if (TViewModelPtr ChannelModel = TrackLane.ImplicitCast()) { VisitChannel(Visitor, ChannelModel); } } } } if (Range.IntersectKeyArea(DataModel, VirtualKeySize.Y)) { const IOutlinerExtension* OutlinerItem = DataModel->CastThis(); const bool bIsExpanded = !OutlinerItem || OutlinerItem->IsExpanded(); VisitAnyChannels(Visitor, DataModel.ToSharedRef(), !bIsExpanded); } } } // Iterate into expanded nodes const IOutlinerExtension* OutlinerExtension = DataModel->CastThis(); if (OutlinerExtension == nullptr || OutlinerExtension->IsExpanded()) { for (TSharedPtr Child : DataModel->GetChildren(EViewModelListType::Outliner)) { // Do not visit nodes that are currently filtered out const IOutlinerExtension* OutlinerItem = DataModel->CastThis(); if (!OutlinerItem || !OutlinerItem->IsFilteredOut()) { ConditionallyIntersectModel(Visitor, Child); } } } } void FSequencerEntityWalker::VisitAnyChannels(const ISequencerEntityVisitor& Visitor, const TSharedRef& InNode, bool bAnyParentCollapsed ) { using namespace UE::Sequencer; if (!Visitor.CheckEntityMask(ESequencerEntity::Key)) { return; } // If this node has or is a key area, visit all the keys on the track FChannelGroupModel* ChannelGroup = nullptr; if (FChannelGroupModel* ChannelGroupNode = InNode->CastThis()) { ChannelGroup = ChannelGroupNode; } if (FTrackModel* TrackModel = InNode->CastThis()) { // TODO: Top-level channel group -- there used to be only one at most but this assumption isn't necessarily true anymore if (auto It = TrackModel->GetTopLevelChannels().IterateSubList(); It) { const TViewModelPtr& TopLevelChannel = *It; ChannelGroup = TopLevelChannel.Get(); } } if (ChannelGroup) { const IOutlinerExtension* OutlinerItem = ChannelGroup->CastThis(); if (!OutlinerItem || OutlinerItem->IsFilteredOut() == false) { for (const TWeakViewModelPtr& WeakChannel : ChannelGroup->GetChannels()) { if (TViewModelPtr Channel = WeakChannel.Pin()) { VisitChannel(Visitor, Channel); } } } } // Otherwise it might be a collapsed node that contains key areas as children. If so we visit them as if they were a part of this track so that key groupings are visited properly. else if (bAnyParentCollapsed) { const IOutlinerExtension* OutlinerItem = InNode->CastThis(); const bool bIsExpanded = !OutlinerItem || OutlinerItem->IsExpanded(); if ((InNode->IsA() || InNode->IsA()) && !bIsExpanded) { return; } for (TSharedPtr ChildNode : InNode->GetChildren(EViewModelListType::Outliner)) { VisitAnyChannels(Visitor, ChildNode.ToSharedRef(), bAnyParentCollapsed || !bIsExpanded); } } } void FSequencerEntityWalker::VisitChannel(const ISequencerEntityVisitor& Visitor, const UE::Sequencer::TViewModelPtr& Channel) { using namespace UE::Sequencer; UMovieSceneSection* Section = Channel->GetSection(); if (Section) { const FFrameTime HalfKeySizeFrames = (VirtualKeySize.X*.5f) * Range.TickResolution; const FFrameTime RangeStartFrame = Range.Range.GetLowerBoundValue() * Range.TickResolution; const FFrameTime RangeEndFrame = Range.Range.GetUpperBoundValue() * Range.TickResolution; TRange VisitRangeFrames( (RangeStartFrame-HalfKeySizeFrames).CeilToFrame(), (RangeEndFrame+HalfKeySizeFrames).FloorToFrame() ); VisitRangeFrames = TRange::Intersection(Section->GetRange(), VisitRangeFrames); Visitor.VisitKeys(Channel, VisitRangeFrames); } }