def setup_capture(self):
        self.read_config(False)
        path = unreal.DirectoryPath(self.output_directory)
        self.capture_settings.set_editor_property("output_directory", path)
        self.capture_settings.set_editor_property("output_format",
                                                  self.FileName_LE.text())

        index = self.Proctocol_Combo.currentIndex()
        protocol = proctocol_dict.get(index)

        capture = unreal.AutomatedLevelSequenceCapture()
        section = "MovieSceneCaptureUIInstance AutomatedLevelSequenceCapture"
        for option, attr in self.json_config.get(section).items():
            if not self.config.has_option(section, option):
                continue
            v = self.config.get(section, option)
            if option == "Settings":
                capture.set_editor_property("settings", self.capture_settings)
                continue
            elif attr == "custom_end_frame":
                pattern = re.compile("(\d+)")
                num = pattern.search(v).group(0)
                v = unreal.FrameNumber(int(num))
            elif attr == "custom_start_frame":
                pattern = re.compile("(\d+)")
                num = pattern.search(v).group(0)
                v = unreal.FrameNumber(int(num))
            elif attr == "audio_capture_protocol_type":
                v = unreal.SoftClassPath(v)
            elif attr == "additional_command_line_arguments":
                pass
            elif attr == "image_capture_protocol_type":
                capture.set_image_capture_protocol_type(protocol)
                protocol_name = (
                    "MovieSceneCaptureUIInstance_ImageProtocol %s" %
                    protocol.__name__)
                print(protocol_name)
                protocol = capture.get_image_capture_protocol()
                for pro_name, pro_attr in self.json_config.get(
                        protocol_name, {}).items():
                    if not self.config.has_option(protocol_name, pro_name):
                        continue
                    pro_v = self.config.get(protocol_name, pro_name)
                    if pro_attr == "capture_gamut":
                        pro_v = HDRCaptureGamut[pro_v.upper()]
                    elif pro_attr == "post_processing_material":
                        if pro_v == "None":
                            continue
                        pro_v = unreal.SoftObjectPath(pro_v)
                    elif pro_attr == "include_render_passes":
                        # NOTE 获取勾选的 passes
                        name_list = [
                            cb.text()
                            for cb in self.Pass_Container.findChildren(
                                QtWidgets.QCheckBox) if cb.isChecked()
                        ]
                        print(name_list)
                        pro_v = unreal.CompositionGraphCapturePasses(name_list)
                    else:
                        pro_v = ast.literal_eval(pro_v)
                    protocol.set_editor_property(pro_attr, pro_v)
                continue
            else:
                v = ast.literal_eval(v)
            capture.set_editor_property(attr, v)
        return capture
def render(sequence_list,
           i,
           output_directory="C:/render",
           output_format="{sequence}"):

    # NOTE 如果超出数组则退出执行
    if i >= len(sequence_list):
        # NOTE 输出完成 打开输出文件夹的路径
        os.startfile(output_directory)
        return

    # NOTE 获取当前渲染序号下的 LevelSequence
    sequence = sequence_list[i]

    # NOTE 配置渲染参数
    settings = unreal.MovieSceneCaptureSettings()
    path = unreal.DirectoryPath(output_directory)
    settings.set_editor_property("output_directory", path)
    settings.set_editor_property("output_format", output_format)
    settings.set_editor_property("overwrite_existing", True)
    settings.set_editor_property("game_mode_override", None)
    settings.set_editor_property("use_relative_frame_numbers", False)
    settings.set_editor_property("handle_frames", 0)
    settings.set_editor_property("zero_pad_frame_numbers", 4)
    settings.set_editor_property("use_custom_frame_rate", True)
    settings.set_editor_property("custom_frame_rate", unreal.FrameRate(24, 1))

    # NOTE 渲染大小
    w, h = 1280, 720
    settings.set_editor_property("resolution", unreal.CaptureResolution(w, h))

    settings.set_editor_property("enable_texture_streaming", False)
    settings.set_editor_property("cinematic_engine_scalability", True)
    settings.set_editor_property("cinematic_mode", True)
    settings.set_editor_property("allow_movement", False)
    settings.set_editor_property("allow_turning", False)
    settings.set_editor_property("show_player", False)
    settings.set_editor_property("show_hud", False)

    # NOTE 设置默认的自动渲染参数
    option = unreal.AutomatedLevelSequenceCapture()
    option.set_editor_property("use_separate_process", False)
    option.set_editor_property("close_editor_when_capture_starts", False)
    option.set_editor_property("additional_command_line_arguments",
                               "-NOSCREENMESSAGES")
    option.set_editor_property("inherited_command_line_arguments", "")
    option.set_editor_property("use_custom_start_frame", False)
    option.set_editor_property("use_custom_end_frame", False)
    option.set_editor_property("warm_up_frame_count", 0.0)
    option.set_editor_property("delay_before_warm_up", 0)
    option.set_editor_property("delay_before_shot_warm_up", 0.0)
    option.set_editor_property("write_edit_decision_list", True)
    # option.set_editor_property("custom_start_frame",unreal.FrameNumber(0))
    # option.set_editor_property("custom_end_frame",unreal.FrameNumber(0))

    option.set_editor_property("settings", settings)
    option.set_editor_property("level_sequence_asset",
                               unreal.SoftObjectPath(sequence.get_path_name()))

    # NOTE 设置自定义渲染参数
    option.set_image_capture_protocol_type(
        unreal.CompositionGraphCaptureProtocol)
    protocol = option.get_image_capture_protocol()
    # NOTE 这里设置 Base Color 渲染 Base Color 通道,可以根据输出的 UI 设置数组名称
    passes = unreal.CompositionGraphCapturePasses(["Base Color"])
    protocol.set_editor_property("include_render_passes", passes)
    # protocol.set_editor_property("compression_quality",100)

    # NOTE 设置全局变量才起作用!
    global on_finished_callback
    on_finished_callback = unreal.OnRenderMovieStopped(lambda s: render(
        sequence_list, i + 1, output_directory, output_format))
    unreal.SequencerTools.render_movie(option, on_finished_callback)
