ObjectService

June 24, 2026 · View on GitHub

Overview

ObjectService provides a comprehensive collection of static utility methods for managing and manipulating Blender objects within MPFB. It is one of the most heavily used services in the addon, handling everything from basic object creation to complex relationship queries.

The service's responsibilities fall into several categories: object lifecycle management (creation, linking, deletion), selection and activation (making objects active, deselecting), type identification (determining if an object is a basemesh, skeleton, clothes, etc.), relationship queries (finding parents, children, and siblings), and file operations (loading and saving Wavefront OBJ files).

ObjectService introduces the concept of MPFB object types, stored as custom properties on objects. These types include Basemesh, Skeleton, Subrig, Proxymesh, Eyes, Clothes, Hair, and others. Many methods use these types to identify and filter objects, making it easy to find all clothes attached to a character or locate the skeleton associated with a mesh.

The service also provides specialized methods for working with Rigify rigs, detecting whether an armature is a Rigify metarig or generated rig, and finding the relationship between them.

All methods are static; the class should never be instantiated.

Source

src/mpfb/services/objectservice.py

Dependencies

DependencyUsage
LogServiceLogging via LogService.get_logger("services.objectservice")
LocationServiceResolving paths to base mesh OBJ and vertex group definitions
GeneralObjectPropertiesAccessing MPFB custom properties on objects
TargetServiceShape key operations (imported at call time to avoid circular imports)
MaterialServiceMaterial deletion (imported at call time to avoid circular imports)

Object Type Constants

MPFB uses custom properties to identify object types:

TypeDescription
BasemeshThe main human body mesh
SkeletonThe primary armature/rig
SubrigSecondary armatures for clothes or accessories
Proxymesh / ProxymeshesBody proxy meshes
EyesEye meshes
EyelashesEyelash meshes
EyebrowsEyebrow meshes
TongueTongue mesh
TeethTeeth mesh
HairHair mesh or particle system holder
ClothesClothing items

Public API

Object Creation

random_name()

Generate a random string of 15 lowercase ASCII characters.

Returns: str — A random string suitable for use as a temporary object name.


create_blender_object_with_mesh(name="NewObject", parent=None, skip_linking=False)

Create a new mesh object with an empty mesh data block.

ArgumentTypeDefaultDescription
namestr"NewObject"The name for the new object
parentbpy.types.ObjectNoneOptional parent object
skip_linkingboolFalseIf True, don't link to the current collection

Returns: bpy.types.Object — The newly created mesh object.

The mesh data block is named <name>Mesh.


create_blender_object_with_armature(name="NewObject", parent=None)

Create a new armature object with an armature data block.

ArgumentTypeDefaultDescription
namestr"NewObject"The name for the new object
parentbpy.types.ObjectNoneOptional parent object

Returns: bpy.types.Object — The newly created armature object.

The armature data block is named <name>Armature.


create_empty(name, empty_type="SPHERE", parent=None)

Create a new empty object.

ArgumentTypeDefaultDescription
namestrThe name for the empty object
empty_typestr"SPHERE"The empty display type (e.g., "PLAIN_AXES", "ARROWS", "SPHERE")
parentbpy.types.ObjectNoneOptional parent object

Returns: bpy.types.Object — The newly created empty object.


Link an object to a collection and optionally set its parent.

ArgumentTypeDefaultDescription
object_to_linkbpy.types.ObjectThe object to link
collectionbpy.types.CollectionNoneTarget collection (defaults to current context collection)
parentbpy.types.ObjectNoneOptional parent object

Returns: None


Object Deletion

delete_object(object_to_delete)

Safely delete a Blender object.

ArgumentTypeDefaultDescription
object_to_deletebpy.types.Object | NoneThe object to delete

Returns: None

Does nothing if the object is None.


delete_object_by_name(name)

Safely delete an object by its name.

ArgumentTypeDefaultDescription
namestr | NoneThe name of the object to delete

Returns: None

Does nothing if the name is empty or no object with that name exists.


Selection and Activation

activate_blender_object(object_to_make_active, *, context=None, deselect_all=False)

Make an object selected and active.

ArgumentTypeDefaultDescription
object_to_make_activebpy.types.ObjectThe object to activate
contextbpy.types.ContextNoneBlender context (defaults to bpy.context)
deselect_allboolFalseIf True, deselect all other objects first

Returns: None


select_object(obj)

Select an object and make it active, deselecting all others.

