Skip to content

Commit

Permalink
Merge pull request #182 from SAM-tak/master
Browse files Browse the repository at this point in the history
Update fbxio with Blender 4.0's one
  • Loading branch information
xavier150 authored Nov 23, 2023
2 parents d41415b + 6d3b3c5 commit c1a3b33
Show file tree
Hide file tree
Showing 7 changed files with 851 additions and 353 deletions.
48 changes: 33 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
# Blender For Unreal Engine
This Add-on allows you to export content created with Blender to Unreal Engine. StaticMeshs, SkeletalMeshs, Animations (NLA and Actions), Collisions and Sockets, Alembic animations, Camera and sequencer [...]

This Add-on allows you to export content created with Blender to Unreal Engine. StaticMeshs, SkeletalMeshs, Animations (NLA and Actions), Collisions and Sockets, Alembic animations, Camera and sequencer...

It works with Blender 3.5 and older to 2.8 and work for UE4, UE5!

Discord:
## Discord

If you need help or you want see my sides project you can join the discord!
-> https://discord.gg/XuYeGCFtxa

# How it works
- [BleuRaven side projects](https://discord.gg/XuYeGCFtxa)

## How it works

Working on object packs for Unreal Engine can be tedious with Blender. That's why I created the Add-on: "Blender for UnrealEngine". it simplified the export process from Blender to Unreal Engine by allowing you to export all the assets of a scene at the same time. It even automatically distributes them in a proper tree structure in correlation with the Unreal Engine pipeline!

Sockets and collision shapes are created directly in Blender, you can precisely choose which animations need to be exported. Blender For Unreal Engine also includes a error checker to prevent the all potential problems. You have an explanation of the problem and how to solve it. In addition to the possibility in certain cases of correcting the problem automatically.

By using Blender For Unreal Engine you can generate python scripts to import the all exported assets directly in Unreal Engine. That work too for the cameras in a Level Sequence with the all cuts and camera ratio.

# Wiki
## Wiki

- [Home](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki)
### Quick Start

## Quick Start

- [Download And Installation](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Download-And-Installation)
- [Blender For Unreal Engine - Quick Start](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Blender-For-Unreal-Engine-Quick-Start)
### Videos
- How Import Blender assets to Unreal Engine -> https://youtu.be/2ehb2Ih3Nbg
- How Import Blender camera to Unreal Sequencer -> https://youtu.be/Xx_9MQu2EkM
- Old Teaser -> https://youtu.be/YLOZZIlhgaM
### Overview Pages

## Videos

- [How Import Blender assets to Unreal Engine](https://youtu.be/2ehb2Ih3Nbg)
- [How Import Blender camera to Unreal Sequencer](https://youtu.be/Xx_9MQu2EkM)
- [Old Teaser](https://youtu.be/YLOZZIlhgaM)

## Overview Pages

- [Transform And Pivot Point](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Transform-And-Pivot-Point)
- [Level of details](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Level-of-details)
- [Collisions](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Collisions)
Expand All @@ -37,13 +49,19 @@ By using Blender For Unreal Engine you can generate python scripts to import the
- [Custom Properties Animation to Curves UE](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Custom-Properties-Animation-to-Curves-UE)
- [Export Collection like a StaticMesh](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Export-collection-like-a-StaticMesh)
- [Import Exported Assets to UE](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/How-import-assets)
### Skeleton and Skeletal Meshs

## Skeleton and Skeletal Meshs

- [Modular Skeletal Meshs](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Modular-skeletal-mesh)
- [Skeleton & Root bone](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Skeleton-&-Root-bone)
- [UE Bone Structure and UE Mannequin](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UE-Bone-Structure-and-UE-Mannequin)
### Community videos
- TRACK THIS! - Camera Tracking From Blender To Unreal Engine -> https://youtu.be/Ta0am2wC-SI?si=2TNyqYHmnDpf20u8
### Additional Information

## Community videos

- TRACK THIS! - [Camera Tracking From Blender To Unreal Engine](https://youtu.be/Ta0am2wC-SI?si=2TNyqYHmnDpf20u8)

## Additional Information

- [API](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/API)
- [Nomenclature](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Nomenclature)
- [Avoid potential errors](https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/How-avoid-potential-errors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,15 @@ class BFU_PT_BlenderForUnrealObject(bpy.types.Panel):
default='X',
)

bpy.types.Object.bfu_export_animation_without_mesh = BoolProperty(
name="Export animation without mesh",
description=(
"If checked, When exporting animation, do not include mesh data in the FBX file."
),
override={'LIBRARY_OVERRIDABLE'},
default=True
)

bpy.types.Object.bfu_mirror_symmetry_right_side_bones = BoolProperty(
name="Revert direction of symmetry right side bones",
description=(
Expand Down Expand Up @@ -1524,10 +1533,13 @@ def draw(self, context):
unreal_skeleton.prop(obj, "bfu_target_skeleton_custom_name")
if obj.bfu_skeleton_search_mode == "custom_reference":
unreal_skeleton.prop(obj, "bfu_target_skeleton_custom_ref")
unreal_skeleton.prop(obj, "bfu_mirror_symmetry_right_side_bones")
MirrorSymmetryRightSideBonesRow = unreal_skeleton.row()
MirrorSymmetryRightSideBonesRow.enabled = obj.bfu_mirror_symmetry_right_side_bones
MirrorSymmetryRightSideBonesRow.prop(obj, "bfu_use_ue_mannequin_bone_alignment")
ue_standard_skeleton = layout.column()
ue_standard_skeleton.enabled = obj.bfu_export_procedure == "ue-standard"
ue_standard_skeleton.prop(obj, "bfu_export_animation_without_mesh")
ue_standard_skeleton.prop(obj, "bfu_mirror_symmetry_right_side_bones")
mirror_symmetry_right_side_bones = ue_standard_skeleton.row()
mirror_symmetry_right_side_bones.enabled = obj.bfu_mirror_symmetry_right_side_bones
mirror_symmetry_right_side_bones.prop(obj, "bfu_use_ue_mannequin_bone_alignment")

scene.bfu_modular_skeletal_mesh_properties_expanded.draw(layout)
if scene.bfu_modular_skeletal_mesh_properties_expanded.is_expend():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def ExportSingleFbxAction(
filepath=bfu_export_utils.GetExportFullpath(dirpath, filename),
check_existing=False,
use_selection=True,
animation_only=True,
animation_only=active.bfu_export_animation_without_mesh,
global_matrix=axis_conversion(to_forward=active.bfu_export_axis_forward, to_up=active.bfu_export_axis_up).to_4x4(),
apply_unit_scale=True,
global_scale=bfu_utils.GetObjExportScale(active),
Expand Down
9 changes: 5 additions & 4 deletions blender-for-unrealengine/fbxio/data_types.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# SPDX-FileCopyrightText: 2006-2012 assimp team
# SPDX-FileCopyrightText: 2013 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later

# Script copyright (C) 2006-2012, assimp team
# Script copyright (C) 2013 Blender Foundation

BOOL = b'C'[0]
BOOL = b'B'[0]
CHAR = b'C'[0]
INT8 = b'Z'[0]
INT16 = b'Y'[0]
INT32 = b'I'[0]
Expand Down
55 changes: 43 additions & 12 deletions blender-for-unrealengine/fbxio/encode_bin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2013 Campbell Barton
#
# SPDX-License-Identifier: GPL-2.0-or-later

# Script copyright (C) 2013 Campbell Barton

try:
from . import data_types
except:
Expand All @@ -12,8 +12,10 @@
import numpy as np
import zlib

_BLOCK_SENTINEL_LENGTH = 13
_BLOCK_SENTINEL_DATA = (b'\0' * _BLOCK_SENTINEL_LENGTH)
_BLOCK_SENTINEL_LENGTH = ...
_BLOCK_SENTINEL_DATA = ...
_ELEM_META_FORMAT = ...
_ELEM_META_SIZE = ...
_IS_BIG_ENDIAN = (__import__("sys").byteorder != 'little')
_HEAD_MAGIC = b'Kaydara FBX Binary\x20\x20\x00\x1a\x00'

Expand Down Expand Up @@ -56,6 +58,14 @@ def add_bool(self, data):
self.props_type.append(data_types.BOOL)
self.props.append(data)

def add_char(self, data):
assert(isinstance(data, bytes))
assert(len(data) == 1)
data = pack('<c', data)

self.props_type.append(data_types.CHAR)
self.props.append(data)

def add_int8(self, data):
assert(isinstance(data, int))
data = pack('<b', data)
Expand Down Expand Up @@ -219,7 +229,7 @@ def _calc_offsets(self, offset, is_last):
assert(self._end_offset == -1)
assert(self._props_length == -1)

offset += 12 # 3 uints
offset += _ELEM_META_SIZE # 3 uints (or 3 ulonglongs for FBX 7500 and later)
offset += 1 + len(self.id) # len + idname

props_length = 0
Expand All @@ -240,17 +250,16 @@ def _calc_offsets_children(self, offset, is_last):
for elem in self.elems:
offset = elem._calc_offsets(offset, (elem is elem_last))
offset += _BLOCK_SENTINEL_LENGTH
elif not self.props or self.id in _ELEMS_ID_ALWAYS_BLOCK_SENTINEL:
if not is_last:
offset += _BLOCK_SENTINEL_LENGTH
elif (not self.props and not is_last) or self.id in _ELEMS_ID_ALWAYS_BLOCK_SENTINEL:
offset += _BLOCK_SENTINEL_LENGTH

return offset

def _write(self, write, tell, is_last):
assert(self._end_offset != -1)
assert(self._props_length != -1)

write(pack('<3I', self._end_offset, len(self.props), self._props_length))
write(pack(_ELEM_META_FORMAT, self._end_offset, len(self.props), self._props_length))

write(bytes((len(self.id),)))
write(self.id)
Expand All @@ -272,9 +281,8 @@ def _write_children(self, write, tell, is_last):
assert(elem.id != b'')
elem._write(write, tell, (elem is elem_last))
write(_BLOCK_SENTINEL_DATA)
elif not self.props or self.id in _ELEMS_ID_ALWAYS_BLOCK_SENTINEL:
if not is_last:
write(_BLOCK_SENTINEL_DATA)
elif (not self.props and not is_last) or self.id in _ELEMS_ID_ALWAYS_BLOCK_SENTINEL:
write(_BLOCK_SENTINEL_DATA)


def _write_timedate_hack(elem_root):
Expand Down Expand Up @@ -308,13 +316,36 @@ def _write_timedate_hack(elem_root):
print("Missing fields!")


# FBX 7500 (aka FBX2016) introduces incompatible changes at binary level:
# * The NULL block marking end of nested stuff switches from 13 bytes long to 25 bytes long.
# * The FBX element metadata (end_offset, prop_count and prop_length) switch from uint32 to uint64.
def init_version(fbx_version):
global _BLOCK_SENTINEL_LENGTH, _BLOCK_SENTINEL_DATA, _ELEM_META_FORMAT, _ELEM_META_SIZE

_BLOCK_SENTINEL_LENGTH = ...
_BLOCK_SENTINEL_DATA = ...
_ELEM_META_FORMAT = ...
_ELEM_META_SIZE = ...

if fbx_version < 7500:
_ELEM_META_FORMAT = '<3I'
_ELEM_META_SIZE = 12
else:
_ELEM_META_FORMAT = '<3Q'
_ELEM_META_SIZE = 24
_BLOCK_SENTINEL_LENGTH = _ELEM_META_SIZE + 1
_BLOCK_SENTINEL_DATA = (b'\0' * _BLOCK_SENTINEL_LENGTH)


def write(fn, elem_root, version):
assert(elem_root.id == b'')

with open(fn, 'wb') as f:
write = f.write
tell = f.tell

init_version(version)

write(_HEAD_MAGIC)
write(pack('<I', version))

Expand Down
Loading

0 comments on commit c1a3b33

Please sign in to comment.