Example #3
0
def render_sequence_to_movie(sequencer_asset_path,
                             level_name,
                             level_path,
                             output_file_name,
                             resolution=(1280, 720),
                             game_mode_override='None',
                             warm_up_frame_count=0,
                             delay_before_warm_up=0.0,
                             delay_before_shot_warm_up=0.0,
                             delay_every_frame=0.0,
                             use_burn_in=False,
                             encorder_format='422'):
    # 1) Create an instance of our UAutomatedLevelSequenceCapture and override all of the settings on it. This class is currently
    # set as a config class so settings will leak between the Unreal Sequencer Render-to-Movie UI and this object. To work around
    # this, we set every setting via the script so that no changes the user has made via the UI will affect the script version.
    # The users UI settings will be reset as an unfortunate side effect of this.
    capture_settings = unreal.AutomatedLevelSequenceCapture()

    # Set all POD settings on the UMovieSceneCapture
    output_dir = unreal.SystemLibrary.get_project_directory(
    ) + "Saved/VideoCaptures/"
    capture_settings.settings.output_directory = unreal.DirectoryPath(
        output_dir)
    if not os.path.exists(output_dir):
        os.mkdir(output_dir)

    #capture_settings.settings.movie_extension = '.avi'

    # If you game mode is implemented in Blueprint, load_asset(...) is going to return you the C++ type ('Blueprint') and not what the BP says it inherits from.
    # Instead, because game_mode_override is a TSubclassOf<AGameModeBase> we can use unreal.load_class to get the UClass which is implicitly convertable.
    # ie: capture_settings.settings.game_mode_override = unreal.load_class(None, "/Game/AI/TestingSupport/AITestingGameMode.AITestingGameMode_C")

    editor_world_settings = unreal.EditorLevelLibrary.get_editor_world(
    ).get_world_settings()
    ''' game_mode_override is not working
		so change game_mode in world setting temporatily '''
    print "# World Setting - Game Mode Input = " + game_mode_override
    print "# World Setting - Game Mode = " + str(
        editor_world_settings.default_game_mode) + " - type = " + str(
            type(editor_world_settings.default_game_mode))
    if game_mode_override == 'None':
        unreal.EditorLevelLibrary.get_editor_world().get_world_settings(
        ).set_editor_property('default_game_mode', None)
    elif game_mode_override == 'BP_CameraGameMode':
        unreal.EditorLevelLibrary.get_editor_world().get_world_settings(
        ).set_editor_property(
            'default_game_mode',
            unreal.load_class(
                None,
                '/VirtualCamera/Custom/BP_CameraGameMode.BP_CameraGameMode_C'))
    else:
        pass

    print "# World Setting - Game Mode Override = " + str(
        editor_world_settings.default_game_mode) + " - type = " + str(
            type(editor_world_settings.default_game_mode))

    capture_settings.settings.game_mode_override = None
    #if game_mode_override=='BP_CameraGameMode':
    #	capture_settings.settings.game_mode_override = None
    capture_settings.settings.output_format = output_file_name
    capture_settings.settings.overwrite_existing = False
    capture_settings.settings.use_relative_frame_numbers = False
    capture_settings.settings.handle_frames = 0
    capture_settings.settings.zero_pad_frame_numbers = 4
    # If you wish to override the output framerate you can use these two lines, otherwise the framerate will be derived from the sequence being rendered
    capture_settings.settings.use_custom_frame_rate = True
    capture_settings.settings.custom_frame_rate = unreal.FrameRate(24, 1)
    capture_settings.settings.resolution.res_x = resolution[0]
    capture_settings.settings.resolution.res_y = resolution[1]
    capture_settings.settings.enable_texture_streaming = False
    capture_settings.settings.cinematic_engine_scalability = True
    capture_settings.settings.cinematic_mode = True
    capture_settings.settings.allow_movement = False  # Requires cinematic_mode = True
    capture_settings.settings.allow_turning = False  # Requires cinematic_mode = True
    capture_settings.settings.show_player = False  # Requires cinematic_mode = True
    capture_settings.settings.show_hud = False  # Requires cinematic_mode = True
    capture_settings.use_separate_process = False
    capture_settings.close_editor_when_capture_starts = False  # Requires use_separate_process = True
    capture_settings.additional_command_line_arguments = "-NOSCREENMESSAGES"  # Requires use_separate_process = True
    capture_settings.inherited_command_line_arguments = ""  # Requires use_separate_process = True

    # Set all the POD settings on UAutomatedLevelSequenceCapture
    capture_settings.use_custom_start_frame = False  # If False, the system will automatically calculate the start based on sequence content
    capture_settings.use_custom_end_frame = False  # If False, the system will automatically calculate the end based on sequence content
    capture_settings.custom_start_frame = unreal.FrameNumber(
        0)  # Requires use_custom_start_frame = True
    capture_settings.custom_end_frame = unreal.FrameNumber(
        0)  # Requires use_custom_end_frame = True
    capture_settings.warm_up_frame_count = warm_up_frame_count
    capture_settings.delay_before_warm_up = delay_before_warm_up
    capture_settings.delay_before_shot_warm_up = delay_before_shot_warm_up
    capture_settings.delay_every_frame = delay_every_frame

    capture_settings.write_edit_decision_list = True

    # Tell the capture settings which level sequence to render with these settings. The asset does not need to be loaded,
    # as we're only capturing the path to it and when the PIE instance is created it will load the specified asset.
    # If you only had a reference to the level sequence, you could use "unreal.SoftObjectPath(mysequence.get_path_name())"
    capture_settings.level_sequence_asset = unreal.SoftObjectPath(
        sequencer_asset_path)

    # To configure the video output we need to tell the capture settings which capture protocol to use. The various supported
    # capture protocols can be found by setting the Unreal Content Browser to "Engine C++ Classes" and filtering for "Protocol"
    # ie: CompositionGraphCaptureProtocol, ImageSequenceProtocol_PNG, etc. Do note that some of the listed protocols are not intended
    # to be used directly.
    # Right click on a Protocol and use "Copy Reference" and then remove the extra formatting around it. ie:
    # Class'/Script/MovieSceneCapture.ImageSequenceProtocol_PNG' gets transformed into "/Script/MovieSceneCapture.ImageSequenceProtocol_PNG"

    #capture_settings.set_image_capture_protocol_type(unreal.load_class(None, "/Script/MovieSceneCapture.VideoCaptureProtocol"))
    capture_settings.set_image_capture_protocol_type(
        unreal.load_class(
            None, "/Script/AppleProResMedia.AppleProResEncoderProtocol"))
    # After we have set the capture protocol to a soft class path we can start editing the settings for the instance of the protocol that is internallyc reated.
    #capture_settings.get_image_capture_protocol().compression_quality = 100

    # The other complex settings is the burn-in. Create an instance of the LevelSequenceBurnInOptions which is used to
    # specify if we should use a burn in, and then which settings.
    burn_in_options = unreal.LevelSequenceBurnInOptions()
    #unreal.log('use_burn_in =' + str(use_burn_in))
    burn_in_options.use_burn_in = use_burn_in
    #unreal.log('burn_in_options.use_burn_in =' + str(burn_in_options.use_burn_in))

    # You have to specify a path to a class to use for the burn in (if use_burn_in = True), and this class specifies a UClass to define the
    # settings object type. We've created a convinence function which takes the class path, loads the class at that path and assigns it to
    # the Settings object.
    #burn_in_options.set_burn_in(unreal.SoftClassPath("/Engine/Sequencer/DefaultBurnIn.DefaultBurnIn_C"))
    burn_in_options.set_burn_in(
        unreal.SoftClassPath("/MofacRenderMovie/CustomBurnIn.CustomBurnIn_C"))

    # The default burn in is implemented entirely in Blueprint which means that the method we've been using to set properties will not
    # work for it. The python bindings that turn bSomeVariableName into "some_variable_name" only work for C++ classes with
    # UPROPERTY(BlueprintReadWrite) marked fields. Python doesn't know about the existence of Blueprint classes and their fields, so we
    # have to use an alternative method.
    burn_in_options.settings.set_editor_property(
        'TopLeftText', "{FocalLength}mm,{Aperture},{FocusDistance}")
    burn_in_options.settings.set_editor_property(
        'TopCenterText',
        "{MasterName} - {Date} - {EngineVersion} - " + proj_names)
    burn_in_options.settings.set_editor_property('TopRightText', artist_names)

    burn_in_options.settings.set_editor_property(
        'BottomLeftText',
        "Level: " + level_name + "\nPath: " + level_path + "\n{ShotName}")
    burn_in_options.settings.set_editor_property(
        'BottomCenterText', "{hh}:{mm}:{ss}:{ff} ({MasterFrame})")
    burn_in_options.settings.set_editor_property(
        'BottomRightText',
        "{shh}:{smm}:{sss}:{sff} {SourceTimecode} {ShotFrame}")

    # Load a Texture2D asset and assign it to the UTexture2D reference that Watermark is.
    # burn_in_settings.set_editor_property('Watermark', None)
    # Note that this example creates a really obvious watermark (a big blurry green smiley face) just so that you know it's working!
    #burn_in_options.settings.set_editor_property('Watermark', unreal.load_asset("/Engine/EngineResources/AICON-Green"))
    #burn_in_options.settings.set_editor_property('WatermarkTint', unreal.LinearColor(1.0, 0.5, 0.5, 0.5)) # Create a FLinearColor to tint our Watermark

    # Assign our created instances to our original capture_settings object.
    capture_settings.burn_in_options = burn_in_options

    # Finally invoke Sequencer's Render to Movie functionality. This will examine the specified settings object and either construct a new PIE instance to render in,
    # or create and launch a new process (optionally shutting down your editor).

    #unreal.log(capture_settings.get_image_capture_protocol().get_editor_property('EncodingFormat'))
    '''
	enum class EAppleProResEncoderFormats : uint8
	{
		F_422HQ UMETA(DisplayName = "422 HQ"),
		F_422 UMETA(DisplayName = "422"),
		F_422LT UMETA(DisplayName = "422 LT"),
		F_422Proxy UMETA(DisplayName = "422 Proxy"),
		F_4444 UMETA(DisplayName = "4444"),
		F_4444XQ UMETA(DisplayName = "4444 XQ"),
	};
	'''
    TargetEncoderFormat = unreal.AppleProResEncoderFormats.F_422
    #print type(TargetEncoderFormat)
    #print TargetEncoderFormat
    #print dir(type(TargetEncoderFormat))
    if encorder_format == '422 HQ':
        TargetEncoderFormat = unreal.AppleProResEncoderFormats.F_422HQ
    elif encorder_format == '422':
        pass
    elif encorder_format == '422 LT':
        TargetEncoderFormat = unreal.AppleProResEncoderFormats.F_422LT
    elif encorder_format == '422 Proxy':
        TargetEncoderFormat = unreal.AppleProResEncoderFormats.F_422_PROXY
    elif encorder_format == '4444':
        TargetEncoderFormat = unreal.AppleProResEncoderFormats.F_4444
    elif encorder_format == '4444 XQ':
        TargetEncoderFormat = unreal.AppleProResEncoderFormats.F_4444XQ

    capture_settings.get_image_capture_protocol().set_editor_property(
        'EncodingFormat', TargetEncoderFormat)

    if args.use_audio_capture:
        capture_settings.set_audio_capture_protocol_type(
            unreal.load_class(
                None,
                "/Script/MovieSceneCapture.MasterAudioSubmixCaptureProtocol"))
        capture_settings.get_audio_capture_protocol().set_editor_property(
            'FileName', output_file_name)
    #unreal.log(capture_settings.get_image_capture_protocol().get_editor_property('EncodingFormat'))
    #unreal.log(type(capture_settings.get_image_capture_protocol().get_editor_property('EncodingFormat')))

    unreal.SequencerTools.render_movie(capture_settings, on_finished_callback)