ArgumentTypeDefaultDescription
objbpy.types.ObjectThe object to select

Returns: None

This is a convenience wrapper around activate_blender_object with deselect_all=True.


deselect_and_deactivate_all()

Ensure no object is selected or active.

Returns: None

Switches to Object mode if necessary and clears all selection state.


Object Queries

object_name_exists(name)

Check if an object with the given name exists.

ArgumentTypeDefaultDescription
namestr | NoneThe name to check

Returns: boolTrue if an object with that name exists.


ensure_unique_name(desired_name)

Generate a unique object name by appending incrementing numbers if needed.

ArgumentTypeDefaultDescription
desired_namestrThe preferred name

Returns: str — The original name if unique, otherwise <name>.001, <name>.002, etc.


find_by_data(id_data)

Find the object that uses a specific data block.

ArgumentTypeDefaultDescription
id_databpy.types.IDThe data block to search for (e.g., mesh, armature)

Returns: bpy.types.Object or None — The object using that data block.


get_list_of_children(parent_object)

Get all direct children of an object.

ArgumentTypeDefaultDescription
parent_objectbpy.types.ObjectThe parent object

Returns: list[bpy.types.Object] — List of child objects.


Selection Filtering

get_selected_objects(exclude_non_mh_objects=False, exclude_mesh_objects=False, exclude_armature_objects=False, exclude_meta_objects=True)

Get selected objects with optional filtering.

ArgumentTypeDefaultDescription
exclude_non_mh_objectsboolFalseExclude objects without an MPFB object_type
exclude_mesh_objectsboolFalseExclude mesh objects
exclude_armature_objectsboolFalseExclude armature objects
exclude_meta_objectsboolTrueExclude objects that are neither mesh nor armature

Returns: list[bpy.types.Object] — Filtered list of selected objects.


get_selected_armature_objects()

Get all selected armature objects.

Returns: list[bpy.types.Object] — List of selected armature objects.


get_selected_mesh_objects()

Get all selected mesh objects.

Returns: list[bpy.types.Object] — List of selected mesh objects.


Object Type Identification

get_object_type(blender_object)

Get the MPFB object type of an object.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe object to query

Returns: str — The object type (e.g., "Basemesh", "Skeleton") or empty string if not set.


object_is(blender_object, mpfb_type_name)

Check if an object matches one or more MPFB types.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe object to check
mpfb_type_namestr or Sequence[str]Type name or list of acceptable type names

Returns: boolTrue if the object matches any of the specified types.

Type matching is case-insensitive and uses substring matching.


object_is_basemesh(blender_object)

Check if an object is a basemesh.

Returns: boolTrue if object_type == "Basemesh".


object_is_skeleton(blender_object)

Check if an object is a skeleton.

Returns: boolTrue if object_type == "Skeleton".


object_is_subrig(blender_object)

Check if an object is a subrig.

Returns: boolTrue if object_type == "Subrig".


object_is_any_skeleton(blender_object)

Check if an object is any skeleton type.

Returns: boolTrue if object_type is "Skeleton" or "Subrig".


object_is_body_proxy(blender_object)

Check if an object is a body proxy.

Returns: boolTrue if object_type is "Proxymesh" or "Proxymeshes".


object_is_eyes(blender_object)

Check if an object is eyes.

Returns: boolTrue if object_type == "Eyes".


object_is_basemesh_or_body_proxy(blender_object)

Check if an object is a basemesh or body proxy.

Returns: boolTrue if the object is a basemesh or proxy.


object_is_any_mesh(blender_object)

Check if an object is a mesh (Blender type).

Returns: bool | None — Truthy if the object exists and has type MESH; None when the object is None.


object_is_any_makehuman_mesh(blender_object)

Check if an object is a mesh with an MPFB object type.

Returns: str | bool | None — The object_type string when the object is a mesh with a valid type; otherwise a falsy False/None.


object_is_any_mesh_asset(blender_object)

Check if an object is a mesh asset (clothes, eyes, hair, etc.).

Returns: bool | None — Truthy if the object is a mesh with a mesh asset type; None when the object is None.


object_is_any_makehuman_object(blender_object)

Check if an object has any MPFB object type set.

Returns: str | None — The object_type string when set; None when the object is None.


Relationship Queries

find_object_of_type_amongst_nearest_relatives(blender_object, mpfb_type_name="Basemesh", *, only_parents=False, strict_parent=False, only_children=False)

