Skip to content

Commit 8ebf56f

Browse files
author
David
committed
Updated the sequencer functionality to work for 4.22 and 4.25 to solve the infinite recursion crash.
Added an add camera function essentially copied from the Sequencer.cpp CreateCamera function. Copied the sequencer example scripts to 2 new examples fo version 4.22 and 4.25 respectively. Possible fix of issues in the example scripts in camera section assignment between 4.22 and 4.25.
1 parent 2597686 commit 8ebf56f

6 files changed

Lines changed: 440 additions & 7 deletions

File tree

‎Source/UnrealEnginePython/Private/UEPyModule.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,10 @@ static PyMethodDef ue_PyUObject_methods[] = {
11261126
{ "sequencer_set_display_name", (PyCFunction)py_ue_sequencer_set_display_name, METH_VARARGS, "" },
11271127
{ "sequencer_get_display_name", (PyCFunction)py_ue_sequencer_get_display_name, METH_VARARGS, "" },
11281128
{ "sequencer_changed", (PyCFunction)py_ue_sequencer_changed, METH_VARARGS, "" },
1129+
{ "sequencer_add_camera", (PyCFunction)py_ue_sequencer_add_camera, METH_VARARGS, "" },
1130+
#if ENGINE_MINOR_VERSION >= 25
1131+
{ "sequencer_set_section_camera_guid", (PyCFunction)py_ue_sequencer_set_section_camera_guid, METH_VARARGS, "" },
1132+
#endif
11291133
{ "sequencer_add_camera_cut_track", (PyCFunction)py_ue_sequencer_add_camera_cut_track, METH_VARARGS, "" },
11301134
{ "sequencer_add_actor", (PyCFunction)py_ue_sequencer_add_actor, METH_VARARGS, "" },
11311135
{ "sequencer_add_actor_component", (PyCFunction)py_ue_sequencer_add_actor_component, METH_VARARGS, "" },

‎Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp‎

Lines changed: 249 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@
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+
457646
PyObject *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+
628872
PyObject *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);

‎Source/UnrealEnginePython/Private/UObject/UEPySequencer.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ PyObject *py_ue_sequencer_get_display_name(ue_PyUObject *, PyObject *);
2222
PyObject *py_ue_sequencer_changed(ue_PyUObject *, PyObject *);
2323
PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *, PyObject *);
2424

25+
#if ENGINE_MINOR_VERSION >= 25
26+
PyObject *py_ue_sequencer_set_section_camera_guid(ue_PyUObject *, PyObject *);
27+
#endif
28+
2529
PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *, PyObject *);
2630

2731
PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *, PyObject *);
2832

2933
PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *, PyObject *);
34+
PyObject *py_ue_sequencer_add_camera(ue_PyUObject *, PyObject *);
3035
PyObject *py_ue_sequencer_add_actor(ue_PyUObject *, PyObject *);
3136
PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *, PyObject *);
3237
PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *, PyObject *);
@@ -48,6 +53,5 @@ PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *, PyObject *);
4853
PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *, PyObject *);
4954

5055

51-
5256
PyObject *py_ue_sequencer_add_track(ue_PyUObject *, PyObject *);
5357

0 commit comments

Comments
 (0)