diff --git a/Release log.txt b/Release log.txt index 6b34839a..325467a5 100644 --- a/Release log.txt +++ b/Release log.txt @@ -405,12 +405,18 @@ It is now possible to force the duration of an animation according to the scene == Rev 0.3.1 == +- New: Generated import script compatible with Python 3.9 (Unreal Engine 5) +- New: Text generator for export camera in Unreal Engine (Copy / Paste). - New potential errors in the check: If you use Parent Bone to parent your mesh to your armature the import will fail. -- New: Generated import script compatible with Python 3.9 (Unreal Engine Preview 2) +- You can now use Force Static Mesh and Export Deform Only options with proxy armatures. +- Naming asset updated for Unreal Engine 5. - Fixed: GetObjExportDir remove ":" in path. - Fixed: GetExportFullpath remove ":" in path. - Fixed: Remove the active object if an exported collection contain a single object. - Fixed: With Unit Scale not at 0.01 and Proxy use animation export will fail. - Fixed: With Unit Scale not at 0.01 and Proxy ShapeKeysCurve scale is wrong. - Fixed: Export camera can do axis flipping. -- Fixed: Import script doesn't work with Unreal Engine Preview 2. +- Fixed: NLA export fail when use inter frame. +- Fixed: NLA export ignore animated influence on actions. +- Fixed: Import script doesn't work with Unreal Engine 5. +- Fixed: Export doesn't work with animation from Proxy Since Blender 3.2.2 diff --git a/blender-for-unrealengine/__init__.py b/blender-for-unrealengine/__init__.py index d78711e8..982f87b3 100644 --- a/blender-for-unrealengine/__init__.py +++ b/blender-for-unrealengine/__init__.py @@ -121,6 +121,7 @@ def register(): bpy.types.Scene.bfu_collection_properties_expanded = bpy.props.BoolProperty() bpy.types.Scene.bfu_object_advanced_properties_expanded = bpy.props.BoolProperty() bpy.types.Scene.bfu_export_type_expanded = bpy.props.BoolProperty() + bpy.types.Scene.bfu_camera_expanded = bpy.props.BoolProperty() bpy.types.Scene.bfu_collision_socket_expanded = bpy.props.BoolProperty() bpy.types.Scene.bfu_lightmap_expanded = bpy.props.BoolProperty() bpy.types.Scene.bfu_nomenclature_properties_expanded = bpy.props.BoolProperty() diff --git a/blender-for-unrealengine/bfu_ui.py b/blender-for-unrealengine/bfu_ui.py index cf9bd898..22be8347 100644 --- a/blender-for-unrealengine/bfu_ui.py +++ b/blender-for-unrealengine/bfu_ui.py @@ -305,7 +305,7 @@ class BFU_PT_BlenderForUnrealObject(bpy.types.Panel): name="", description="The name of the Skeleton in Unreal. Skeleton not the skeletal mesh.", override={'LIBRARY_OVERRIDABLE'}, - default="SK_MySketonName_Skeleton" + default="SKM_MySketonName_Skeleton" ) bpy.types.Object.bfu_target_skeleton_custom_ref = StringProperty( @@ -315,7 +315,7 @@ class BFU_PT_BlenderForUnrealObject(bpy.types.Panel): "(Use right clic on asset and copy reference.)" ), override={'LIBRARY_OVERRIDABLE'}, - default="SkeletalMesh'/Game/ImportedFbx/SK_MySketonName_Skeleton.SK_MySketonName_Skeleton'" + default="SkeletalMesh'/Game/ImportedFbx/SKM_MySketonName_Skeleton.SKM_MySketonName_Skeleton'" ) # StaticMeshImportData @@ -900,6 +900,36 @@ def execute(self, context): ) return {'FINISHED'} + class BFU_OT_CopyRegularCameraButton(Operator): + bl_label = "Copy Regular Camera for Unreal" + bl_idname = "object.copy_regular_camera_command" + bl_description = "Copy Regular Camera Script command" + + def execute(self, context): + obj = context.object + result = GetImportCameraScriptCommand([obj], False) + if result[0]: + setWindowsClipboard(result[1]) + self.report({'INFO'}, result[2]) + else: + self.report({'WARNING'}, result[2]) + return {'FINISHED'} + + class BFU_OT_CopyCineCameraButton(Operator): + bl_label = "Copy Cine Camera for Unreal" + bl_idname = "object.copy_cine_camera_command" + bl_description = "Copy Cine Camera Script command" + + def execute(self, context): + obj = context.object + result = GetImportCameraScriptCommand([obj], False) + if result[0]: + setWindowsClipboard(result[1]) + self.report({'INFO'}, result[2]) + else: + self.report({'WARNING'}, result[2]) + return {'FINISHED'} + class BFU_OT_ComputLightMap(Operator): bl_label = "Calculate surface area" bl_idname = "object.computlightmap" @@ -914,21 +944,6 @@ def execute(self, context): "Compunted Light map: " + str(GetCompuntedLightMap(obj))) return {'FINISHED'} - class BFU_OT_ComputAllLightMap(Operator): - bl_label = "Calculate all surface area" - bl_idname = "object.computalllightmap" - bl_description = ( - "Click to calculate the surface of the all object in the scene" - ) - - def execute(self, context): - updated = UpdateAreaLightMapList() - self.report( - {'INFO'}, - "The light maps of " + str(updated) + - " object(s) have been updated." - ) - return {'FINISHED'} # Animation : @@ -1292,6 +1307,12 @@ def draw(self, contex): ExportType = layout.column() ExportType.prop(obj, 'ExportEnum') + if obj.type == "CAMERA": + CameraProp = layout.column() + CameraProp.operator("object.copy_regular_camera_command", icon="COPYDOWN") + CameraProp.operator("object.copy_cine_camera_command", icon="COPYDOWN") + + if obj.ExportEnum == "export_recursive": folderNameProperty = layout.column() @@ -1302,7 +1323,6 @@ def draw(self, contex): ) if obj.type == "CAMERA": - CameraProp = layout.column() CameraProp.prop(obj, 'bfu_export_fbx_camera') else: @@ -1340,14 +1360,15 @@ def draw(self, contex): if GetAssetType(obj) != "SkeletalMesh": LodProp = layout.column() LodProp.prop(obj, 'ExportAsLod') + if not obj.ExportAsAlembic: + if obj.type == "ARMATURE": + AssetType2 = layout.column() + # Show asset type + AssetType2.prop(obj, "ForceStaticMesh") + if GetAssetType(obj) == "SkeletalMesh": + AssetType2.prop(obj, 'exportDeformOnly') - if obj.type == "ARMATURE": - AssetType2 = layout.column() - # Show asset type - AssetType2.prop(obj, "ForceStaticMesh") - if GetAssetType(obj) == "SkeletalMesh": - AssetType2.prop(obj, 'exportDeformOnly') - + if not GetExportAsProxy(obj): # exportCustomName exportCustomName = layout.row() exportCustomName.prop(obj, "bfu_use_custom_export_name") @@ -1704,6 +1725,36 @@ class BFU_PT_BlenderForUnrealTool(bpy.types.Panel): default="MySocket" ) + class BFU_OT_CopyRegularCamerasButton(Operator): + bl_label = "Copy Regular Cameras for Unreal" + bl_idname = "object.copy_regular_cameras_command" + bl_description = "Copy Regular Cameras Script command" + + def execute(self, context): + objs = context.selected_objects + result = GetImportCameraScriptCommand(objs, False) + if result[0]: + setWindowsClipboard(result[1]) + self.report({'INFO'}, result[2]) + else: + self.report({'WARNING'}, result[2]) + return {'FINISHED'} + + class BFU_OT_CopyCineCamerasButton(Operator): + bl_label = "Copy Cine Cameras for Unreal" + bl_idname = "object.copy_cine_cameras_command" + bl_description = "Copy Cine Cameras Script command" + + def execute(self, context): + objs = context.selected_objects + result = GetImportCameraScriptCommand(objs, False) + if result[0]: + setWindowsClipboard(result[1]) + self.report({'INFO'}, result[2]) + else: + self.report({'WARNING'}, result[2]) + return {'FINISHED'} + class BFU_OT_ConvertToCollisionButtonBox(Operator): bl_label = "Convert to box (UBX)" bl_idname = "object.converttoboxcollision" @@ -1822,7 +1873,7 @@ class BFU_OT_ConvertToSkeletalSocketButton(Operator): " to Unreal sockets ready for export (SkeletalMesh)") def execute(self, context): - ConvertedObj = Ue4SubObj_set("SK_Socket") + ConvertedObj = Ue4SubObj_set("SKM_Socket") if len(ConvertedObj) > 0: self.report( {'INFO'}, @@ -1849,7 +1900,23 @@ def execute(self, context): setWindowsClipboard(GetImportSkeletalMeshSocketScriptCommand(obj)) self.report( {'INFO'}, - "Skeletal sockets copied. Paste in Unreal Engine for import sockets.") + "Skeletal sockets copied. Paste in Unreal Engine Skeletal Mesh assets for import sockets. (Ctrl+V)") + return {'FINISHED'} + + class BFU_OT_ComputAllLightMap(Operator): + bl_label = "Calculate all surface area" + bl_idname = "object.computalllightmap" + bl_description = ( + "Click to calculate the surface of the all object in the scene" + ) + + def execute(self, context): + updated = UpdateAreaLightMapList() + self.report( + {'INFO'}, + "The light maps of " + str(updated) + + " object(s) have been updated." + ) return {'FINISHED'} def draw(self, context): @@ -1903,6 +1970,12 @@ def FoundTypeInSelect(targetType, include_active=True): export_type_cameras.operator("object.converttoboxcollision", icon='MESH_CUBE') ''' + bfu_ui_utils.LayoutSection(layout, "bfu_camera_expanded", "Camera") + if scene.bfu_camera_expanded: + copy_camera_buttons = layout.column() + copy_camera_buttons.operator("object.copy_regular_cameras_command", icon="COPYDOWN") + copy_camera_buttons.operator("object.copy_cine_cameras_command", icon="COPYDOWN") + bfu_ui_utils.LayoutSection(layout, "bfu_collision_socket_expanded", "Collision and Socket") if scene.bfu_collision_socket_expanded: if not ActiveModeIs("OBJECT"): @@ -1981,7 +2054,7 @@ def FoundTypeInSelect(targetType, include_active=True): copy_skeletalsocket_buttons.enabled = False copy_skeletalsocket_buttons.operator( "object.copy_skeletalsocket_command", - icon='OUTLINER_DATA_EMPTY') + icon='COPYDOWN') if obj is not None: if obj.type == "ARMATURE": copy_skeletalsocket_buttons.enabled = True @@ -2114,23 +2187,29 @@ class BFU_PT_Export(bpy.types.Panel): bl_category = "Unreal Engine" # Prefix - bpy.types.Scene.static_prefix_export_name = bpy.props.StringProperty( + bpy.types.Scene.static_mesh_prefix_export_name = bpy.props.StringProperty( name="StaticMesh Prefix", description="Prefix of staticMesh", maxlen=32, default="SM_") - bpy.types.Scene.skeletal_prefix_export_name = bpy.props.StringProperty( + bpy.types.Scene.skeletal_mesh_prefix_export_name = bpy.props.StringProperty( name="SkeletalMesh Prefix ", description="Prefix of SkeletalMesh", maxlen=32, + default="SKM_") + + bpy.types.Scene.skeleton_prefix_export_name = bpy.props.StringProperty( + name="skeleton Prefix ", + description="Prefix of skeleton", + maxlen=32, default="SK_") bpy.types.Scene.alembic_prefix_export_name = bpy.props.StringProperty( name="Alembic Prefix ", description="Prefix of Alembic (SkeletalMesh in unreal)", maxlen=32, - default="SK_") + default="SKM_") bpy.types.Scene.anim_prefix_export_name = bpy.props.StringProperty( name="AnimationSequence Prefix", @@ -2242,8 +2321,9 @@ class BFU_OT_AddNomenclaturePreset(AddPresetBase, Operator): # Properties to store in the preset preset_values = [ - 'scene.static_prefix_export_name', - 'scene.skeletal_prefix_export_name', + 'scene.static_mesh_prefix_export_name', + 'scene.skeletal_mesh_prefix_export_name', + 'scene.skeleton_prefix_export_name', 'scene.alembic_prefix_export_name', 'scene.anim_prefix_export_name', 'scene.pose_prefix_export_name', @@ -2701,11 +2781,9 @@ def draw(self, context): # Prefix propsPrefix = self.layout.row() propsPrefix = propsPrefix.column() - propsPrefix.prop(scene, 'static_prefix_export_name', icon='OBJECT_DATA') - propsPrefix.prop( - scene, - 'skeletal_prefix_export_name', - icon='OBJECT_DATA') + propsPrefix.prop(scene, 'static_mesh_prefix_export_name', icon='OBJECT_DATA') + propsPrefix.prop(scene, 'skeletal_mesh_prefix_export_name', icon='OBJECT_DATA') + propsPrefix.prop(scene, 'skeleton_prefix_export_name', icon='OBJECT_DATA') propsPrefix.prop(scene, 'alembic_prefix_export_name', icon='OBJECT_DATA') propsPrefix.prop(scene, 'anim_prefix_export_name', icon='OBJECT_DATA') propsPrefix.prop(scene, 'pose_prefix_export_name', icon='OBJECT_DATA') @@ -2851,6 +2929,8 @@ def execute(self, context): BFU_PT_BlenderForUnrealObject.BFU_MT_ObjectGlobalPropertiesPresets, BFU_PT_BlenderForUnrealObject.BFU_OT_AddObjectGlobalPropertiesPreset, BFU_PT_BlenderForUnrealObject.BFU_OT_OpenDocumentationPage, + BFU_PT_BlenderForUnrealObject.BFU_OT_CopyRegularCameraButton, + BFU_PT_BlenderForUnrealObject.BFU_OT_CopyCineCameraButton, BFU_PT_BlenderForUnrealObject.BFU_OT_ComputLightMap, BFU_PT_BlenderForUnrealObject.BFU_UL_ActionExportTarget, BFU_PT_BlenderForUnrealObject.BFU_OT_UpdateObjActionListButton, @@ -2860,6 +2940,8 @@ def execute(self, context): BFU_PT_BlenderForUnrealObject.BFU_OT_ShowCollectionToExport, BFU_PT_BlenderForUnrealTool, + BFU_PT_BlenderForUnrealTool.BFU_OT_CopyRegularCamerasButton, + BFU_PT_BlenderForUnrealTool.BFU_OT_CopyCineCamerasButton, BFU_PT_BlenderForUnrealTool.BFU_OT_ConvertToCollisionButtonBox, BFU_PT_BlenderForUnrealTool.BFU_OT_ConvertToCollisionButtonCapsule, BFU_PT_BlenderForUnrealTool.BFU_OT_ConvertToCollisionButtonSphere, @@ -2867,7 +2949,7 @@ def execute(self, context): BFU_PT_BlenderForUnrealTool.BFU_OT_ConvertToStaticSocketButton, BFU_PT_BlenderForUnrealTool.BFU_OT_ConvertToSkeletalSocketButton, BFU_PT_BlenderForUnrealTool.BFU_OT_CopySkeletalSocketButton, - BFU_PT_BlenderForUnrealObject.BFU_OT_ComputAllLightMap, + BFU_PT_BlenderForUnrealTool.BFU_OT_ComputAllLightMap, # BFU_PT_BlenderForUnrealDebug, # Unhide for dev diff --git a/blender-for-unrealengine/bfu_utils.py b/blender-for-unrealengine/bfu_utils.py index c12e3441..4970ed1b 100644 --- a/blender-for-unrealengine/bfu_utils.py +++ b/blender-for-unrealengine/bfu_utils.py @@ -29,8 +29,13 @@ if "bpy" in locals(): import importlib + if "bfu_write_text" in locals(): + importlib.reload(bfu_write_text) if "bfu_basics" in locals(): importlib.reload(bfu_basics) + + +from . import bfu_write_text from . import bfu_basics from .bfu_basics import * @@ -297,7 +302,7 @@ def ApplySaveOnTarget(self, target): new_nla_track.select = nla_track.select for strip in nla_track.strips: if strip.action: - new_strip = new_nla_track.strips.new(strip.name, strip.frame_start, strip.action) + new_strip = new_nla_track.strips.new(strip.name, int(strip.frame_start), strip.action) # new_strip.action = strip.action new_strip.action_frame_end = strip.action_frame_end new_strip.action_frame_start = strip.action_frame_start @@ -306,10 +311,18 @@ def ApplySaveOnTarget(self, target): new_strip.blend_out = strip.blend_out new_strip.blend_type = strip.blend_type new_strip.extrapolation = strip.extrapolation - # new_strip.fcurves = strip.fcurves #TO DO + # new_strip.fcurves = strip.fcurves new_strip.frame_end = strip.frame_end # new_strip.frame_start = strip.frame_start + new_strip.use_animated_influence = strip.use_animated_influence new_strip.influence = strip.influence + print(new_strip.influence) + print(new_strip.influence) + print(new_strip.influence) + print(new_strip.influence) + print(new_strip.influence) + print(new_strip.influence) + print(new_strip.influence) # new_strip.modifiers = strip.modifiers #TO DO new_strip.mute = strip.mute # new_strip.name = strip.name @@ -318,6 +331,28 @@ def ApplySaveOnTarget(self, target): new_strip.select = strip.select new_strip.strip_time = strip.strip_time # new_strip.strips = strip.strips #TO DO + for i, fcurve in enumerate(strip.fcurves): + if fcurve: + new_fcurve = new_strip.fcurves.find(fcurve.data_path) + if new_fcurve: + new_fcurve.array_index = fcurve.array_index + new_fcurve.color = fcurve.color + new_fcurve.color_mode = fcurve.color_mode + # new_fcurve.data_path = fcurve.data_path + # new_fcurve.driver = fcurve.driver #TO DO + new_fcurve.extrapolation = fcurve.extrapolation + new_fcurve.group = fcurve.group + new_fcurve.hide = fcurve.hide + # new_fcurve.is_empty = fcurve.is_empty + # new_fcurve.is_valid = fcurve.is_valid + # new_fcurve.keyframe_points = fcurve.keyframe_points + new_fcurve.lock = fcurve.lock + # new_fcurve.modifiers = fcurve.modifiers #TO DO + new_fcurve.mute = fcurve.mute + # new_fcurve.sampled_points = fcurve.sampled_points + new_fcurve.select = fcurve.select + for keyframe_point in fcurve.keyframe_points: + new_fcurve.keyframe_points.insert(keyframe_point.co[0], keyframe_point.co[1]) class Proxy_NLA_Track(): def __init__(self, nla_track): @@ -345,6 +380,7 @@ def __init__(self, strip): self.fcurves = strip.fcurves # TO DO self.frame_end = strip.frame_end self.frame_start = strip.frame_start + self.use_animated_influence = strip.use_animated_influence self.influence = strip.influence self.modifiers = strip.modifiers # TO DO self.mute = strip.mute @@ -1656,7 +1692,7 @@ def GetCollectionExportFileName(collection, fileType=".fbx"): # Generate assset file name scene = bpy.context.scene - return scene.static_prefix_export_name+collection+fileType + return scene.static_mesh_prefix_export_name+collection+fileType def GetObjExportFileName(obj, fileType=".fbx"): @@ -1669,9 +1705,9 @@ def GetObjExportFileName(obj, fileType=".fbx"): if assetType == "Camera": return ValidFilename(scene.camera_prefix_export_name+obj.name+fileType) elif assetType == "StaticMesh": - return ValidFilename(scene.static_prefix_export_name+obj.name+fileType) + return ValidFilename(scene.static_mesh_prefix_export_name+obj.name+fileType) elif assetType == "SkeletalMesh": - return ValidFilename(scene.skeletal_prefix_export_name+obj.name+fileType) + return ValidFilename(scene.skeletal_mesh_prefix_export_name+obj.name+fileType) elif assetType == "Alembic": return ValidFilename(scene.alembic_prefix_export_name+obj.name+fileType) else: @@ -1724,6 +1760,124 @@ def GetImportAssetScriptCommand(): return 'py "'+fullpath+'"' +def GetImportCameraScriptCommand(objs, CineCamera=True): + # Return (success, command) + + success = False + command = "" + report = "" + add_camera_num = 0 + + def AddCameraToCommand(camera): + if camera.type == "CAMERA": + t = "" + # Get Camera Data + scene = bpy.context.scene + frame_current = scene.frame_current + + # First I get the camera data. + # This is a very bad way to do this. I need do a new python file specific to camera with class to get data. + data = bfu_write_text.WriteCameraAnimationTracks(camera, frame_current, frame_current) + transform_track = data["Camera transform"][frame_current] + location_x = transform_track["location_x"] + location_y = transform_track["location_y"] + location_z = transform_track["location_z"] + rotation_x = transform_track["rotation_x"] + rotation_y = transform_track["rotation_y"] + rotation_z = transform_track["rotation_z"] + scale_x = transform_track["scale_x"] + scale_y = transform_track["scale_y"] + scale_z = transform_track["scale_z"] + FieldOfView = data["Camera FieldOfView"][frame_current] + FocalLength = data["Camera FocalLength"][frame_current] + SensorWidth = data["Camera SensorWidth"][frame_current] + SensorHeight = data["Camera SensorHeight"][frame_current] + FocusDistance = data["Camera FocusDistance"][frame_current] + Aperture = data["Camera Aperture"][frame_current] + AspectRatio = data["desired_screen_ratio"] + CameraName = camera.name + + # Actor + if CineCamera: + t += " " + "Begin Actor Class=/Script/CinematicCamera.CineCameraActor Name="+CameraName+" Archetype=/Script/CinematicCamera.CineCameraActor'/Script/CinematicCamera.Default__CineCameraActor'" + "\n" + else: + t += " " + "Begin Actor Class=/Script/Engine.CameraActor Name="+CameraName+" Archetype=/Script/Engine.CameraActor'/Script/Engine.Default__CameraActor'" + "\n" + + # Init SceneComponent + if CineCamera: + t += " " + "Begin Object Class=/Script/Engine.SceneComponent Name=\"SceneComponent\" Archetype=SceneComponent'/Script/CinematicCamera.Default__CineCameraActor:SceneComponent'" + "\n" + t += " " + "End Object" + "\n" + else: + t += " " + "Begin Object Class=/Script/Engine.SceneComponent Name=\"SceneComponent\" Archetype=SceneComponent'/Script/Engine.Default__CameraActor:SceneComponent'" + "\n" + t += " " + "End Object" + "\n" + + # Init CameraComponent + if CineCamera: + t += " " + "Begin Object Class=/Script/CinematicCamera.CineCameraComponent Name=\"CameraComponent\" Archetype=CineCameraComponent'/Script/CinematicCamera.Default__CineCameraActor:CameraComponent'" + "\n" + t += " " + "End Object" + "\n" + else: + t += " " + "Begin Object Class=/Script/Engine.CameraComponent Name=\"CameraComponent\" Archetype=CameraComponent'/Script/Engine.Default__CameraActor:CameraComponent'" + "\n" + t += " " + "End Object" + "\n" + + # SceneComponent + t += " " + "Begin Object Name=\"SceneComponent\"" + "\n" + t += " " + "RelativeLocation=(X="+str(location_x)+",Y="+str(location_y)+",Z="+str(location_z)+")" + "\n" + t += " " + "RelativeRotation=(Pitch="+str(rotation_y)+",Yaw="+str(rotation_z)+",Roll="+str(rotation_x)+")" + "\n" + t += " " + "RelativeScale3D=(X="+str(scale_x)+",Y="+str(scale_y)+",Z="+str(scale_z)+")" + "\n" + t += " " + "End Object" + "\n" + + # CameraComponent + t += " " + "Begin Object Name=\"CameraComponent\"" + "\n" + t += " " + "Filmback=(SensorWidth="+str(SensorWidth)+",SensorHeight="+str(SensorHeight)+", SensorAspectRatio="+str(AspectRatio)+")" + "\n" + t += " " + "CurrentAperture="+str(Aperture)+")" + "\n" + t += " " + "CurrentFocalLength="+str(FocalLength)+")" + "\n" + t += " " + "CurrentFocusDistance="+str(FocusDistance)+")" + "\n" + t += " " + "CurrentFocusDistance="+str(FocusDistance)+")" + "\n" + t += " " + "FieldOfView="+str(FieldOfView)+")" + "\n" + t += " " + "AspectRatio="+str(AspectRatio)+")" + "\n" + t += " " + "End Object" + "\n" + + # Attach + t += " " + "CameraComponent=\"CameraComponent\"" + "\n" + t += " " + "SceneComponent=\"SceneComponent\"" + "\n" + t += " " + "RootComponent=\"SceneComponent\"" + "\n" + t += " " + "ActorLabel=\""+CameraName+"\"" + "\n" + + # Close + t += " " + "End Actor" + "\n" + return t + return None + + cameras = [] + for obj in objs: + if obj.type == "CAMERA": + cameras.append(obj) + + if len(cameras) == 0: + report = "Please select at least one camera." + return (success, command, report) + + # And I apply the camrta data to the copy paste text. + t = "Begin Map" + "\n" + t += " " + "Begin Level" + "\n" + for camera in cameras: + add_command = AddCameraToCommand(camera) + if add_command: + t += add_command + add_camera_num += 1 + + t += " " + "End Level" + "\n" + t += "Begin Surface" + "\n" + t += "End Surface" + "\n" + t += "End Object" + "\n" + + success = True + command = t + report = str(add_camera_num)+" camera(s) copied. Paste in Unreal Engine scene for import the camera. (Ctrl+V)" + + return (success, command, report) + + def GetImportSkeletalMeshSocketScriptCommand(obj): if obj: @@ -1744,7 +1898,7 @@ def GetImportSkeletalMeshSocketScriptCommand(obj): t += "\t" + 'RelativeScale=' + "(X="+str(s[0])+",Y="+str(s[1])+",Z="+str(s[2])+")" + "\n" t += "End Object" + "\n" return t - return "Select a Skeletal Mesh" + return "Please select an armature." def GetImportSequencerScriptCommand(): diff --git a/blender-for-unrealengine/bfu_write_import_asset_script.py b/blender-for-unrealengine/bfu_write_import_asset_script.py index 9f8218a5..b3d275c9 100644 --- a/blender-for-unrealengine/bfu_write_import_asset_script.py +++ b/blender-for-unrealengine/bfu_write_import_asset_script.py @@ -103,7 +103,7 @@ def WriteImportAssetScript(): if GetIsAnimation(asset.asset_type): if(asset.object.bfu_skeleton_search_mode) == "auto": - customName = scene.skeletal_prefix_export_name+ValidUnrealAssetsName(asset.skeleton_name)+"_Skeleton" + customName = scene.skeletal_mesh_prefix_export_name+ValidUnrealAssetsName(asset.skeleton_name)+"_Skeleton" SkeletonName = customName+"."+customName SkeletonLoc = os.path.join(asset.folder_name, SkeletonName) diff --git a/blender-for-unrealengine/bfu_write_text.py b/blender-for-unrealengine/bfu_write_text.py index 9f785652..6fc453f5 100644 --- a/blender-for-unrealengine/bfu_write_text.py +++ b/blender-for-unrealengine/bfu_write_text.py @@ -150,9 +150,15 @@ def WriteExportLog(): return ExportLog -def WriteCameraAnimationTracks(obj): +def WriteCameraAnimationTracks(obj, target_frame_start=None, target_frame_end=None): # Write as json file + scene = bpy.context.scene + if target_frame_start is None: + target_frame_start = scene.frame_start + if target_frame_end is None: + target_frame_end = scene.frame_end+1 + def getCameraFocusDistance(Camera, Target): transA = Camera.matrix_world.copy() transB = Target.matrix_world.copy() @@ -166,7 +172,7 @@ def getAllCamDistKeys(Camera, Target): scene = bpy.context.scene saveFrame = scene.frame_current # Save current frame keys = [] - for frame in range(scene.frame_start, scene.frame_end+1): + for frame in range(target_frame_start, target_frame_end): scene.frame_set(frame) v = getCameraFocusDistance(Camera, Target) keys.append((frame, v)) @@ -177,7 +183,7 @@ def getAllKeysByMatrix(obj): scene = bpy.context.scene saveFrame = scene.frame_current # Save current frame keys = [] - for frame in range(scene.frame_start, scene.frame_end+1): + for frame in range(target_frame_start, target_frame_end): scene.frame_set(frame) v = obj.matrix_world*1 keys.append((frame, v)) @@ -214,17 +220,19 @@ def getAllKeysByFcurves(obj, DataPath, DataValue, IsData=True): f = obj.animation_data.action.fcurves.find(DataPath) if f is not None: - for frame in range(scene.frame_start, scene.frame_end+1): + for frame in range(target_frame_start, target_frame_end): v = f.evaluate(frame) keys.append((frame, v)) return keys - return[(scene.frame_start, DataValue)] + return[(target_frame_start, DataValue)] class CameraDataAtFrame(): def __init__(self): scene = bpy.context.scene self.transform_track = {} + self.fov = {} + self.angle = {} self.lens = {} self.sensor_width = {} self.sensor_height = {} @@ -264,10 +272,12 @@ def EvaluateTracksAtFrame(self, camera, frame): transform["scale_z"] = array_scale.z self.transform_track[frame] = transform - # Get FocalLength SensorWidth SensorHeight + # Get FOV FocalLength SensorWidth SensorHeight + self.angle[frame] = getOneKeysByFcurves(camera, "angle", camera.data.angle, frame) self.lens[frame] = getOneKeysByFcurves(camera, "lens", camera.data.lens, frame) self.sensor_width[frame] = getOneKeysByFcurves(camera, "sensor_width", camera.data.sensor_width, frame) self.sensor_height[frame] = getOneKeysByFcurves(camera, "sensor_height", camera.data.sensor_height, frame) + self.fov[frame] = math.degrees(self.angle[frame]) # Get FocusDistance scale_length = bpy.context.scene.unit_settings.scale_length @@ -327,16 +337,25 @@ def EvaluateTracks(self, camera, frame_start, frame_end): '3/3': ti('write_text_additional_track_end'), } + data["resolution_x"] = scene.render.resolution_x + data["resolution_y"] = scene.render.resolution_y + data["desired_screen_ratio"] = scene.render.resolution_x / scene.render.resolution_y + data["frame_start"] = target_frame_start + data["frame_end"] = target_frame_end + + # Frames is old, need to update and remove. data['Frames'] = [] data['Frames'].append({ - 'frame_start': scene.frame_start, - 'frame_end': scene.frame_end, + 'frame_start': target_frame_start, + 'frame_end': target_frame_end, }) camera_tracks = CameraDataAtFrame() - camera_tracks.EvaluateTracks(obj, scene.frame_start, scene.frame_end) + camera_tracks.EvaluateTracks(obj, target_frame_start, target_frame_end) data['Camera transform'] = camera_tracks.transform_track + data["Camera FieldOfView"] = camera_tracks.fov + data["Camera FocalAngle"] = camera_tracks.angle data['Camera FocalLength'] = camera_tracks.lens data['Camera SensorWidth'] = camera_tracks.sensor_width data['Camera SensorHeight'] = camera_tracks.sensor_height diff --git a/blender-for-unrealengine/export/bfu_export_single_fbx_action.py b/blender-for-unrealengine/export/bfu_export_single_fbx_action.py index 483a38a1..bf7c3941 100644 --- a/blender-for-unrealengine/export/bfu_export_single_fbx_action.py +++ b/blender-for-unrealengine/export/bfu_export_single_fbx_action.py @@ -95,9 +95,12 @@ def ExportSingleFbxAction( SelectParentAndDesiredChilds(obj) asset_name = PrepareExportName(obj, True) - duplicate_data = DuplicateSelectForExport() - SetDuplicateNameForExport(duplicate_data) - MakeSelectVisualReal() + if export_as_proxy is False: + duplicate_data = DuplicateSelectForExport() + SetDuplicateNameForExport(duplicate_data) + + if export_as_proxy is False: + MakeSelectVisualReal() BaseTransform = obj.matrix_world.copy() active = bpy.context.view_layer.objects.active @@ -214,11 +217,12 @@ def ExportSingleFbxAction( bpy.context.scene.unit_settings.scale_length = savedUnitLength RescaleAllActionCurve(1/(rrf*oldScale), 0.01/savedUnitLength) - CleanDeleteObjects(bpy.context.selected_objects) - for data in duplicate_data.data_to_remove: - data.RemoveData() + if export_as_proxy is False: + CleanDeleteObjects(bpy.context.selected_objects) + for data in duplicate_data.data_to_remove: + data.RemoveData() - ResetDuplicateNameAfterExport(duplicate_data) + ResetDuplicateNameAfterExport(duplicate_data) for obj in scene.objects: ClearAllBFUTempVars(obj) diff --git a/blender-for-unrealengine/export/bfu_export_single_fbx_nla_anim.py b/blender-for-unrealengine/export/bfu_export_single_fbx_nla_anim.py index 77b47f8e..feb9bffb 100644 --- a/blender-for-unrealengine/export/bfu_export_single_fbx_nla_anim.py +++ b/blender-for-unrealengine/export/bfu_export_single_fbx_nla_anim.py @@ -86,13 +86,17 @@ def ExportSingleFbxNLAAnim( export_as_proxy = GetExportAsProxy(obj) export_proxy_child = GetExportProxyChild(obj) + SafeModeSet('OBJECT') SelectParentAndDesiredChilds(obj) asset_name = PrepareExportName(obj, True) - duplicate_data = DuplicateSelectForExport() - SetDuplicateNameForExport(duplicate_data) - MakeSelectVisualReal() + if export_as_proxy is False: + duplicate_data = DuplicateSelectForExport() + SetDuplicateNameForExport(duplicate_data) + + if export_as_proxy is False: + MakeSelectVisualReal() BaseTransform = obj.matrix_world.copy() active = bpy.context.view_layer.objects.active @@ -103,6 +107,8 @@ def ExportSingleFbxNLAAnim( animation_data = AnimationManagment() animation_data.SaveAnimationData(obj) animation_data.SetAnimationData(active, True) + + if export_as_proxy: ApplyProxyData(active) @@ -191,11 +197,12 @@ def ExportSingleFbxNLAAnim( bpy.context.scene.unit_settings.scale_length = savedUnitLength RescaleAllActionCurve(1/(rrf*oldScale), 0.01/savedUnitLength) - CleanDeleteObjects(bpy.context.selected_objects) - for data in duplicate_data.data_to_remove: - data.RemoveData() + if export_as_proxy is False: + CleanDeleteObjects(bpy.context.selected_objects) + for data in duplicate_data.data_to_remove: + data.RemoveData() - ResetDuplicateNameAfterExport(duplicate_data) + ResetDuplicateNameAfterExport(duplicate_data) for obj in scene.objects: ClearAllBFUTempVars(obj) \ No newline at end of file diff --git a/blender-for-unrealengine/export/bfu_export_utils.py b/blender-for-unrealengine/export/bfu_export_utils.py index f97f0d31..0f15f6be 100644 --- a/blender-for-unrealengine/export/bfu_export_utils.py +++ b/blender-for-unrealengine/export/bfu_export_utils.py @@ -368,7 +368,8 @@ def SetVertexColorForUnrealExport(parent): obj.data.vertex_colors.active_index = vced.index new_vertex_color = obj.data.vertex_colors.new() - new_vertex_color.name = "BFU_VertexColorExportName" + if new_vertex_color: + new_vertex_color.name = "BFU_VertexColorExportName" number = len(obj.data.vertex_colors) - 1 for i in range(number): diff --git a/docs/ExportAssetDocCopyCamera.png b/docs/ExportAssetDocCopyCamera.png new file mode 100644 index 00000000..0fae504e Binary files /dev/null and b/docs/ExportAssetDocCopyCamera.png differ