Skip to content

Commit e78cc59

Browse files
committed
removed unmantained functionality, did some cleanup
1 parent ff6f7cc commit e78cc59

5 files changed

Lines changed: 38 additions & 441 deletions

File tree

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
1-
# Blender Datasmith addon
1+
# Blender Datasmith addon
2+
3+
This addon lets you export your blender scene to UE4 using the Datasmith
4+
format.
5+
6+
You can see a brief overview of how it works here:
7+
https://youtu.be/bUUDqerdqAc
8+
9+
To install, get the latest release and install from the blender addons
10+
preferences pane.
11+
12+
This is a work in progress, if you want to contribute to improve it, feel free
13+
to submit pull requests or support me on Patreon:
14+
https://www.patreon.com/0xafbf
15+
16+
This is an unofficial exporter. This is not supported by Epic Games. That being
17+
said, you can still discuss about it and give feedback in the Unreal Engine
18+
forums.
19+

io_scene_udatasmith/__init__.py

Lines changed: 3 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -59,147 +59,14 @@
5959
axis_conversion,
6060
)
6161

62-
63-
b_major, b_minor, b_patch = bpy.app.version
64-
65-
class ImportDatasmith(bpy.types.Operator, ImportHelper):
66-
"""Load a Datasmith file"""
67-
bl_idname = "import_scene.udatasmith"
68-
bl_label = "Import Datasmith"
69-
bl_options = {'PRESET', 'UNDO'}
70-
71-
filename_ext = ".udatasmith"
72-
filter_glob = StringProperty(
73-
default="*.udatasmith",
74-
options={'HIDDEN'}
75-
)
76-
77-
use_smooth_groups = BoolProperty(
78-
name="Smooth Groups",
79-
description="Surround smooth groups by sharp edges",
80-
default=True,
81-
)
82-
83-
def execute(self, context):
84-
keywords = self.as_keywords(ignore=("filter_glob",))
85-
from . import import_datasmith
86-
return import_datasmith.load(self, context, **keywords)
87-
88-
def draw(self, context):
89-
layout = self.layout
90-
91-
layout.prop(self, 'use_smooth_groups')
92-
93-
9462
class ExportDatasmith(bpy.types.Operator, ExportHelper):
9563
"""Write a Datasmith file"""
9664
bl_idname = "export_scene.datasmith"
9765
bl_label = "Export Datasmith"
9866
bl_options = {'UNDO', 'PRESET'}
9967

10068
filename_ext = ".udatasmith"
101-
filter_glob = StringProperty(default="*.udatasmith", options={'HIDDEN'})
102-
103-
# List of operator properties, the attributes will be assigned
104-
# to the class instance from the operator settings before calling.
105-
106-
version = EnumProperty(
107-
items=(('UD_420', "Datasmith for UE 4.20", "Datasmith for Unreal Engine 4.20"),
108-
('UD_421', "Datasmith for UE 4.21", "Datasmith for Unreal Engine 4.21"),
109-
),
110-
name="Version",
111-
description="Choose which version of the exporter to use",
112-
)
113-
114-
use_selection = BoolProperty(
115-
name="Selected Objects",
116-
description="Export selected objects on visible layers",
117-
default=False,
118-
)
119-
global_scale = FloatProperty(
120-
name="Scale",
121-
description="Scale all data (Some importers do not support scaled armatures!)",
122-
min=0.001, max=1000.0,
123-
soft_min=0.01, soft_max=1000.0,
124-
default=1.0,
125-
)
126-
127-
# TODO remove, left as doc for a multiple choice enum
128-
'''object_types = EnumProperty(
129-
name="Object Types",
130-
options={'ENUM_FLAG'},
131-
items=(('EMPTY', "Empty", ""),
132-
('CAMERA', "Camera", ""),
133-
('LIGHT', "Lamp", ""),
134-
('ARMATURE', "Armature", "WARNING: not supported in dupli/group instances"),
135-
('MESH', "Mesh", ""),
136-
('OTHER', "Other", "Other geometry types, like curve, metaball, etc. (converted to meshes)"),
137-
),
138-
description="Which kind of object to export",
139-
default={'EMPTY', 'CAMERA', 'LIGHT', 'ARMATURE', 'MESH', 'OTHER'},
140-
)'''
141-
142-
# TODO remove: some of these are for fbx, maybe are useful for datasmith
143-
'''
144-
use_mesh_modifiers = BoolProperty(
145-
name="Apply Modifiers",
146-
description="Apply modifiers to mesh objects (except Armature ones) - "
147-
"WARNING: prevents exporting shape keys",
148-
default=True,
149-
)
150-
use_mesh_modifiers_render = BoolProperty(
151-
name="Use Modifiers Render Setting",
152-
description="Use render settings when applying modifiers to mesh objects",
153-
default=True,
154-
)
155-
mesh_smooth_type = EnumProperty(
156-
name="Smoothing",
157-
items=(('OFF', "Normals Only", "Export only normals instead of writing edge or face smoothing data"),
158-
('FACE', "Face", "Write face smoothing"),
159-
('EDGE', "Edge", "Write edge smoothing"),
160-
),
161-
description="Export smoothing information "
162-
"(prefer 'Normals Only' option if your target importer understand split normals)",
163-
default='OFF',
164-
)
165-
use_mesh_edges = BoolProperty(
166-
name="Loose Edges",
167-
description="Export loose edges (as two-vertices polygons)",
168-
default=False,
169-
)
170-
# 7.4 only
171-
use_tspace = BoolProperty(
172-
name="Tangent Space",
173-
description="Add binormal and tangent vectors, together with normal they form the tangent space "
174-
"(will only work correctly with tris/quads only meshes!)",
175-
default=False,
176-
)
177-
# 7.4 only'''
178-
179-
180-
include_metadata = BoolProperty(
181-
name="Include Metadata",
182-
description="Include meshes metadata in file",
183-
default=False,
184-
)
185-
186-
187-
#I don't know what this does
188-
path_mode = path_reference_mode
189-
# 7.4 only
190-
embed_textures = BoolProperty(
191-
name="Embed Textures",
192-
description="Embed textures in Datasmith binary file (only for \"Copy\" path mode!)",
193-
default=False,
194-
)
195-
batch_mode = EnumProperty(
196-
name="Batch Mode",
197-
items=(('OFF', "Off", "Active scene to file"),
198-
('SCENE', "Scene", "Each scene as a file"),
199-
('GROUP', "Group", "Each group as a file"),
200-
),
201-
)
202-
69+
filter_glob: StringProperty(default="*.udatasmith", options={'HIDDEN'})
20370