Example #4
0
def render_sequence_to_movie(sequencer_asset_path):
    # 1) Create an instance of our UAutomatedLevelSequenceCapture and override all of the settings on it. This class is currently
    # set as a config class so settings will leak between the Unreal Sequencer Render-to-Movie UI and this object. To work around
    # this, we set every setting via the script so that no changes the user has made via the UI will affect the script version.
    # The users UI settings will be reset as an unfortunate side effect of this.
    capture_settings = unreal.AutomatedLevelSequenceCapture()

    # Set all POD settings on the UMovieSceneCapture
    capture_settings.settings.output_directory = unreal.DirectoryPath(
        "../../../QAGame/Saved/VideoCaptures/")

    # If you game mode is implemented in Blueprint, load_asset(...) is going to return you the C++ type ('Blueprint') and not what the BP says it inherits from.
    # Instead, because game_mode_override is a TSubclassOf<AGameModeBase> we can use unreal.load_class to get the UClass which is implicitly convertable.
    # ie: capture_settings.settings.game_mode_override = unreal.load_class(None, "/Game/AI/TestingSupport/AITestingGameMode.AITestingGameMode_C")
    capture_settings.settings.game_mode_override = None
    capture_settings.settings.output_format = "{world}"
    capture_settings.settings.overwrite_existing = True
    capture_settings.settings.use_relative_frame_numbers = False
    capture_settings.settings.handle_frames = 0
    capture_settings.settings.zero_pad_frame_numbers = 4
    capture_settings.settings.frame_rate = unreal.FrameRate(24, 1)
    capture_settings.settings.resolution.res_x = 1280
    capture_settings.settings.resolution.res_y = 720
    capture_settings.settings.enable_texture_streaming = False
    capture_settings.settings.cinematic_engine_scalability = True
    capture_settings.settings.cinematic_mode = True
    capture_settings.settings.allow_movement = False  # Requires cinematic_mode = True
    capture_settings.settings.allow_turning = False  # Requires cinematic_mode = True
    capture_settings.settings.show_player = False  # Requires cinematic_mode = True
    capture_settings.settings.show_hud = False  # Requires cinematic_mode = True
    capture_settings.use_separate_process = False
    capture_settings.close_editor_when_capture_starts = False  # Requires use_separate_process = True
    capture_settings.additional_command_line_arguments = "-NOSCREENMESSAGES"  # Requires use_separate_process = True
    capture_settings.inherited_command_line_arguments = ""  # Requires use_separate_process = True

    # Set all the POD settings on UAutomatedLevelSequenceCapture
    capture_settings.use_custom_start_frame = False  # If False, the system will automatically calculate the start based on sequence content
    capture_settings.use_custom_end_frame = False  # If False, the system will automatically calculate the end based on sequence content
    capture_settings.custom_start_frame = unreal.FrameNumber(
        0)  # Requires use_custom_start_frame = True
    capture_settings.custom_end_frame = unreal.FrameNumber(
        0)  # Requires use_custom_end_frame = True
    capture_settings.warm_up_frame_count = 0.0
    capture_settings.delay_before_warm_up = 0  # ToDo: Test int -> float
    capture_settings.delay_before_shot_warm_up = 0.0
    capture_settings.write_edit_decision_list = True

    # Tell the capture settings which level sequence to render with these settings. The asset does not need to be loaded,
    # as we're only capturing the path to it and when the PIE instance is created it will load the specified asset.
    # If you only had a reference to the level sequence, you could use "unreal.SoftObjectPath(mysequence.get_path_name())"
    capture_settings.level_sequence_asset = unreal.SoftObjectPath(
        sequencer_asset_path)

    # Now let's work on setting some more complex settings. The image capture is composed of two fields that need to correspond to each other.
    # The first field is the CaptureType ("Output Format" on the UI). This is how you specify if you want to output custom render passes, a video sequence, etc.
    # Then, once you decide on a capture type you also need to create an instance of the settings object specific to that capture type and assign it.
    # In this example, we will be using a Custom Render Passes output as it is the most complicated one to set up.
    #
    # The settings classes for each capture protocol can be found in the /Engine/Source/Runtime/MovieSceneCapture/Public/Protocols folder.
    # The identifier comes from MovieSceneCaptureModule.cpp ("CustomRenderPasses", "Video", "PNG", "JPG", "BMP")
    capture_settings.capture_type.identifier = "CustomRenderPasses"
    protocol_settings = unreal.CompositionGraphCaptureSettings()
    # The passes comes from BufferVisualizationData.cpp
    protocol_settings.include_render_passes.value.append("BaseColor")
    protocol_settings.include_render_passes.value.append("SceneDepth")
    protocol_settings.include_render_passes.value.append("Roughness")
    protocol_settings.capture_frames_in_hdr = False
    protocol_settings.hdr_compression_quality = 1  # Requires capture_frames_in_hdr = True, 0 means no compression, 1 means standard.
    protocol_settings.capture_gamut = unreal.HDRCaptureGamut.HCGM_REC709  # Requires capture_frames_in_hdr = True
    # protocol_settings.post_processing_material = unreal.SoftObjectPath("/Game/Path/To/Material")
    protocol_settings.post_processing_material = unreal.SoftObjectPath(
        "")  # Soft Object Paths use an empty string for None.
    protocol_settings.disable_screen_percentage = True

    # The other complex settings is the burn-in. Create an instance of the LevelSequenceBurnInOptions which is used to
    # specify if we should use a burn in, and then which settings.
    burn_in_options = unreal.LevelSequenceBurnInOptions()
    burn_in_options.use_burn_in = True

    # You have to specify a path to a class to use for the burn in (if use_burn_in = True), and this class specifies a UClass to define the
    # settings object type. We've created a convinence function which takes the class path, loads the class at that path and assigns it to
    # the Settings object.
    burn_in_options.set_burn_in(
        unreal.SoftClassPath(
            "/Engine/Sequencer/DefaultBurnIn.DefaultBurnIn_C"))

    # The default burn in is implemented entirely in Blueprint which means that the method we've been using to set properties will not
    # work for it. The python bindings that turn bSomeVariableName into "some_variable_name" only work for C++ classes with
    # UPROPERTY(BlueprintReadWrite) marked fields. Python doesn't know about the existence of Blueprint classes and their fields, so we
    # have to use an alternative method.
    burn_in_options.settings.set_editor_property(
        'TopLeftText', "{FocalLength}mm,{Aperture},{FocusDistance}")
    burn_in_options.settings.set_editor_property(
        'TopCenterText', "{MasterName} - {Date} - {EngineVersion}")
    burn_in_options.settings.set_editor_property(
        'TopRightText',
        "{TranslationX} {TranslationY} {TranslationZ}, {RotationX} {RotationY} {RotationZ}"
    )

    burn_in_options.settings.set_editor_property('BottomLeftText',
                                                 "{ShotName}")
    burn_in_options.settings.set_editor_property(
        'BottomCenterText', "{hh}:{mm}:{ss}:{ff} ({MasterFrame})")
    burn_in_options.settings.set_editor_property('BottomRightText',
                                                 "{ShotFrame}")

    # Load a Texture2D asset and assign it to the UTexture2D reference that Watermark is.
    # burn_in_settings.set_editor_property('Watermark', None)
    burn_in_options.settings.set_editor_property(
        'Watermark', unreal.load_asset("/Engine/EngineResources/AICON-Green"))
    burn_in_options.settings.set_editor_property(
        'WatermarkTint',
        unreal.LinearColor(1.0, 0.5, 0.5,
                           0.5))  # Create a FLinearColor to tint our Watermark

    # Assign our created instances to our original capture_settings object.
    capture_settings.burn_in_options = burn_in_options
    capture_settings.protocol_settings = protocol_settings

    # Finally invoke Sequencer's Render to Movie functionality. This will examine the specified settings object and either construct a new PIE instance to render in,
    # or create and launch a new process (optionally shutting down your editor).
    unreal.SequencerTools.render_movie(capture_settings)
