Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 19 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

<img alt="Blender Eevee render" src="docs/blender.jpg" width="300">
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

<img alt="UE4 render" src="docs/unreal.jpg" width="300">

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
5 changes: 1 addition & 4 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": "",
Expand All @@ -13,7 +13,6 @@
"wiki_url": "https://github.com/0xafbf/blender-datasmith-export",
}


if "bpy" in locals():
import importlib
if "export_datasmith" in locals():
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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()
60 changes: 35 additions & 25 deletions export_datasmith.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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("<", "&lt;")
out_value = out_value.replace(">", "&gt;")
out_value = out_value.replace('"', "&quot;")
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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2470,4 +2481,3 @@ def save(context, *, filepath, **kwargs):
log.removeHandler(handler)

return {'FINISHED'}

3 changes: 3 additions & 0 deletions todo.md
Original file line number Diff line number Diff line change
@@ -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