20471
def draw(self, context):
20572
layout = self.layout
@@ -212,11 +79,6 @@ def draw(self, context):
21279
layout.prop(self, "batch_mode")
21380

21481

215-
216-
@property
217-
def check_extension(self):
218-
return self.batch_mode == 'OFF'
219-
22082
def execute(self, context):
22183
keywords = self.as_keywords(ignore=("filter_glob",))
22284
from . import export_datasmith
@@ -225,35 +87,20 @@ def execute(self, context):
22587
def menu_func_export(self, context):
22688
self.layout.operator(ExportDatasmith.bl_idname, text="Datasmith (.udatasmith)")
22789

228-
def menu_func_import(self, context):
229-
self.layout.operator(ImportDatasmith.bl_idname, text="Datasmith (.udatasmith)")
230-
23190
classes = (
23291
ExportDatasmith,
233-
ImportDatasmith,
23492
)
23593

23694

23795
def register():
23896
for cls in classes:
23997
bpy.utils.register_class(cls)
24098

241-
if b_minor >= 80:
242-
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
243-
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
244-
else:
245-
bpy.types.INFO_MT_file_export.append(menu_func_export)
246-
bpy.types.INFO_MT_file_import.append(menu_func_import)
99+
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
247100

248101

249102
def unregister():
250-
if b_minor >= 80:
251-
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
252-
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
253-
else:
254-
bpy.types.INFO_MT_file_export.remove(menu_func_export)
255-
bpy.types.INFO_MT_file_import.remove(menu_func_import)
256-
103+
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
257104

258105
for cls in classes:
259106
bpy.utils.unregister_class(cls)

io_scene_udatasmith/data_types.py

Lines changed: 7 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ class UDMesh():
120120

121121
def __init__(self, path=None, node:ElementTree.Element = None, name=None):
122122
self.name = name
123-
if node:
124-
self.init_with_xmlnode(node)
125-
elif path:
123+
if path:
126124
self.init_with_path(path)
127125

128126
else:
@@ -153,79 +151,9 @@ def check_fields(self):
153151
assert self.struct_property == 'StructProperty'
154152
assert self.datasmith_mesh_source_model == 'DatasmithMeshSourceModel'
155153

156-
def init_with_xmlnode(self, node:ElementTree.Element):
157-
self.name = node.attrib['name']
158-
self.label = node.attrib['label']
159-
self.relative_path = node.find('file').attrib['path']
160-
161-
parent_path = path.dirname(os.path.abspath(self.parent.path))
162-
self.init_with_path(path.join(parent_path, self.relative_path))
163-
# self.materials = {n.attrib['id']: n.attrib['name'] for n in node.iter('Material')}
164-
165-
# flatten material lists
166-
material_map = {int(n.attrib['id']): idx for idx, n in enumerate(node.iter('Material'))}
167-
self.materials = {idx: n.attrib['name'] for idx, n in enumerate(node.iter('Material'))}
168-
if 0 not in material_map:
169-
last_index = len(material_map)
170-
material_map[0] = last_index
171-
self.materials[last_index] = 'default_material'
172-
173-
self.tris_material_slot = list(map(lambda x: material_map.get(x, 0), self.tris_material_slot))
174-
175-
176-
def init_with_path(self, path):
177-
with open(path, 'rb') as file:
178-
179154

