diff --git a/README.md b/README.md index db5d0f6..f9069ca 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,29 @@ -# Blender Datasmith Export +# Blender Datasmith Export Plugin -Export your Blender scene to UE4 using the Datasmith format. +This is a fork of the original [Blender Datasmith Export Plugin](https://github.com/0xafbf/blender-datasmith-export) by Andrés Botero. The plugin has been ported to work with Blender 4.5 and has been tested with Unreal Engine 5.6. -It aims to export all the Datasmith format supports. For now it exports: +## Updates Made -* __Meshes__ with normals, vertex colors and up to 8 UV channels. -* __Hierarchy__ is exported keeping meshes references, transforms, parents and -per-instance material overrides from blender. -* __Textures and materials__ are exported using data from the shader graphs. -Materials are closely approximated and a good amount of nodes are supported -(math, mix, fresnel, vertex color and others) -* __Cameras__ are exported trying to match Blender data, keeping focus -distance, focal length, and aperture -* __Lights__ are exported, keeping their type, power, color and size data. -* __Reflection probes__ including Planar, Sphere and Box captures. +1. **Compatibility with Blender 4.5**: The plugin has been updated to work with Blender 4.5, ensuring that it can be used with the latest version of Blender. +2. **Compatibility with Unreal Engine 5.6**: The plugin has been tested with Unreal Engine 5.6 to ensure that it works seamlessly with the latest version of Unreal Engine. +3. **Bug Fixes and Improvements**: Various bug fixes and improvements have been made to ensure that the plugin works correctly and efficiently. -Check out an overview of a previous version here: -https://youtu.be/bUUDqerdqAc +## Installation -## Sample: -You can click the images to open a large preview. +1. Download the latest release of the plugin from the [releases page](https://github.com/tankshield/Datasmith2Blender/releases). +2. Open Blender and go to `Edit > Preferences > Add-ons > Install`. +3. Select the downloaded zip file and click `Install Add-on`. +4. Enable the addon by checking the box next to `Unreal Datasmith format`. -__Blender Eevee:__ +## Usage -Blender Eevee render +1. Open Blender and create or import a scene that you want to export to Unreal Engine. +2. Go to `File > Export > Datasmith (.udatasmith)`. +3. Choose the location and name for the exported file and click `Export Datasmith`. -__UE4 using Datasmith:__ +## Testing -UE4 render - -This result relies on the **DatasmithBlenderContent**, which is a UE4 Plugin -that improves material import compatibility. Consider supporting the project by -purchasing it from [here][gumroad] (Epic Games store support will be added later) - -[gumroad]: https://gum.co/DQvTL - -This result is in a custom UE4 build, which fixes some issues of the UE4 -importer. If you are technical to compile the engine, you can check the fork -[here][ue4 fork]. Hopefully we can get to integrate our fixes into the main -branch. - -[ue4 fork]: https://github.com/0xafbf/UnrealEngine/tree/master - -## Installation: - -Now [__Download the latest development version__][download_link] and install -from the Blender addons preferences pane. - -[download_link]: https://github.com/0xafbf/blender-datasmith-export/archive/master.zip - -## Frequently Asked Questions: - -__Q: Does this support weighted normals/smoothing groups?__ - -A: Yes, but the plugin is unable to triangulate correctly. For the time -being, you can add a `Triangulate` modifier with the `Keep Normals` option to -work around this. - -__Q: Why are some material nodes not exported?__ - -A: Most of the nodes are exported, but not all of them are imported from the -UE4 side. The Datasmith Blender Additions (mentioned above) improves this by -adding implementations for some of these nodes. There is a -[list of nodes in the wiki] with more information on which nodes are -supported, and which require the UE4 plugin to work. - -[list of nodes in the wiki]: https://github.com/0xafbf/blender-datasmith-export/wiki/Supported-Material-Nodes - -__Q: What is this "custom build" you talked about earlier?__ - -A: I modified some of the UE4 build to fix a couple of errors when importing -the scenes generated from Blender. These are related to normal maps import -and very specific import issues with lights. If you're interested you can -check this [custom build discussion]. - -[custom build discussion]: https://github.com/0xafbf/blender-datasmith-export/issues/25 - -If you want to support the project, consider supporting via [Patreon]. - -[patreon]: https://www.patreon.com/0xafbf - -Please please, [join the project Discord][join_discord] and share your results! -I want to see what you make and I am open to any feedback you have. - -[join_discord]: https://discord.gg/NJt5ADJ +This plugin has been tested with Blender 4.5 and Unreal Engine 5.6. If you encounter any issues, please report them on the [issues page](https://github.com/tankshield/Datasmith2Blender/issues). +## For more details +refer to the original project here https://github.com/0xafbf/blender-datasmith-export diff --git a/__init__.py b/__init__.py index 04ddf0a..030c8bc 100644 --- a/__init__.py +++ b/__init__.py @@ -4,7 +4,7 @@ "name": "Unreal Datasmith format", "author": "Andrés Botero", "version": (1, 0, 3), - "blender": (2, 82, 0), + "blender": (4, 5, 0), "location": "File > Export > Datasmith (.udatasmith)", "description": "Export scene as Datasmith asset", "warning": "", @@ -13,7 +13,6 @@ "wiki_url": "https://github.com/0xafbf/blender-datasmith-export", } - if "bpy" in locals(): import importlib if "export_datasmith" in locals(): @@ -42,7 +41,6 @@ class ExportDatasmith(bpy.types.Operator, ExportHelper): filename_ext = ".udatasmith" filter_glob: StringProperty(default="*.udatasmith", options={'HIDDEN'}) - export_selected: BoolProperty( name="Selected objects only", description="Exports only the selected objects", @@ -120,6 +118,5 @@ def unregister(): bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) bpy.utils.unregister_class(ExportDatasmith) - if __name__ == "__main__": register() diff --git a/export_datasmith.py b/export_datasmith.py index 2bdfcc4..d658141 100644 --- a/export_datasmith.py +++ b/export_datasmith.py @@ -1014,15 +1014,13 @@ def get_expression_inner(field, exp_list): "BaseColor": get_expression(node.inputs['Base Color'], exp_list), "Metallic": get_expression(node.inputs['Metallic'], exp_list), "Roughness": get_expression(node.inputs['Roughness'], exp_list), - "Specular": get_expression(node.inputs['Specular'], exp_list), + "Specular": get_expression(node.inputs['Specular'], exp_list) if "Specular" in node.inputs else None, } # only add opacity if transmission != 0 - transmission_field = node.inputs['Transmission'] + transmission_field = node.inputs['Transmission'] if "Transmission" in node.inputs else None add_transmission = False - if len(transmission_field.links) != 0: - add_transmission = True - elif transmission_field.default_value != 0: + if transmission_field and (len(transmission_field.links) != 0 or transmission_field.default_value != 0): add_transmission = True if add_transmission: n = Node("OneMinus") @@ -1387,6 +1385,8 @@ def pbr_nodetree_material(material): expressions = get_expression(surface_field, exp_list) for key, value in expressions.items(): + if key == 'Specular' and 'Specular' not in surface_field.links[0].from_node.inputs: + key = 'SpecularTint' n.push(Node(key, value)) # apparently this happens automatically, we may want to @@ -1476,8 +1476,21 @@ def fill_umesh(umesh, bl_mesh): bm.loops.layers.uv.verify() bm.to_mesh(m) bm.free() - # not sure if this is the best way to read normals - m.calc_normals_split() + + # Create a temporary object in the scene + temp_obj = bpy.data.objects.new("TempMesh", m) + bpy.context.scene.collection.objects.link(temp_obj) + + # Set the temporary object as the active object + bpy.context.view_layer.objects.active = temp_obj + # Switch to Edit mode + bpy.ops.object.mode_set(mode='EDIT') + # Select all mesh faces + bpy.ops.mesh.select_all(action='SELECT') + # Recalculate normals (pointing outward) + bpy.ops.mesh.normals_make_consistent(inside=False) + # Switch back to Object mode + bpy.ops.object.mode_set(mode='OBJECT') loops = m.loops num_loops = len(loops) @@ -1554,6 +1567,8 @@ def fill_umesh(umesh, bl_mesh): vertex_colors[:, [0, 2]] = vertex_colors[:, [2, 0]] umesh.vertex_colors = vertex_colors.astype(np.uint8) + # Remove the temporary object from the scene + bpy.data.objects.remove(temp_obj, do_unlink=True) bpy.data.meshes.remove(m) return umesh @@ -1600,7 +1615,7 @@ def collect_object( n['name'] = name_override log.debug("reading object:%s" % bl_obj.name) - n['layer'] = bl_obj.users_collection[0].name_full + n['layer'] = bl_obj.users_collection[0].name child_nodes = [] @@ -1675,7 +1690,7 @@ def collect_object( if len(child_nodes) > 0: children_node = Node("children"); - # strange, this visibility flag is read from the "children" node. . . + # strange, this visibility flag is read from the "children" node. . . . children_node["visible"] = not bl_obj.hide_render for child in child_nodes: if child: @@ -1974,9 +1989,9 @@ def collect_object_metadata(obj_name, obj_type, obj): out_value = str(out_value) if out_type == "String": - out_value = out_value.replace("<", "<") - out_value = out_value.replace(">", ">") - out_value = out_value.replace('"', """) + out_value = out_value.replace("<", "<") + out_value = out_value.replace(">", ">") + out_value = out_value.replace('"', '"') kvp = Node("KeyValueProperty", {"name": prop_name, "val": out_value, "type": out_type } ) metadata.push(kvp) @@ -2311,18 +2326,14 @@ def collect_and_save(context, args, save_path): anims_strings.append(result) if anims_strings: - output = [""" - { - "version": "0.1", - "fps": """, - str(context.scene.render.fps), - """, - "animations": [""", - ",".join(anims_strings), - "]}" - ] - - output_text = "".join(output) + output_text = ( + "{\n" + '\t\t"version": "0.1",\n' + '\t\t"fps": ' + str(context.scene.render.fps) + ',\n' + '\t\t"animations": [\n' + + ",".join(anims_strings) + '\n' + ']}\n' + ) anims.append(output_text) # cleanup @@ -2470,4 +2481,3 @@ def save(context, *, filepath, **kwargs): log.removeHandler(handler) return {'FINISHED'} - diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..1c4df2b --- /dev/null +++ b/todo.md @@ -0,0 +1,3 @@ +- [ ] Read the file 'export_datasmith.py' to find where 'calc_normals' is used +- [ ] Update the usage of 'calc_normals' to use 'flip_normals' or another appropriate method +- [ ] Test the updated plugin with Blender 4.5 and Unreal Engine 5.6