Example #5
0
    def render_sequence_to_movie(sequence_path, sequence_name):
        def on_render_movie_finished(success):
            print(
                "Movie has finished rendering. Python can now invoke another movie render if needed. Sucess: "
                + str(success))

        on_finished_callback = unreal.OnRenderMovieStopped()
        on_finished_callback.bind_callable(on_render_movie_finished)

        file_path = sequence_path + "/" + sequence_name

        capture_settings = unreal.AutomatedLevelSequenceCapture()

        # out put path
        capture_settings.settings.output_directory = unreal.DirectoryPath(
            "../../../Game/Saved/VideoCaptures/")

        capture_settings.settings.game_mode_override = None

        # out put name
        capture_settings.settings.output_format = sequence_name

        capture_settings.settings.overwrite_existing = False
        capture_settings.settings.use_relative_frame_numbers = False
        capture_settings.settings.handle_frames = 0
        capture_settings.settings.zero_pad_frame_numbers = 4

        capture_settings.settings.use_custom_frame_rate = True

        capture_settings.settings.custom_frame_rate = unreal.FrameRate(24, 1)
        capture_settings.settings.resolution.res_x = 1280
        capture_settings.settings.resolution.res_y = 720

        capture_settings.settings.enable_texture_streaming = False
        capture_settings.settings.cinematic_engine_scalability = True
        capture_settings.settings.cinematic_mode = True
        capture_settings.settings.allow_movement = False
        capture_settings.settings.allow_turning = False
        capture_settings.settings.show_player = False
        capture_settings.settings.show_hud = False
        capture_settings.use_separate_process = False
        capture_settings.close_editor_when_capture_starts = False
        capture_settings.additional_command_line_arguments = "-NOSCREENMESSAGES"
        capture_settings.inherited_command_line_arguments = ""

        capture_settings.use_custom_start_frame = False
        capture_settings.use_custom_end_frame = False
        capture_settings.custom_start_frame = unreal.FrameNumber(0)
        capture_settings.custom_end_frame = unreal.FrameNumber(0)
        capture_settings.warm_up_frame_count = 0.0
        capture_settings.delay_before_warm_up = 0
        capture_settings.delay_before_shot_warm_up = 0.0
        capture_settings.write_edit_decision_list = True

        # Change format
        capture_settings.set_image_capture_protocol_type(
            unreal.load_class(
                None, "/Script/MovieSceneCapture.ImageSequenceProtocol_JPG"))
        capture_settings.get_image_capture_protocol().compression_quality = 100

        capture_settings.level_sequence_asset = unreal.SoftObjectPath(
            file_path)
        unreal.SequencerTools.render_movie(capture_settings,
                                           on_finished_callback)