180155
# this may need some work, found some documentation:
181156
# Engine/Source/Developer/Rawmesh
182-
183-
self.a01 = read_data(file, 'II') # a 1 and the whole bytes size
184-
self.name = read_string(file)
185-
186-
self.a02 = file.read(5)
187-
188-
self.source_models = read_string(file)
189-
self.struct_property = read_string(file)
190-
self.a03 = file.read(8)
191-
192-
self.datasmith_mesh_source_model = read_string(file)
193-
194-
self.a04 = file.read(25) # just zeros
195-
196-
self.payload_length = read_data(file, 'II') # this is the size of the rawmesh
197-
198-
self.a04_b = file.read(8) # this is a 125 and a zero, no idea what it is
199-
200-
self.raw_mesh_version = read_data(file, 'i') # always 1 for what I can see
201-
self.raw_mesh_lic_version = read_data(file, 'i') # always 0 for what I can see
202-
203-
self.tris_material_slot = read_array_data(file, "I") #FaceMaterialIndices
204-
self.tris_smoothing_group = read_array_data(file, "I") #FaceSmoothingMasks
205-
206-
self.vertices = read_array_data(file, "fff") #VertexPositions
207-
self.triangles = read_array_data(file, "I") #WedgeIndices
208-
209-
self.a05 = read_array_data(file, "I") # WedgeTangentX (maybe)
210-
self.a06 = read_array_data(file, "I") # WedgeTangentY (maybe)
211-
212-
self.vertex_normals = read_array_data(file, "fff") #WedgeTangentZ
213-
self.uvs = read_array_data(file, "ff") #WedgeTexCoords
214-
215-
self.a07 = file.read(28) # these may be WedgeTexCoods[1,2...7]
216-
# ue4 defines 8 texcoord layers, here we read the other seven zeros
217-
self.a07_b = file.read(4) # these seem to be WedgeColors count
218-
self.a07_c = file.read(4) # MaterialIndexToImportIndex?
219-
220-
self.checksum = file.read(16) # I guess this is Guid?
221-
222-
self.a08 = file.read() # And maybe this is bGuidIsHash
223-
224-
# small check here to crash if something is suspicious
225-
assert len(self.triangles) == len(self.uvs)
226-
assert len(self.vertex_normals) == len(self.uvs)
227-
assert self.a08 == b'\x00\x00\x00\x00' # just to be sure its the end
228-
229157
def write_to_path(self, path):
230158
with open(path, 'wb') as file:
231159
#write_null(file, 8)
@@ -603,12 +531,13 @@ class UDScene():
603531
current_scene = None
604532

605533
def __init__(self, source=None):
606-
self.init_fields()
607-
if type(source) is str:
608-
self.path = source
609-
self.init_with_path(self.path)
534+
self.name = 'udscene_name'
535+
536+
self.materials = {}
537+
self.meshes = {}
538+
self.objects = {}
539+
self.textures = {}
610540

611-
self.check_fields() # to test if it is possible for these fields to have different values
612541

613542
def get_field(self, cls, name, **kwargs):
614543
group = getattr(self, cls.node_group)
@@ -622,52 +551,6 @@ def get_field(self, cls, name, **kwargs):
622551
group[name] = new_object
623552
return new_object
624553

625-
626-
def init_fields(self):
627-
self.name = 'udscene_name'
628-
629-
self.materials = {}
630-
self.meshes = {}
631-
self.objects = {}
632-
self.textures = {}
633-
634-
def check_fields(self):
635-
pass
636-
637-
def init_with_path(self, path):
638-
639-
# unmantained, for importing.
640-
tree = ElementTree.parse(path)
641-
root = tree.getroot()
642-
643-
self.version = root.find('Version').text
644-
self.sdk_version = root.find('SDKVersion').text
645-
self.host = root.find('Host').text
646-
# there are other bunch of data that i'll skip for now
647-
648-
classes = [
649-
UDTexture,
650-
UDMaterial,
651-
UDMasterMaterial,
652-
UDMesh,
653-
UDActor,
654-
UDActorMesh,
655-
UDActorCamera,
656-
UDActorLight,
657-
]
658-
659-
mappings = {cls.node_type:cls for cls in classes}
660-
661-
UDScene.current_scene = self
662-
for node in root:
663-
name = node.get('name') # most relevant nodes have a name as identifier
664-
cls = mappings.get(node.tag)
665-
if cls:
666-
uobj = cls(name=name, node=node)
667-
self.objects.push(uobj)
668-
669-
print("loaded")
670-
671554
def node(self):
672555
n = Node('DatasmithUnrealScene')
673556
n.push(Node('Version', children=['0.20']))

io_scene_udatasmith/export_datasmith.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ def make_field_node(field, name, default_name):
9696
texture.image = image
9797

9898
return node
99+
elif input_node.type == 'HUE_SAT':
100+
return make_field_node(input_node.inputs['Color'], name, default_name)
101+
elif input_node.type == 'MIX_RGB':
102+
factor = input_node.inputs['Fac'].default_value
103+
selected_input = 'Color1' if factor < 0.5 else 'Color2'
104+
return make_field_node(input_node.inputs[selected_input], name, default_name)
105+
elif input_node.type == 'RGB':
106+
value = input_node.outputs[0]
107+
return make_default_node(value, default_name)
99108
else:
100109
log.error("unhandled node")
101110

0 commit comments

Comments
 (0)