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
2 changes: 1 addition & 1 deletion src/addons/send2ue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
bl_info = {
"name": "Send to Unreal",
"author": "Epic Games Inc (now a community fork)",
"version": (2, 6, 8),
"version": (2, 6, 9),
"blender": (3, 6, 0),
"location": "Header > Pipeline > Send to Unreal",
"description": "Sends an asset to the first open Unreal Editor instance on your machine.",
Expand Down
74 changes: 64 additions & 10 deletions src/addons/send2ue/core/io/fbx_b4.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,9 @@ def fbx_data_object_elements(root, ob_obj, scene_data):
if bpy.context.scene.send2ue.use_object_origin:
loc = Vector((0, 0, 0))

elif ob_obj.type == 'Ellipsis':
loc = Vector((loc[0] * SCALE_FACTOR, loc[1] * SCALE_FACTOR, loc[2] * SCALE_FACTOR))
elif ob_obj.type == 'EMPTY':
if bpy.context.scene.send2ue.use_object_origin:
loc = Vector((0, 0, 0))
elif ob_obj.type == 'MESH':
# centers mesh object by their object origin
if bpy.context.scene.send2ue.use_object_origin:
Expand All @@ -486,13 +487,66 @@ def fbx_data_object_elements(root, ob_obj, scene_data):
# https://github.com/EpicGamesExt/BlenderTools/issues/627
empty_object_name = asset_data.get('empty_object_name')
if empty_object_name:
empty_object = bpy.data.objects.get(empty_object_name)
empty_world_location = empty_object.matrix_world.to_translation()
loc = Vector((
(object_world_location[0] - empty_world_location[0]) * SCALE_FACTOR,
(object_world_location[1] - empty_world_location[1]) * SCALE_FACTOR,
(object_world_location[2] - empty_world_location[2]) * SCALE_FACTOR
))
# Check if this is a collision mesh (UBX_, UCP_, USP_, UCX_)
_is_collision = any(
current_object.name.startswith(token + '_')
for token in ('UBX', 'UCP', 'USP', 'UCX')
)
if _is_collision:
import math
_gm = scene_data.settings.global_matrix
empty_object = bpy.data.objects.get(empty_object_name)
_parent_is_empty = (
ob_obj.parent and ob_obj.parent.name == empty_object_name
)
if ob_obj.parent and not _parent_is_empty:
# Child collision (parented to visual mesh, grandchild
# of EMPTY): fbx_object_tx is contaminated because
# fbx_object_matrix's parent correction (lines 1790-1796
# in fbx_utils.py) uses matrix_local which contains
# EMPTY_world^-1 via matrix_parent_inverse. The EMPTY's
# non-uniform display scale leaks into loc/rot/scale.
# Compute clean local transform using matrix_world
# (where EMPTY's influence is fully cancelled) via
# global_space=True which takes the clean is_global path.
_desired_fbx = ob_obj.fbx_object_matrix(scene_data, global_space=True)
_par_world = ob_obj.parent.fbx_object_matrix(
scene_data, global_space=True
)
_local = _par_world.inverted_safe() @ _desired_fbx
_l, _r, _s = _local.decompose()
loc = Vector(_l)
rot = tuple(math.degrees(a) for a in _r.to_euler('XYZ'))
scale = Vector(_s)
else:
# Root collision (parented to EMPTY or no parent):
# fbx_object_tx IS clean here because the EMPTY is not
# in the export set, so fbx_object_matrix takes the
# is_global path which uses matrix_world (clean).
# Just adjust loc to be EMPTY-relative.
_empty_fbx_pos = _gm @ empty_object.matrix_world.to_translation()
loc = Vector((
loc[0] - _empty_fbx_pos[0],
loc[1] - _empty_fbx_pos[1],
loc[2] - _empty_fbx_pos[2]
))
# scale from fbx_object_tx already includes
# the unit conversion (100x from global_matrix)
# for root nodes. No additional factor needed.
else:
# Non-collision visual meshes: root FBX nodes with
# correct fbx_object_tx world transforms. Adjust loc
# to center the combined mesh around the EMPTY's
# position (ignoring EMPTY rot/scale which are for
# display only).
empty_object = bpy.data.objects.get(empty_object_name)
_gm = scene_data.settings.global_matrix
_empty_fbx_pos = _gm @ empty_object.matrix_world.to_translation()
loc = Vector((
loc[0] - _empty_fbx_pos[0],
loc[1] - _empty_fbx_pos[1],
loc[2] - _empty_fbx_pos[2]
))
else:
asset_world_location = asset_object.matrix_world.to_translation()
loc = Vector((
Expand Down Expand Up @@ -646,4 +700,4 @@ def fbx_data_bindpose_element(root, me_obj, me, scene_data, arm_obj=None, mat_wo
export_fbx_bin.fbx_animations_do = original_fbx_animations_do
export_fbx_bin.fbx_data_armature_elements = original_fbx_data_armature_elements
export_fbx_bin.fbx_data_object_elements = original_fbx_data_object_elements
export_fbx_bin.fbx_data_bindpose_element = original_fbx_data_bindpose_element
export_fbx_bin.fbx_data_bindpose_element = original_fbx_data_bindpose_element
10 changes: 8 additions & 2 deletions src/addons/send2ue/core/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -1202,20 +1202,26 @@ def report_path_error_message(layout, send2ue_property, report_text):

def select_all_children(scene_object, object_type, exclude_postfix_tokens=False):
"""
Selects all of an objects children.
Selects all of an objects children that are in the Export collection hierarchy.

:param object scene_object: A object.
:param str object_type: The type of object to select.
:param bool exclude_postfix_tokens: Whether or not to exclude objects that have a postfix token.
"""
export_collection = bpy.data.collections.get(ToolInfo.EXPORT_COLLECTION.value)
export_objects = set(export_collection.all_objects) if export_collection else set()
children = scene_object.children or get_meshes_using_armature_modifier(scene_object)
for child_object in children:
if child_object.type == object_type:
if exclude_postfix_tokens:
if any(child_object.name.startswith(f'{token.value}_') for token in PreFixToken):
continue

child_object.select_set(True)
if child_object not in export_objects:
continue

if child_object.name in bpy.context.view_layer.objects:
child_object.select_set(True)
if child_object.children:
select_all_children(child_object, object_type, exclude_postfix_tokens)

Expand Down
11 changes: 10 additions & 1 deletion src/addons/send2ue/release_notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## Bug Fixes
* Fixed combined mesh collision transforms not matching visual mesh positions
* [188](https://github.com/poly-hammer/BlenderTools/pull/188)
* Fixed combined mesh visual mesh transforms when using object origin centering
* Fixed distortion of rotated instanced meshes when parent EMPTY has display transforms
* Added EMPTY type check to prevent non-EMPTY parents from entering combined mesh path
* Fixed crash when child objects exist outside the current View Layer
* Fixed non-export-collection children being included in combined mesh exports

## Patch Changes
* Lowered aggressiveness of socket names
* [173](https://github.com/poly-hammer/BlenderTools/issues/173)
Expand All @@ -6,5 +15,5 @@
@Daerst

## Tests Passing On
* Blender `3.6`, `4.2` (installed from blender.org)
* Blender `3.6`, `4.2`, `5.0` (installed from blender.org)
* Unreal `5.3`, `5.4`