Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ This add-on bundles and redistributes the following third-party components.

* **OpenRigLogic** (DNA and RigLogic libraries) — © Epic Games, Inc., licensed under the [MIT License](https://github.com/EpicGames/OpenRigLogic/blob/main/LICENSE).
* **Sentry SDK for Python** — © Functional Software, Inc. dba Sentry, licensed under the MIT License.
* **ufbx for Python** — MIT License.

Epic Games, Unreal Engine, Fab, MetaHuman, RigLogic, and OpenRigLogic, and their associated design logos,
are trademarks or registered trademarks of Epic Games, Inc. All other trademarks are the property of
Expand Down
14 changes: 13 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
],
"words": [
"bpy",
"ufbx",
"Rigify",
".npz",
"cytoscape",
Expand Down Expand Up @@ -223,7 +224,18 @@
"svd",
"vmask",
"vidx",
"regularizers"
"regularizers",
"Samuli",
"Raivio",
"pyufbx",
"uuv",
"XZY",
"YXZ",
"YZX",
"ZXY",
"ZYX",
"quats",
"interp"
]
},
{
Expand Down
3 changes: 0 additions & 3 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ Check these in order:

Live RigLogic evaluation doesn't run before each frame when rendering. **Bake** the face board animation to keyframes first so the pose bones, shape keys, and mask values are stored on the timeline. See [Animation](./free-features/animation.md).

!!! note
It is now possible to disable `Batched Evaluations` in the addon preferences. This is currently experimental. Please report any issues with evaluation in the scene or while rendering with this option on.

## Still stuck?

Browse or search existing reports and open a new issue here:
Expand Down
18 changes: 17 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ keywords = ["blender", "metahuman", "dna", "riglogic", "animation", "3d"]
name = "character-dna-addon"
readme = "README.md"
requires-python = ">=3.11,<3.14"
version = "0.11.3"
version = "0.12.2"

[project.optional-dependencies]
dev = [
Expand Down Expand Up @@ -117,6 +117,21 @@ output = "reports/coverage/results.xml"
[tool.hatch.build.targets.wheel]
packages = ["src/addons/character_dna"]

# =============================================================================
# uv Sources
# =============================================================================
# ufbx Python bindings used to read FBX animation. Resolved from the same
# pre-built wheels the extension bundles
[tool.uv.sources]
pyufbx = [
{path = "src/addons/character_dna/wheels/pyufbx-0.0.0-cp311-cp311-win_amd64.whl", marker = "sys_platform == 'win32' and python_full_version >= '3.11' and python_full_version < '3.12'"},
{path = "src/addons/character_dna/wheels/pyufbx-0.0.0-cp313-cp313-win_amd64.whl", marker = "sys_platform == 'win32' and python_full_version >= '3.13' and python_full_version < '3.14'"},
{path = "src/addons/character_dna/wheels/pyufbx-0.0.0-cp311-cp311-macosx_11_0_arm64.whl", marker = "sys_platform == 'darwin' and python_full_version >= '3.11' and python_full_version < '3.12'"},
{path = "src/addons/character_dna/wheels/pyufbx-0.0.0-cp313-cp313-macosx_11_0_arm64.whl", marker = "sys_platform == 'darwin' and python_full_version >= '3.13' and python_full_version < '3.14'"},
{path = "src/addons/character_dna/wheels/pyufbx-0.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", marker = "sys_platform == 'linux' and python_full_version >= '3.11' and python_full_version < '3.12'"},
{path = "src/addons/character_dna/wheels/pyufbx-0.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", marker = "sys_platform == 'linux' and python_full_version >= '3.13' and python_full_version < '3.14'"},
]

# =============================================================================
# MyPy Configuration (optional, for those who prefer mypy)
# =============================================================================
Expand Down Expand Up @@ -409,6 +424,7 @@ dev = [
"genbadge[all]>=1.1.2",
"mkdocs>=1.6.1",
"poly-hammer-utils",
"pyufbx",
"ruff>=0.8.0",
"pre-commit>=4.0.0",
"pip>=25.3",
Expand Down
3 changes: 2 additions & 1 deletion src/addons/character_dna/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
bl_info = {
"name": "Character DNA",
"author": "Poly Hammer",
"version": (0, 11, 3),
"version": (0, 12, 2),
"blender": (4, 5, 0),
"location": "File > Import > MetaHuman DNA",
"description": (
Expand All @@ -44,6 +44,7 @@
operators.AppendOrLinkCharacter,
operators.ImportFaceBoardAnimation,
operators.ImportComponentAnimation,
operators.ConfirmAnimationImport,
operators.BakeFaceBoardAnimation,
operators.BakeComponentAnimation,
operators.TestSentry,
Expand Down
22 changes: 18 additions & 4 deletions src/addons/character_dna/blender_manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ schema_version = "1.0.0"
id = "character_dna"
name = "Character DNA"
tagline = "Customize MetaHuman DNA files with RigLogic evaluation support"
version = "0.11.3"
version = "0.12.2"
type = "add-on"

# =============================================================================
Expand All @@ -32,12 +32,14 @@ blender_version_max = "5.3.0"
# License Information
# =============================================================================
# The add-on's own code is licensed under GPL-3.0. It also bundles and
# redistributes the OpenRigLogic DNA/RigLogic Python bindings and the Sentry SDK.
# redistributes the OpenRigLogic DNA/RigLogic Python bindings, the ufbx Python
# bindings, and the Sentry SDK.
license = ["GPL-3.0 license", "MIT license"]
copyright = [
"2026 Poly Hammer",
"2026 Epic Games, Inc. (OpenRigLogic DNA and RigLogic libraries, MIT License)",
"2018 Functional Software, Inc. dba Sentry (sentry-sdk, MIT License)",
"2020 Samuli Raivio (ufbx, MIT License)",
]

# =============================================================================
Expand Down Expand Up @@ -95,8 +97,20 @@ permissions = { files = "Import and export DNA files", network = "Error reportin
# Wheels (Python Dependencies)
# =============================================================================
# Python packages required by this extension
# These will be installed automatically when the extension is enabled
wheels = ["./wheels/sentry_sdk-2.52.0-py2.py3-none-any.whl"]
# These will be installed automatically when the extension is enabled.
# The pyufbx wheels are native and therefore per-platform and per-Python-ABI: the
# packager keeps only the wheels matching each platform in that platform's zip, and
# Blender then installs the cp311 (Blender 4.5) or cp313 (Blender 5.x) wheel that
# matches its own interpreter.
wheels = [
"./wheels/sentry_sdk-2.52.0-py2.py3-none-any.whl",
"./wheels/pyufbx-0.0.0-cp311-cp311-win_amd64.whl",
"./wheels/pyufbx-0.0.0-cp311-cp311-macosx_11_0_arm64.whl",
"./wheels/pyufbx-0.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
"./wheels/pyufbx-0.0.0-cp313-cp313-win_amd64.whl",
"./wheels/pyufbx-0.0.0-cp313-cp313-macosx_11_0_arm64.whl",
"./wheels/pyufbx-0.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
]

# =============================================================================
# Build Settings
Expand Down
7 changes: 7 additions & 0 deletions src/addons/character_dna/components/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

# local imports
from .. import utilities
from ..fbx.reader import FbxAnimationClip
from ..utilities import exclude_rig_instance_evaluation
from .base import CharacterComponentBase

Expand All @@ -28,6 +29,7 @@ def import_action(
match_frame_rate: bool = True,
prefix_instance_name: bool = True,
prefix_component_name: bool = True,
clip: "FbxAnimationClip | None" = None,
):
file_path = Path(file_path)

Expand All @@ -39,6 +41,11 @@ def import_action(
file_path=file_path,
component="body",
armature=self.body_rig_object,
round_sub_frames=round_sub_frames,
match_frame_rate=match_frame_rate,
prefix_instance_name=prefix_instance_name,
prefix_component_name=prefix_component_name,
clip=clip,
# include animation only for body that are not driven by rig logic
include_only_bones=[
b.name
Expand Down
4 changes: 4 additions & 0 deletions src/addons/character_dna/components/head.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
FACE_BOARD_SWITCHES,
REGION_VERTEX_GROUP_PREFIX,
)
from ..fbx.reader import FbxAnimationClip
from ..rig_definition import HeadRigDefinition
from ..utilities import exclude_rig_instance_evaluation
from .base import CharacterComponentBase
Expand All @@ -35,6 +36,7 @@ def import_action(
match_frame_rate: bool = True,
prefix_instance_name: bool = True,
prefix_component_name: bool = True,
clip: "FbxAnimationClip | None" = None,
):
file_path = Path(file_path)

Expand All @@ -50,6 +52,7 @@ def import_action(
match_frame_rate=match_frame_rate,
prefix_instance_name=prefix_instance_name,
prefix_component_name=prefix_component_name,
clip=clip,
)
elif self.head_rig_object:
utilities.import_action_from_fbx(
Expand All @@ -61,6 +64,7 @@ def import_action(
match_frame_rate=match_frame_rate,
prefix_instance_name=prefix_instance_name,
prefix_component_name=prefix_component_name,
clip=clip,
)

def ingest(self, align: bool = True, constrain: bool = True) -> tuple[bool, str]:
Expand Down
2 changes: 1 addition & 1 deletion src/addons/character_dna/dna_io/calibrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def calibrate_vertex_positions(self):
head_to_body_edge_loop_mapping=head_to_body_edge_loop_mapping,
)

real_name = mesh_object.name.replace(f"{self._instance.name}_", "")
real_name = utilities.remove_instance_prefix(mesh_object.name, self._instance.name)
logger.info(f'Calibrating "{real_name}" vertex positions...')
mesh_index = mesh_index_lookup.get(real_name)

Expand Down
6 changes: 3 additions & 3 deletions src/addons/character_dna/dna_io/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def initialize_scene_data(self):
self._images.append((output_item.image_object, output_item.name))

# Sort the meshes by the order in the ORDER dictionary
mesh_objects.sort(key=lambda x: x.name.replace(f"{self._prefix}_", ""))
mesh_objects.sort(key=lambda x: utilities.remove_instance_prefix(x.name, self._prefix))

# Populate the LODs with the mesh objects and their indices
mesh_index = 1
Expand Down Expand Up @@ -582,7 +582,7 @@ def export_meshes(self, bone_indices: list[int]):
def _export_mesh(self, mesh_object: bpy.types.Object, mesh_index: int):
"""Write a single mesh's geometry (positions, faces, normals, uvs, vertex
layouts), skin weights, and vertex colors into the DNA at ``mesh_index``."""
real_name = mesh_object.name.replace(f"{self._prefix}_", "")
real_name = utilities.remove_instance_prefix(mesh_object.name, self._prefix)

logger.info(f'Exporting mesh: "{mesh_object.name}" to DNA as "{real_name}"...')
self._dna_writer.clearFaceVertexLayoutIndices(meshIndex=mesh_index)
Expand Down Expand Up @@ -659,7 +659,7 @@ def export_shape_keys(self):

for mesh_objects in self._export_lods.values():
for mesh_object, export_mesh_index in mesh_objects:
real_name = mesh_object.name.replace(f"{self._prefix}_", "")
real_name = utilities.remove_instance_prefix(mesh_object.name, self._prefix)
source_mesh_index = source_mesh_index_by_name.get(real_name)
if source_mesh_index is None:
# A brand new mesh (e.g. custom teeth under a name not in the
Expand Down
2 changes: 1 addition & 1 deletion src/addons/character_dna/editors
14 changes: 14 additions & 0 deletions src/addons/character_dna/fbx/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""FBX animation reading and fast Action writing.

:mod:`.reader` and :mod:`.maths` are free of Blender imports so they can be
tested standalone; :mod:`.writer` is the Blender-facing half.
"""

from .reader import FbxAnimationClip, load_fbx_animation, load_fbx_animation_buffer


__all__ = [
"FbxAnimationClip",
"load_fbx_animation",
"load_fbx_animation_buffer",
]
Loading