Example #6
0
    def _unreal_render_sequence_with_movie_queue(self,
                                                 output_path,
                                                 unreal_map_path,
                                                 sequence_path,
                                                 presets=None,
                                                 shot_name=None):
        """
        Renders a given sequence in a given level with the Movie Render queue.

        :param str output_path: Full path to the movie to render.
        :param str unreal_map_path: Path of the Unreal map in which to run the sequence.
        :param str sequence_path: Content Browser path of sequence to render.
        :param presets: Optional :class:`unreal.MoviePipelineMasterConfig` instance to use for renderig.
        :param str shot_name: Optional shot name to render a single shot from this sequence.
        :returns: True if a movie file was generated, False otherwise
                  string representing the path of the generated movie file
        :raises ValueError: If a shot name is specified but can't be found in
                            the sequence.
        """
        output_folder, output_file = os.path.split(output_path)
        movie_name = os.path.splitext(output_file)[0]

        qsub = unreal.MoviePipelineQueueEngineSubsystem()
        queue = qsub.get_queue()
        job = queue.allocate_new_job(unreal.MoviePipelineExecutorJob)
        job.sequence = unreal.SoftObjectPath(sequence_path)
        job.map = unreal.SoftObjectPath(unreal_map_path)
        # If a specific shot was given, disable all the others.
        if shot_name:
            shot_found = False
            for shot in job.shot_info:
                if shot.outer_name != shot_name:
                    self.logger.info("Disabling shot %s" % shot.outer_name)
                    shot.enabled = False
                else:
                    shot_found = True
            if not shot_found:
                raise ValueError(
                    "Unable to find shot %s in sequence %s, aborting..." %
                    (shot_name, sequence_path))
        # Set settings from presets, if any
        if presets:
            job.set_preset_origin(presets)
        # Ensure the settings we need are set.
        config = job.get_configuration()
        # https://docs.unrealengine.com/4.26/en-US/PythonAPI/class/MoviePipelineOutputSetting.html?highlight=setting#unreal.MoviePipelineOutputSetting
        output_setting = config.find_or_add_setting_by_class(
            unreal.MoviePipelineOutputSetting)
        output_setting.output_directory = unreal.DirectoryPath(output_folder)
        output_setting.output_resolution = unreal.IntPoint(1280, 720)
        output_setting.file_name_format = movie_name
        output_setting.override_existing_output = True  # Overwrite existing files
        # If needed we could enforce a frame rate, like for the Sequencer code.
        # output_setting.output_frame_rate = unreal.FrameRate(24)
        # output_setting.use_custom_frame_rate = True
        # Remove problematic settings
        for setting, reason in self._check_render_settings(config):
            self.logger.warning("Disabling %s: %s." %
                                (setting.get_name(), reason))
            config.remove_setting(setting)

        # Default rendering
        config.find_or_add_setting_by_class(
            unreal.MoviePipelineDeferredPassBase)
        # Render to a movie
        config.find_or_add_setting_by_class(
            unreal.MoviePipelineAppleProResOutput)
        # TODO: check which codec we should use.

        # We render in a forked process that we can control.
        # It would be possible to render in from the running process using an
        # Executor, however it seems to sometimes deadlock if we don't let Unreal
        # process its internal events, rendering is asynchronous and being notified
        # when the render completed does not seem to be reliable.
        # Sample code:
        #    exc = unreal.MoviePipelinePIEExecutor()
        #    # If needed, we can store data in exc.user_data
        #    # In theory we can set a callback to be notified about completion
        #    def _on_movie_render_finished_cb(executor, result):
        #       print("Executor %s finished with %s" % (executor, result))
        #    # exc.on_executor_finished_delegate.add_callable(_on_movie_render_finished_cb)
        #    r = qsub.render_queue_with_executor_instance(exc)

        # We can't control the name of the manifest file, so we save and then rename the file.
        _, manifest_path = unreal.MoviePipelineEditorLibrary.save_queue_to_manifest_file(
            queue)

        manifest_path = os.path.abspath(manifest_path)
        manifest_dir, manifest_file = os.path.split(manifest_path)
        f, new_path = tempfile.mkstemp(
            suffix=os.path.splitext(manifest_file)[1], dir=manifest_dir)
        os.close(f)
        os.replace(manifest_path, new_path)

        self.logger.debug("Queue manifest saved in %s" % new_path)
        # We now need a path local to the unreal project "Saved" folder.
        manifest_path = new_path.replace(
            "%s%s" % (
                os.path.abspath(
                    os.path.join(unreal.SystemLibrary.get_project_directory(),
                                 "Saved")),
                os.path.sep,
            ),
            "",
        )
        self.logger.debug("Manifest short path: %s" % manifest_path)
        # Command line parameters were retrieved by submitting a queue in Unreal Editor with
        # a MoviePipelineNewProcessExecutor executor.
        # https://docs.unrealengine.com/4.27/en-US/PythonAPI/class/MoviePipelineNewProcessExecutor.html?highlight=executor
        cmd_args = [
            sys.executable,
            "%s" % os.path.join(
                unreal.SystemLibrary.get_project_directory(),
                "%s.uproject" % unreal.SystemLibrary.get_game_name(),
            ),
            "MoviePipelineEntryMap?game=/Script/MovieRenderPipelineCore.MoviePipelineGameMode",
            "-game",
            "-Multiprocess",
            "-NoLoadingScreen",
            "-FixedSeed",
            "-log",
            "-Unattended",
            "-messaging",
            "-SessionName=\"Publish2 Movie Render\"",
            "-nohmd",
            "-windowed",
            "-ResX=1280",
            "-ResY=720",
            # TODO: check what these settings are
            "-dpcvars=%s" % ",".join([
                "sg.ViewDistanceQuality=4",
                "sg.AntiAliasingQuality=4",
                "sg.ShadowQuality=4",
                "sg.PostProcessQuality=4",
                "sg.TextureQuality=4",
                "sg.EffectsQuality=4",
                "sg.FoliageQuality=4",
                "sg.ShadingQuality=4",
                "r.TextureStreaming=0",
                "r.ForceLOD=0",
                "r.SkeletalMeshLODBias=-10",
                "r.ParticleLODBias=-10",
                "foliage.DitheredLOD=0",
                "foliage.ForceLOD=0",
                "r.Shadow.DistanceScale=10",
                "r.ShadowQuality=5",
                "r.Shadow.RadiusThreshold=0.001000",
                "r.ViewDistanceScale=50",
                "r.D3D12.GPUTimeout=0",
                "a.URO.Enable=0",
            ]),
            "-execcmds=r.HLOD 0",
            # This need to be a path relative the to the Unreal project "Saved" folder.
            "-MoviePipelineConfig=\"%s\"" % manifest_path,
        ]
        unreal.log("Movie Queue command-line arguments: {}".format(
            " ".join(cmd_args)))
        # Make a shallow copy of the current environment and clear some variables
        run_env = copy.copy(os.environ)
        # Prevent SG TK to try to bootstrap in the new process
        if "UE_SHOTGUN_BOOTSTRAP" in run_env:
            del run_env["UE_SHOTGUN_BOOTSTRAP"]
        if "UE_SHOTGRID_BOOTSTRAP" in run_env:
            del run_env["UE_SHOTGRID_BOOTSTRAP"]
        self.logger.info("Running %s" % cmd_args)
        subprocess.call(cmd_args, env=run_env)
        return os.path.isfile(output_path), output_path