Find a single object of the specified type among relatives.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe starting object
mpfb_type_namestr or Sequence[str]"Basemesh"Type(s) to search for
only_parentsboolFalseOnly search up the parent chain
strict_parentboolFalseStop at non-MakeHuman parents
only_childrenboolFalseOnly search children

Returns: bpy.types.Object or None — The first matching object found.

Searches the object itself, its children, parents, and siblings.


find_all_objects_of_type_amongst_nearest_relatives(blender_object, mpfb_type_name="Basemesh", *, only_parents=False, strict_parent=False, only_children=False)

Find all objects of the specified type among relatives.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe starting object
mpfb_type_namestr or Sequence[str]"Basemesh"Type(s) to search for
only_parentsboolFalseOnly search up the parent chain
strict_parentboolFalseStop at non-MakeHuman parents
only_childrenboolFalseOnly search children

Returns: Generator[bpy.types.Object] — Generator yielding matching objects.


Find all related MPFB objects.

Returns: Generator[bpy.types.Object] — Generator yielding all related MPFB objects.


Find all related skeleton objects.

Returns: Generator[bpy.types.Object] — Generator yielding skeleton objects.


Find all related mesh objects (basemesh and assets).

Returns: Generator[bpy.types.Object] — Generator yielding mesh objects.


Find all related mesh asset objects (not basemesh).

Returns: Generator[bpy.types.Object] — Generator yielding mesh asset objects.


Find all related body part assets (eyes, teeth, etc.).

Returns: Generator[bpy.types.Object] — Generator yielding body part objects.


find_deformed_child_meshes(armature_object)

Find all mesh objects deformed by an armature.

ArgumentTypeDefaultDescription
armature_objectbpy.types.Object | NoneThe armature to check

Returns: Generator[bpy.types.Object] — Generator yielding deformed mesh objects.


Rigify Integration

object_is_generated_rigify_rig(blender_object)

Check if an object is a generated Rigify rig.

Returns: bool | None — Truthy (the rig_id value) if the armature has a rig_id data property; None when the object is None.


object_is_rigify_metarig(blender_object, *, check_bones=False)

Check if an object is a Rigify metarig.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe object to check
check_bonesboolFalseAlso check bones for rigify_type property

Returns: boolTrue if the object is a Rigify metarig.


find_rigify_metarig_by_rig(blender_object)

Find the metarig associated with a generated Rigify rig.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe generated rig

Returns: bpy.types.Object or None — The associated metarig.


find_rigify_rig_by_metarig(blender_object)

Find the generated rig associated with a Rigify metarig.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe metarig

Returns: bpy.types.Object or None — The generated rig.


find_armature_context_objects(armature_object, *, operator=None, is_subrig=None, only_basemesh=False)

Find the base rig, basemesh, and directly controlled mesh for an armature.

ArgumentTypeDefaultDescription
armature_objectbpy.types.ObjectThe armature to analyze
operatorbpy.types.OperatorNoneOperator for error reporting
is_subrigboolNoneOverride subrig detection
only_basemeshboolFalseOnly find basemesh, not direct mesh

Returns: tuple[Object, Object, Object](base_rig, basemesh, direct_mesh), any may be None.


File Operations

load_wavefront_file(filepath, context=None)

Load a Wavefront OBJ file into Blender.

ArgumentTypeDefaultDescription
filepathstrPath to the OBJ file
contextbpy.types.ContextNoneBlender context

Returns: bpy.types.Object — The loaded mesh object.

Raises: ValueError if filepath is None, IOError if file doesn't exist.


save_wavefront_file(filepath, mesh_object, context=None)

Save a mesh object to a Wavefront OBJ file.

ArgumentTypeDefaultDescription
filepathstrPath for the OBJ file
mesh_objectbpy.types.ObjectThe mesh to save
contextbpy.types.ContextNoneBlender context

Returns: None

Raises: ValueError if filepath is None or mesh_object is invalid.


load_base_mesh(context=None, scale_factor=1.0, load_vertex_groups=True, exclude_vertex_groups=None)

Load the MPFB base mesh.

ArgumentTypeDefaultDescription
contextbpy.types.ContextNoneBlender context
scale_factorfloat1.0Scale to apply to the mesh
load_vertex_groupsboolTrueWhether to load vertex groups
exclude_vertex_groupslist[str]NoneVertex groups to skip

