1111#if ENGINE_MINOR_VERSION >= 24
1212#include " Subsystems/AssetEditorSubsystem.h"
1313#endif
14+ #if ENGINE_MINOR_VERSION >= 25
15+ #include " Editor/MovieSceneTools/Public/MovieSceneToolHelpers.h"
16+ #include " Runtime/MovieSceneTracks/Public/Sections/MovieSceneCameraCutSection.h"
17+ #endif
1418#include " Editor/Sequencer/Public/ISequencer.h"
1519#include " Editor/Sequencer/Public/ISequencerModule.h"
1620#include " Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h"
21+ #include " Editor/UnrealEd/Public/LevelEditorViewport.h"
22+ #include " Editor/Sequencer/Private/Sequencer.h"
23+ #include " Camera/CameraActor.h"
24+ #include " CinematicCamera/Public/CineCameraActor.h"
1725#if ENGINE_MINOR_VERSION >= 20
1826#include " LevelSequenceEditor/Private/LevelSequenceEditorToolkit.h"
1927#else
@@ -169,7 +177,7 @@ static bool ImportFBXTransform(FString NodeName, UMovieScene3DTransformSection*
169177 FInterpCurveFloat EulerRotation[3 ];
170178 FInterpCurveFloat Scale[3 ];
171179#endif
172- #if ENGINE_MINOR_VERSION > 21
180+ #if ENGINE_MINOR_VERSION > 22
173181 CurveAPI.GetConvertedTransformCurveData (NodeName, Translation[0 ], Translation[1 ], Translation[2 ], EulerRotation[0 ], EulerRotation[1 ], EulerRotation[2 ], Scale[0 ], Scale[1 ], Scale[2 ], DefaultTransform, false );
174182#else
175183 CurveAPI.GetConvertedTransformCurveData (NodeName, Translation[0 ], Translation[1 ], Translation[2 ], EulerRotation[0 ], EulerRotation[1 ], EulerRotation[2 ], Scale[0 ], Scale[1 ], Scale[2 ], DefaultTransform);
@@ -454,34 +462,231 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args)
454462 return PyUnicode_FromString (TCHAR_TO_UTF8 (*new_guid.ToString ()));
455463}
456464
465+ PyObject *py_ue_sequencer_add_camera (ue_PyUObject *self, PyObject * args)
466+ {
467+ // because of what is likely a bug in Unreal Engine we cannot add cameras via AddActors from 4.22
468+ // only way I can see is to replicate CreateCamera - because cant figure out how to determine
469+ // which camera was created
470+ // NOTA BENE - this code MUST be checked at every Unreal Engine version update to ensure
471+ // it remains consistent with the current CreateCamera
472+ // so far I can see no difference between the CreateCamera function in versions 4.22 and 4.25
473+ // from 4.21 logs we have determined that the python sequence of spawn camera followed by add actor
474+ // seems to be equivalent to adding a camera manually via the GUI - which calls CreateCamera
475+
476+ // current differences to 4.21
477+ // this sets the camera position as the current viewpoint - add_actor just leaves camera object in its incoming position
478+ // using add_actor seems to add an additional camera item in the sequencer window - I think this is just a template camera
479+ // using this function we add just one camera per call (see the Destroy old actor line)
480+ // these are spawned cameras in 4.22
481+
482+ ue_py_check (self);
483+
484+ if (!self->ue_object ->IsA <ULevelSequence>())
485+ return PyErr_Format (PyExc_Exception, " uobject is not a LevelSequence" );
486+
487+ ULevelSequence *seq = (ULevelSequence *)self->ue_object ;
488+
489+ #if ENGINE_MINOR_VERSION >= 24
490+ // UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
491+ GEditor->GetEditorSubsystem <UAssetEditorSubsystem>()->OpenEditorForAsset (seq);
492+
493+ IAssetEditorInstance *editor = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>()->FindEditorForAsset (seq, true );
494+ #else
495+ FAssetEditorManager::Get ().OpenEditorForAsset (seq);
496+
497+ IAssetEditorInstance *editor = FAssetEditorManager::Get ().FindEditorForAsset (seq, true );
498+ #endif
499+
500+ FGuid CameraGuid;
501+ ACineCameraActor* NewCamera = nullptr ;
502+
503+ if (editor)
504+ {
505+ FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor;
506+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_camera: opening editor for asset 1" ));
507+ ISequencer *sequencer = toolkit->GetSequencer ().Get ();
508+
509+ // dangerous type cast here but Im guessing ISequencer is an interface of an FSequencer
510+ // well pigs FSequencer is private - although it does seem UEP uses a number of Private include files
511+ // as of 4.25 cant use most FSequencer symbols as they are not available at link time
512+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_camera: fixup 1" ));
513+ FSequencer* fsequencer = (FSequencer *)sequencer;
514+
515+ // so all I can do is re-implement CreateCamera here
516+
517+ UWorld* World = GCurrentLevelEditingViewportClient ? GCurrentLevelEditingViewportClient->GetWorld () : nullptr ;
518+ if (!World)
519+ {
520+ return PyErr_Format (PyExc_Exception, " cannot get World" );
521+ }
522+
523+ // what to do about this
524+ // I think this is for undo actions - as we are doing this in python dont think we need this
525+ // const FScopedTransaction Transaction(NSLOCTEXT("Sequencer", "CreateCameraHere", "Create Camera Here"));
526+
527+ const bool bCreateAsSpawnable = sequencer->GetSequencerSettings ()->GetCreateSpawnableCameras ();
528+
529+ FActorSpawnParameters SpawnParams;
530+ if (bCreateAsSpawnable)
531+ {
532+ // Don't bother transacting this object if we're creating a spawnable since it's temporary
533+ SpawnParams.ObjectFlags &= ~RF_Transactional;
534+ }
535+
536+ // Set new camera to match viewport
537+ NewCamera = World->SpawnActor <ACineCameraActor>(SpawnParams);
538+ if (!NewCamera)
539+ {
540+ return PyErr_Format (PyExc_Exception, " failed to spawn camera actor" );
541+ }
542+
543+ // this is pointless - how can Spawnable ever be not NULL in 2nd line!!
544+ FMovieSceneSpawnable* Spawnable = nullptr ;
545+ ESpawnOwnership SavedOwnership = Spawnable ? Spawnable->GetSpawnOwnership () : ESpawnOwnership::InnerSequence;
546+
547+ if (bCreateAsSpawnable)
548+ {
549+ CameraGuid = sequencer->MakeNewSpawnable (*NewCamera);
550+ Spawnable = sequencer->GetFocusedMovieSceneSequence ()->GetMovieScene ()->FindSpawnable (CameraGuid);
551+
552+ if (ensure (Spawnable))
553+ {
554+ // Override spawn ownership during this process to ensure it never gets destroyed
555+ SavedOwnership = Spawnable->GetSpawnOwnership ();
556+ Spawnable->SetSpawnOwnership (ESpawnOwnership::External);
557+ }
558+
559+ // Destroy the old actor
560+ World->EditorDestroyActor (NewCamera, false );
561+
562+ // note that this directly accesses ActiveTemplateIDs.Top() in CreateCamera but GetFocusedTemplateID just calls ActiveTemplateIDs.Top()
563+ for (TWeakObjectPtr<UObject>& Object : fsequencer->FindBoundObjects (CameraGuid, sequencer->GetFocusedTemplateID ()))
564+ {
565+ NewCamera = Cast<ACineCameraActor>(Object.Get ());
566+ if (NewCamera)
567+ {
568+ break ;
569+ }
570+ }
571+ ensure (NewCamera);
572+
573+ }
574+ else
575+ {
576+ CameraGuid = sequencer->CreateBinding (*NewCamera, NewCamera->GetActorLabel ());
577+ }
578+
579+ if (!CameraGuid.IsValid ())
580+ {
581+ return PyErr_Format (PyExc_Exception, " failed to create valid guid" );
582+ }
583+
584+ NewCamera->SetActorLocation ( GCurrentLevelEditingViewportClient->GetViewLocation (), false );
585+ NewCamera->SetActorRotation ( GCurrentLevelEditingViewportClient->GetViewRotation () );
586+ // pNewCamera->CameraComponent->FieldOfView = ViewportClient->ViewFOV; //@todo set the focal length from this field of view
587+
588+ sequencer->OnActorAddedToSequencer ().Broadcast (NewCamera, CameraGuid);
589+
590+ #if ENGINE_MINOR_VERSION >= 25
591+ // annoyingly we dont have access to NewCameraAdded in 4.25
592+ // as before re-implement
593+ // (note that AddActors also calls NewCameraAdded if the Actor is a camera)
594+ // also note that NewCameraAdded at 4.22 does not have the MovieSceneToolHelpers::CameraAdded call because it doesnt exist
595+ // - it does some extra actions which Im assuming somewhere between 4.23 and 4.25 was moved to MovieSceneToolHelpers::CameraAdded
596+
597+ {
598+
599+ sequencer->SetPerspectiveViewportCameraCutEnabled (false );
600+
601+ // Lock the viewport to this camera
602+ if (NewCamera && NewCamera->GetLevel ())
603+ {
604+ GCurrentLevelEditingViewportClient->SetMatineeActorLock (nullptr );
605+ GCurrentLevelEditingViewportClient->SetActorLock (NewCamera);
606+ GCurrentLevelEditingViewportClient->bLockedCameraView = true ;
607+ GCurrentLevelEditingViewportClient->UpdateViewForLockedActor ();
608+ GCurrentLevelEditingViewportClient->Invalidate ();
609+ }
610+
611+ UMovieSceneSequence* Sequence = sequencer->GetFocusedMovieSceneSequence ();
612+ UMovieScene* OwnerMovieScene = Sequence->GetMovieScene ();
613+
614+ MovieSceneToolHelpers::CameraAdded (OwnerMovieScene, CameraGuid, sequencer->GetLocalTime ().Time .FloorToFrame ());
615+
616+ }
617+ #else
618+ fsequencer->NewCameraAdded (CameraGuid, NewCamera);
619+ #endif
620+
621+ if (bCreateAsSpawnable && ensure (Spawnable))
622+ {
623+ Spawnable->SetSpawnOwnership (SavedOwnership);
624+ }
625+
626+ sequencer->NotifyMovieSceneDataChanged (EMovieSceneDataChangeType::MovieSceneStructureItemAdded);
627+
628+ }
629+ else
630+ {
631+ return PyErr_Format (PyExc_Exception, " unable to access sequencer" );
632+ }
633+
634+ ue_PyUObject *py_camera = ue_get_python_uobject_inc (NewCamera);
635+ if (!py_camera)
636+ return PyErr_Format (PyExc_Exception, " uobject is in invalid state" );
637+
638+ // we need a tuple return as we want both the camera uobject and the guid
639+ PyObject *ret = PyTuple_New (2 );
640+ PyTuple_SetItem (ret, 0 , (PyObject*)py_camera);
641+ PyTuple_SetItem (ret, 1 , PyUnicode_FromString (TCHAR_TO_UTF8 (*CameraGuid.ToString ())));
642+
643+ return ret;
644+ }
645+
457646PyObject *py_ue_sequencer_add_actor (ue_PyUObject *self, PyObject * args)
458647{
459648
460649 ue_py_check (self);
461650
651+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: starting" ));
652+
462653 PyObject *py_obj;
463654 if (!PyArg_ParseTuple (args, " O:sequencer_add_actor" , &py_obj))
464655 {
465656 return NULL ;
466657 }
467658
659+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: 1" ));
660+
468661 if (!self->ue_object ->IsA <ULevelSequence>())
469662 return PyErr_Format (PyExc_Exception, " uobject is not a LevelSequence" );
470663
664+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: 2" ));
665+
471666 ue_PyUObject *py_ue_obj = ue_is_pyuobject (py_obj);
472667 if (!py_ue_obj)
473668 return PyErr_Format (PyExc_Exception, " argument is not a uobject" );
474669
475- if (!py_ue_obj->ue_object ->IsA <AActor>())
476- return PyErr_Format (PyExc_Exception, " argument is not an actor" );
670+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: 3" ));
671+
672+ #if ENGINE_MINOR_VERSION > 21
673+ if (py_ue_obj->ue_object ->IsA <ACameraActor>())
674+ return PyErr_Format (PyExc_Exception, " argument is a camera actor" );
675+ #endif
676+
677+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: 4" ));
477678
478679 ULevelSequence *seq = (ULevelSequence *)self->ue_object ;
479680
681+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: 5" ));
682+
480683 TArray<TWeakObjectPtr<AActor>> actors;
481684 actors.Add ((AActor *)py_ue_obj->ue_object );
482685
483686 // try to open the editor for the asset
484687
688+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: opening editor for asset" ));
689+
485690#if ENGINE_MINOR_VERSION >= 24
486691 // UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
487692 GEditor->GetEditorSubsystem <UAssetEditorSubsystem>()->OpenEditorForAsset (seq);
@@ -495,8 +700,11 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args)
495700 if (editor)
496701 {
497702 FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor;
703+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: opening editor for asset 1" ));
498704 ISequencer *sequencer = toolkit->GetSequencer ().Get ();
705+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: opening editor for asset 2" ));
499706 sequencer->AddActors (actors);
707+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: opening editor for asset 3" ));
500708 }
501709 else
502710 {
@@ -505,11 +713,14 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args)
505713
506714 UObject& u_obj = *actors[0 ];
507715
716+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: opening editor for asset 4" ));
717+
508718#if ENGINE_MINOR_VERSION < 15
509719 FGuid new_guid = seq->FindPossessableObjectId (u_obj);
510720#else
511721 FGuid new_guid = seq->FindPossessableObjectId (u_obj, u_obj.GetWorld ());
512722#endif
723+ EXTRA_UE_LOG (LogPython, Warning, TEXT (" py_ue_sequencer_add_actor: opening editor for asset 5" ));
513724 if (!new_guid.IsValid ())
514725 {
515726 return PyErr_Format (PyExc_Exception, " unable to find guid" );
@@ -625,6 +836,39 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args
625836}
626837#endif
627838
839+ #if WITH_EDITOR
840+
841+ #if ENGINE_MINOR_VERSION >= 25
842+ PyObject *py_ue_sequencer_set_section_camera_guid (ue_PyUObject *self, PyObject * args)
843+ {
844+
845+ ue_py_check (self);
846+
847+ UMovieSceneCameraCutSection *section = ue_py_check_type<UMovieSceneCameraCutSection>(self);
848+ if (!section)
849+ return PyErr_Format (PyExc_Exception, " uobject is not a MovieSceneCameraCutSection" );
850+
851+ char *guid;
852+ if (!PyArg_ParseTuple (args, " s:sequencer_set_section_camera_guid" , &guid))
853+ {
854+ return NULL ;
855+ }
856+
857+ FGuid f_guid;
858+ if (!FGuid::Parse (FString (guid), f_guid))
859+ {
860+ return PyErr_Format (PyExc_Exception, " invalid guid" );
861+ }
862+
863+ section->SetCameraGuid (f_guid);
864+
865+ Py_RETURN_NONE;
866+ }
867+
868+ #endif
869+
870+ #endif
871+
628872PyObject *py_ue_sequencer_master_tracks (ue_PyUObject *self, PyObject * args)
629873{
630874
@@ -1689,7 +1933,7 @@ PyObject *py_ue_sequencer_import_fbx_transform(ue_PyUObject *self, PyObject * ar
16891933 continue ;
16901934
16911935 // Look for transforms explicitly
1692- #if ENGINE_MINOR_VERSION > 21
1936+ #if ENGINE_MINOR_VERSION > 22
16931937 FRichCurve Translation[3 ];
16941938 FRichCurve EulerRotation[3 ];
16951939 FRichCurve Scale[3 ];
@@ -1700,7 +1944,7 @@ PyObject *py_ue_sequencer_import_fbx_transform(ue_PyUObject *self, PyObject * ar
17001944#endif
17011945 FTransform DefaultTransform;
17021946#if ENGINE_MINOR_VERSION >= 18
1703- #if ENGINE_MINOR_VERSION >= 21
1947+ #if ENGINE_MINOR_VERSION > 22
17041948 CurveAPI.GetConvertedTransformCurveData (NodeName, Translation[0 ], Translation[1 ], Translation[2 ], EulerRotation[0 ], EulerRotation[1 ], EulerRotation[2 ], Scale[0 ], Scale[1 ], Scale[2 ], DefaultTransform, false );
17051949#else
17061950 CurveAPI.GetConvertedTransformCurveData (NodeName, Translation[0 ], Translation[1 ], Translation[2 ], EulerRotation[0 ], EulerRotation[1 ], EulerRotation[2 ], Scale[0 ], Scale[1 ], Scale[2 ], DefaultTransform);
0 commit comments