Example #7
0
def render_sequence_to_movie(sequencer_asset_path):
    # 1) Create an instance of our UAutomatedLevelSequenceCapture and override all of the settings on it. This class is currently
    # set as a config class so settings will leak between the Unreal Sequencer Render-to-Movie UI and this object. To work around
    # this, we set every setting via the script so that no changes the user has made via the UI will affect the script version.
    # The users UI settings will be reset as an unfortunate side effect of this.
    capture_settings = unreal.AutomatedLevelSequenceCapture()

    # Set all POD settings on the UMovieSceneCapture
    capture_settings.settings.output_directory = unreal.DirectoryPath(
        "../../../QAGame/Saved/VideoCaptures/")

    # If you game mode is implemented in Blueprint, load_asset(...) is going to return you the C++ type ('Blueprint') and not what the BP says it inherits from.
    # Instead, because game_mode_override is a TSubclassOf<AGameModeBase> we can use unreal.load_class to get the UClass which is implicitly convertable.
    # ie: capture_settings.settings.game_mode_override = unreal.load_class(None, "/Game/AI/TestingSupport/AITestingGameMode.AITestingGameMode_C")
    capture_settings.settings.game_mode_override = None
    capture_settings.settings.output_format = "{world}"
    capture_settings.settings.overwrite_existing = True
    capture_settings.settings.use_relative_frame_numbers = False
    capture_settings.settings.handle_frames = 0
    capture_settings.settings.zero_pad_frame_numbers = 4
    # If you wish to override the output framerate you can use these two lines, otherwise the framerate will be derived from the sequence being rendered
    capture_settings.settings.use_custom_frame_rate = True
    capture_settings.settings.custom_frame_rate = unreal.FrameRate(24, 1)
    capture_settings.settings.resolution.res_x = 1280
    capture_settings.settings.resolution.res_y = 720
    capture_settings.settings.enable_texture_streaming = False
    capture_settings.settings.cinematic_engine_scalability = True
    capture_settings.settings.cinematic_mode = True
    capture_settings.settings.allow_movement = False  # Requires cinematic_mode = True
    capture_settings.settings.allow_turning = False  # Requires cinematic_mode = True
    capture_settings.settings.show_player = False  # Requires cinematic_mode = True
    capture_settings.settings.show_hud = False  # Requires cinematic_mode = True
    capture_settings.use_separate_process = False
    capture_settings.close_editor_when_capture_starts = False  # Requires use_separate_process = True
    capture_settings.additional_command_line_arguments = "-NOSCREENMESSAGES"  # Requires use_separate_process = True
    capture_settings.inherited_command_line_arguments = ""  # Requires use_separate_process = True

    # Set all the POD settings on UAutomatedLevelSequenceCapture
    capture_settings.use_custom_start_frame = False  # If False, the system will automatically calculate the start based on sequence content
    capture_settings.use_custom_end_frame = False  # If False, the system will automatically calculate the end based on sequence content
    capture_settings.custom_start_frame = unreal.FrameNumber(
        0)  # Requires use_custom_start_frame = True
    capture_settings.custom_end_frame = unreal.FrameNumber(
        0)  # Requires use_custom_end_frame = True
    capture_settings.warm_up_frame_count = 0.0
    capture_settings.delay_before_warm_up = 0
    capture_settings.delay_before_shot_warm_up = 0.0
    capture_settings.write_edit_decision_list = True

    # Tell the capture settings which level sequence to render with these settings. The asset does not need to be loaded,
    # as we're only capturing the path to it and when the PIE instance is created it will load the specified asset.
    # If you only had a reference to the level sequence, you could use "unreal.SoftObjectPath(mysequence.get_path_name())"
    capture_settings.level_sequence_asset = unreal.SoftObjectPath(
        sequencer_asset_path)

    # To configure the video output we need to tell the capture settings which capture protocol to use. The various supported
    # capture protocols can be found by setting the Unreal Content Browser to "Engine C++ Classes" and filtering for "Protocol"
    # ie: CompositionGraphCaptureProtocol, ImageSequenceProtocol_PNG, etc. Do note that some of the listed protocols are not intended
    # to be used directly.
    # Right click on a Protocol and use "Copy Reference" and then remove the extra formatting around it. ie:
    # Class'/Script/MovieSceneCapture.ImageSequenceProtocol_PNG' gets transformed into "/Script/MovieSceneCapture.ImageSequenceProtocol_PNG"
    capture_settings.set_image_capture_protocol_type(
        unreal.load_class(
            None, "/Script/MovieSceneCapture.ImageSequenceProtocol_PNG"))

    # After we have set the capture protocol to a soft class path we can start editing the settings for the instance of the protocol that is internallyc reated.
    capture_settings.get_image_capture_protocol().compression_quality = 100

    # The other complex settings is the burn-in. Create an instance of the LevelSequenceBurnInOptions which is used to
    # specify if we should use a burn in, and then which settings.
    burn_in_options = unreal.LevelSequenceBurnInOptions()
    burn_in_options.use_burn_in = True

    # You have to specify a path to a class to use for the burn in (if use_burn_in = True), and this class specifies a UClass to define the
    # settings object type. We've created a convinence function which takes the class path, loads the class at that path and assigns it to
    # the Settings object.
    burn_in_options.set_burn_in(
        unreal.SoftClassPath(
            "/Engine/Sequencer/DefaultBurnIn.DefaultBurnIn_C"))

    # The default burn in is implemented entirely in Blueprint which means that the method we've been using to set properties will not
    # work for it. The python bindings that turn bSomeVariableName into "some_variable_name" only work for C++ classes with
    # UPROPERTY(BlueprintReadWrite) marked fields. Python doesn't know about the existence of Blueprint classes and their fields, so we
    # have to use an alternative method.
    burn_in_options.settings.set_editor_property(
        'TopLeftText', "{FocalLength}mm,{Aperture},{FocusDistance}")
    burn_in_options.settings.set_editor_property(
        'TopCenterText', "{MasterName} - {Date} - {EngineVersion}")
    burn_in_options.settings.set_editor_property(
        'TopRightText',
        "{TranslationX} {TranslationY} {TranslationZ}, {RotationX} {RotationY} {RotationZ}"
    )

    burn_in_options.settings.set_editor_property('BottomLeftText',
                                                 "{ShotName}")
    burn_in_options.settings.set_editor_property(
        'BottomCenterText', "{hh}:{mm}:{ss}:{ff} ({MasterFrame})")
    burn_in_options.settings.set_editor_property('BottomRightText',
                                                 "{ShotFrame}")

    # Load a Texture2D asset and assign it to the UTexture2D reference that Watermark is.
    # burn_in_settings.set_editor_property('Watermark', None)
    # Note that this example creates a really obvious watermark (a big blurry green smiley face) just so that you know it's working!
    burn_in_options.settings.set_editor_property(
        'Watermark', unreal.load_asset("/Engine/EngineResources/AICON-Green"))
    burn_in_options.settings.set_editor_property(
        'WatermarkTint',
        unreal.LinearColor(1.0, 0.5, 0.5,
                           0.5))  # Create a FLinearColor to tint our Watermark

    # Assign our created instances to our original capture_settings object.
    capture_settings.burn_in_options = burn_in_options

    # Finally invoke Sequencer's Render to Movie functionality. This will examine the specified settings object and either construct a new PIE instance to render in,
    # or create and launch a new process (optionally shutting down your editor).
    unreal.SequencerTools.render_movie(capture_settings, on_finished_callback)