HumanService

March 6, 2026 · View on GitHub

Overview

HumanService is the highest-level orchestrator in MPFB's service layer. It coordinates the full lifecycle of a human character: creating the basemesh, applying macro and micro targets, adding rigs, equipping body-part and clothing assets, setting skin and eye materials, and serializing/deserializing the entire character definition to and from JSON or MHM files.

The serialization path collects information from a fully assembled character (targets, rig type, equipped assets, material settings, ink layers, alternative materials, and color adjustments) into a human_info dictionary that is written as JSON. The deserialization path reads that dictionary back and recreates the character from scratch, respecting override settings for rig, skin model, clothes material model, subdivision levels, and more. MHM files (MakeHuman's native save format) are also supported as an import source, with target and asset lines parsed and mapped to MPFB equivalents.

The service also manages a preset system: character definitions stored as human.*.json files in the user config directory can be listed, cached, and loaded. A refit operation allows the character to be updated after target changes by re-fitting all equipped clothes and re-positioning the rig.

Characters with custom rigs (user-defined rigs saved to the rigs/ directory in user data) are fully supported: they can be added via add_custom_rig(), serialized with the "custom.<name>" identifier, and reloaded from a preset with a descriptive error message if the rig file is no longer available. All methods are static; the class should never be instantiated.

Source

src/mpfb/services/humanservice.py

Dependencies

DependencyUsage
LogServiceLogging via LogService.get_logger("services.humanservice")
ObjectServiceBasemesh loading, object discovery, parenting, deselection
TargetServiceMacro/micro target loading, shape key management
AssetServiceLocating asset files (clothes, skins, body parts)
ClothesServiceFitting clothes, delete groups, weight interpolation, rigging
RigServiceRig identification, weight loading, armature management, refitting
NodeServiceShader node queries and value manipulation
MaterialServiceMaterial creation, deletion, identification, ink layers, color adjustment
LocationServiceResolving paths to config, data, and settings directories
SystemServiceChecking for Rigify addon availability
HumanObjectPropertiesReading/writing human-specific custom properties (phenotype values, material source)
GeneralObjectPropertiesReading/writing general custom properties (asset source, UUID, scale factor)
MhcloMHCLO entity for loading clothes/body-part assets
RigRig entity for creating armatures from JSON definitions
MakeSkinMaterialMakeSkin material node tree generation
MhMaterialMHMAT file parsing
EnhancedSkinMaterialEnhanced skin material node tree generation
PrimitiveProfilerPerformance profiling

Public API

Character Creation

create_human(mask_helpers=True, detailed_helpers=True, extra_vertex_groups=True, feet_on_ground=True, scale=0.1, macro_detail_dict=None)

Create a new human basemesh with the specified settings. Loads the basemesh via ObjectService, applies macro details (gender, age, weight, etc.) from the provided dictionary, optionally masks helper geometry, and positions the feet on the ground plane.

ArgumentTypeDefaultDescription
mask_helpersboolTrueAdd a MASK modifier to hide helper vertex groups
detailed_helpersboolTrueInclude detailed helper and joint vertex groups
extra_vertex_groupsboolTrueInclude extra vertex groups (Mid, Right, Left, basemesh extras)
feet_on_groundboolTrueTranslate the mesh so the lowest point sits at Z=0
scalefloat0.1Scale factor for the basemesh (0.1 = decimeters)
macro_detail_dictdictNoneMacro detail settings; uses TargetService.get_default_macro_info_dict() if None

Returns: bpy.types.Object — The created basemesh object.


add_builtin_rig(basemesh, rig_name, *, import_weights=True, operator=None)

Add a built-in rig to the basemesh. Supports both standard rigs and Rigify meta-rigs (names starting with "rigify."). The rig is loaded from a JSON definition file in the data/rigs/ directory, fitted to the basemesh, and the basemesh is parented to the armature. Weights are loaded from a sidecar weight file if available.

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe basemesh to attach the rig to
rig_namestrRig identifier (e.g., "default", "rigify.human_toes")
import_weightsboolTrueWhether to load bone weights from the weight file
operatorbpy.types.OperatorNoneOptional operator for error reporting via operator.report

Returns: bpy.types.Object or None — The created armature object, or None if the rig file was not found.

Raises: NotImplementedError if a Rigify rig is requested but Rigify is not enabled. IOError if the rig file is missing and no operator is provided.


add_custom_rig(basemesh, rig_name, *, import_weights=True, operator=None)

Load a custom rig from user data and attach it to the given basemesh. The rig is looked up via AssetService.get_custom_rigs() using the short name (the part after "custom."). The rig JSON file is loaded and fitted to the basemesh, the basemesh is parented to the armature, rotation modes are normalized, and the armature is moved to the basemesh's original world location. If import_weights is True, the corresponding weights file (weights.<name>.json in the same directory as the rig JSON) is loaded if it exists.

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe basemesh to attach the rig to
rig_namestrFull rig name including prefix (e.g., "custom.my_rig")
import_weightsboolTrueWhether to load weights and create an armature modifier
operatorbpy.types.OperatorNoneOptional operator for error/warning reporting via operator.report

Returns: bpy.types.Object or None — The created armature object, or None if the custom rig was not found in user data.

Raises: ValueError if the rig is not found and no operator is provided (when an operator is provided, an ERROR report is issued and None is returned instead).


Asset Management

add_mhclo_asset(mhclo_file, basemesh, asset_type="Clothes", subdiv_levels=1, material_type="MAKESKIN", alternative_materials=None, color_adjustments=None, set_up_rigging=True, interpolate_weights=True, import_subrig=True, import_weights=True)

Load an MHCLO asset (clothes, body part, proxy, etc.) and fully integrate it with the basemesh: import the mesh, assign materials, fit to the character, set up delete groups, configure rigging, and add a subdivision modifier. Material setup supports MAKESKIN, GAMEENGINE, and PROCEDURAL_EYES types, with optional alternative material and color adjustment overrides.

ArgumentTypeDefaultDescription
mhclo_filestrAbsolute path to the MHCLO file
basemeshbpy.types.ObjectThe basemesh to equip the asset onto
asset_typestr"Clothes"Asset category (e.g., "Clothes", "Eyes", "Hair", "Proxymeshes")
subdiv_levelsint1Render subdivision levels; set to 0 to skip
material_typestr"MAKESKIN"Material type: "MAKESKIN", "GAMEENGINE", "PROCEDURAL_EYES", or "NONE"
alternative_materialsdictNoneUUID-keyed dictionary of alternative material path fragments
color_adjustmentsdictNoneUUID-keyed dictionary of color adjustment settings
set_up_riggingboolTrueWhether to rig the asset to the character's armature
interpolate_weightsboolTrueWhether to interpolate bone weights from basemesh
import_subrigboolTrueWhether to import a custom sub-rig
import_weightsboolTrueWhether to load custom weight files

Returns: bpy.types.Object — The imported clothes/asset mesh object.

Raises: IOError if the mesh fails to import.


set_character_skin(mhmat_file, basemesh, bodyproxy=None, skin_type="ENHANCED_SSS", material_instances=True, slot_overrides=None)

Set the skin material for a character. Supports multiple skin material types: MAKESKIN (simple node-based), GAMEENGINE (optimized for game export), LAYERED (multi-group v2 skin), and ENHANCED/ENHANCED_SSS (procedural skin with optional subsurface scattering). When material_instances is enabled for enhanced skins, separate material slots are created for different body zones with per-slot settings from the default or overridden configuration.

ArgumentTypeDefaultDescription
mhmat_filestrAbsolute path to the MHMAT skin material file
basemeshbpy.types.ObjectThe basemesh to apply the skin to
bodyproxybpy.types.ObjectNoneOptional body proxy; auto-discovered if None
skin_typestr"ENHANCED_SSS"Skin material type: "MAKESKIN", "GAMEENGINE", "LAYERED", "ENHANCED", or "ENHANCED_SSS"
material_instancesboolTrueCreate separate material slots per body zone
slot_overridesdictNonePer-slot material settings keyed by zone name (e.g., "body", "face")

Returns: None


unload_mhclo_asset(basemesh, asset)

Unload and remove an equipped MHCLO asset. Removes the associated delete-group MASK modifiers from the basemesh and proxy, deletes the sub-rig if one exists, and removes the asset object from the scene.

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe basemesh the asset belongs to
assetbpy.types.ObjectThe asset object to remove

Returns: None


get_asset_sources_of_equipped_mesh_assets(basemesh)

Retrieve the asset_source property values of all mesh assets currently equipped on the basemesh (clothes, body parts, proxies).

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe basemesh to query

Returns: list[str] — List of asset source path fragments.


Serialization

serialize_to_json_string(basemesh, save_clothes=False)

Serialize a fully assembled character to a JSON string. Collects phenotype (macro) details, rig type, body parts (eyes, hair, teeth, etc.), clothes, proxy, skin settings, eye material settings, ink layers, alternative materials, and color adjustments. Custom rigs are serialized using the "custom.<name>" identifier returned by RigService.identify_rig().

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe basemesh of the character to serialize
save_clothesboolFalseWhether to include clothes in the serialization

Returns: str — JSON string representation of the character.

Raises: ValueError if basemesh is None, is not an MPFB human project, or has an attached rig that cannot be identified (including rigs not in user data).


serialize_to_json_file(basemesh, filename, save_clothes=False)

Serialize a character to a JSON file. Calls serialize_to_json_string and writes the result to disk. Automatically refreshes the preset list after writing.

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe basemesh to serialize
filenamestrOutput file path
save_clothesboolFalseWhether to include clothes

Returns: None

Raises: ValueError if filename is None or empty.


Deserialization

deserialize_from_dict(human_info, deserialization_settings)

Reconstruct a full character from a human_info dictionary and deserialization settings. Creates the basemesh, loads targets, adds the rig, equips body parts and clothes, sets up the proxy, applies skin and eye materials, loads ink layers, and enables shape key edit mode. If the "rig" key starts with "custom.", the rig is loaded via add_custom_rig() instead of add_builtin_rig(). If the named custom rig is not available in user data, deserialization fails with a descriptive error message.

ArgumentTypeDefaultDescription
human_infodictCharacter definition dictionary (phenotype, rig, assets, materials, etc.)
deserialization_settingsdictSettings controlling the deserialization process (see get_default_deserialization_settings)

Returns: bpy.types.Object — The created basemesh object.

Raises: ValueError if human_info is None or empty, or if a referenced custom rig is not found in user data.


deserialize_from_json_file(filename, deserialization_settings)

Load a character definition from a JSON file and deserialize it. The preset name is extracted from the filename pattern human.<name>.json.

ArgumentTypeDefaultDescription
filenamestrPath to the JSON file
deserialization_settingsdictDeserialization settings dictionary

Returns: bpy.types.Object — The created basemesh object.

Raises: IOError if the file does not exist.


deserialize_from_mhm(filename, deserialization_settings)

Import a character from a MakeHuman Model (.mhm) file. Parses modifier lines (macro targets and micro targets), body-part references, clothes references, skeleton type, and skin material. Asset matching supports both UUID-based and filename-based lookups, with optional deep search for harder-to-find assets.

ArgumentTypeDefaultDescription
filenamestrPath to the MHM file
deserialization_settingsdictSettings dictionary; must include clothes_deep_search and bodypart_deep_search booleans

Returns: bpy.types.Object — The created basemesh object.

Raises: IOError if the file does not exist.


get_default_deserialization_settings()

Return a dictionary with default values for all deserialization options.

Returns: dict — Default settings:

KeyDefaultDescription
mask_helpersTrueMask helper geometry
detailed_helpersTrueInclude detailed helper groups
extra_vertex_groupsTrueInclude extra vertex groups
feet_on_groundTruePlace feet at Z=0
scale0.1Basemesh scale factor
subdiv_levels1Subdivision render levels
load_clothesTrueEquip clothes from the preset
override_skin_model"PRESET"Skin material override ("PRESET" uses the saved value)
override_rig"PRESET"Rig override ("PRESET" uses the saved value, "NONE" skips rig)
material_instances"NEVER"Material instance mode for enhanced skins

Presets

update_list_of_human_presets()

Scan the user config directory for files matching human.*.json and refresh the cached preset list.

Returns: None


get_list_of_human_presets(as_list_enum=True, use_cache=True)

Return the list of available human presets.

ArgumentTypeDefaultDescription
as_list_enumboolTrueReturn as Blender EnumProperty-compatible list of (id, name, description) tuples
use_cacheboolTrueUse the cached list if available

Returns: list — List of tuples (if as_list_enum=True) or list of strings.


Refitting

refit(blender_object)

Refit a character after target changes. Finds the basemesh from the given object's relatives, re-fits all equipped clothes and proxy meshes via ClothesService.fit_clothes_to_human, and re-positions the rig and any sub-rigs.

ArgumentTypeDefaultDescription
blender_objectbpy.types.ObjectAny object belonging to the character (basemesh, rig, clothes, etc.)

Returns: None

Raises: ValueError if the basemesh cannot be found as a relative of the given object.


Examples

Creating a Character from Scratch

from mpfb.services.humanservice import HumanService
from mpfb.services.targetservice import TargetService

# Create a basemesh with default settings
basemesh = HumanService.create_human(scale=0.1, feet_on_ground=True)

# Customize macro details
macro = TargetService.get_default_macro_info_dict()
macro["gender"] = 1.0  # female
macro["age"] = 0.5
basemesh = HumanService.create_human(macro_detail_dict=macro)

# Add a built-in rig
rig = HumanService.add_builtin_rig(basemesh, "default")

# Add a custom rig from user data
rig = HumanService.add_custom_rig(basemesh, "custom.my_rig", import_weights=True)

Equipping Assets

from mpfb.services.humanservice import HumanService

# Add a clothing item
HumanService.add_mhclo_asset("/path/to/jeans.mhclo", basemesh)

# Add eyes with procedural material
HumanService.add_mhclo_asset(
    "/path/to/high-poly-eyes.mhclo", basemesh,
    asset_type="Eyes", material_type="PROCEDURAL_EYES"
)

# Set the skin material
HumanService.set_character_skin(
    "/path/to/young_caucasian_female.mhmat", basemesh,
    skin_type="ENHANCED_SSS", material_instances=True
)

Serialization and Deserialization

from mpfb.services.humanservice import HumanService

# Save a character to a preset file
HumanService.serialize_to_json_file(basemesh, "/path/to/human.my_character.json")

# Load a character from a preset
settings = HumanService.get_default_deserialization_settings()
settings["subdiv_levels"] = 2
basemesh = HumanService.deserialize_from_json_file(
    "/path/to/human.my_character.json", settings
)

# Import from a MakeHuman save file
settings["clothes_deep_search"] = False
settings["bodypart_deep_search"] = True
basemesh = HumanService.deserialize_from_mhm("/path/to/character.mhm", settings)

Managing Equipped Assets

from mpfb.services.humanservice import HumanService

# List all equipped asset sources
sources = HumanService.get_asset_sources_of_equipped_mesh_assets(basemesh)
for source in sources:
    print(f"Equipped: {source}")

# Unload an asset
HumanService.unload_mhclo_asset(basemesh, jeans_object)

# Refit all assets after changing targets
HumanService.refit(basemesh)