Returns: bpy.types.Object — The loaded basemesh with smooth shading and MPFB properties set.


Vertex Group Operations

has_vertex_group(blender_object, vertex_group_name)

Check if an object has a specific vertex group.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe object to check
vertex_group_namestr | NoneThe vertex group name

Returns: boolTrue if the vertex group exists.


get_vertex_indexes_for_vertex_group(blender_object, vertex_group_name)

Get vertex indices belonging to a vertex group.

ArgumentTypeDefaultDescription
blender_objectbpy.types.Object | NoneThe mesh object
vertex_group_namestr | NoneThe vertex group name

Returns: list[int] — List of vertex indices.


assign_vertex_groups(blender_object, vertex_group_definition, exclude_groups=None)

Assign vertex groups from a definition dictionary.

ArgumentTypeDefaultDescription
blender_objectbpy.types.ObjectThe mesh object
vertex_group_definitiondictDict mapping group names to vertex index lists
exclude_groupslist[str]NoneGroups to skip

Returns: None


get_base_mesh_vertex_group_definition()

Get the standard vertex group definition for the base mesh.

Returns: dict — Dictionary mapping group names to lists of vertex indices.

Results are cached after first load.


Mesh Utilities

get_lowest_point(basemesh, take_shape_keys_into_account=True)

Get the lowest Z coordinate of a basemesh.

ArgumentTypeDefaultDescription
basemeshbpy.types.ObjectThe mesh object
take_shape_keys_into_accountboolTrueConsider shape key deformation

Returns: float — The minimum Z coordinate.

Useful for positioning characters on a ground plane.


get_face_to_vertex_table()

Get the face-to-vertex mapping table for the base mesh.

Returns: dict — Dictionary mapping face indices to vertex index lists.

Results are cached after first load.


get_vertex_to_face_table()

Get the vertex-to-face mapping table for the base mesh.

Returns: dict — Dictionary mapping vertex indices to face index lists.

Results are cached after first load.


extract_vertex_group_to_new_object(existing_object, vertex_group_name)

Extract a vertex group into a new mesh object.

ArgumentTypeDefaultDescription
existing_objectbpy.types.ObjectThe source mesh
vertex_group_namestrThe vertex group to extract

Returns: None — The method creates a new "clothes" mesh object (linked to the current collection) as a side effect, but does not return it.


Examples

Loading and Manipulating the Base Mesh

from mpfb.services.objectservice import ObjectService
import bpy

# Load the base mesh at 10% scale
basemesh = ObjectService.load_base_mesh(
    context=bpy.context,
    scale_factor=0.1,
    load_vertex_groups=True
)

# Make it active
ObjectService.activate_blender_object(basemesh, deselect_all=True)

# Check what type it is
print(ObjectService.get_object_type(basemesh))  # "Basemesh"
from mpfb.services.objectservice import ObjectService

# Starting from any object in a character hierarchy
selected = bpy.context.active_object

# Find the basemesh
basemesh = ObjectService.find_object_of_type_amongst_nearest_relatives(
    selected, "Basemesh"
)

# Find all clothes
for clothes in ObjectService.find_all_objects_of_type_amongst_nearest_relatives(
    basemesh, "Clothes", only_children=True
):
    print(f"Found clothes: {clothes.name}")

# Find the skeleton
skeleton = ObjectService.find_object_of_type_amongst_nearest_relatives(
    basemesh, "Skeleton"
)

Working with Rigify

from mpfb.services.objectservice import ObjectService

armature = bpy.context.active_object

if ObjectService.object_is_generated_rigify_rig(armature):
    # Find the metarig
    metarig = ObjectService.find_rigify_metarig_by_rig(armature)
    print(f"Metarig: {metarig.name}")
elif ObjectService.object_is_rigify_metarig(armature):
    # Find the generated rig
    rig = ObjectService.find_rigify_rig_by_metarig(armature)
    if rig:
        print(f"Generated rig: {rig.name}")

Creating Custom Objects

from mpfb.services.objectservice import ObjectService

# Create an empty as a parent
parent_empty = ObjectService.create_empty("CharacterRoot", empty_type="PLAIN_AXES")

# Create a mesh parented to it
mesh_obj = ObjectService.create_blender_object_with_mesh(
    name="CustomMesh",
    parent=parent_empty
)

# Create an armature parented to the empty
armature_obj = ObjectService.create_blender_object_with_armature(
    name="CustomRig",
    parent=parent_empty
)