Soft-Skinned Actors
A soft-skinned actor is an articulated actor that combines the articulated skeleton with one or more soft (deformable) volumes rigidly attached to the skeleton. The skeleton provides the overall pose while the soft volumes add deformation — surface-level dynamics, compression, and contact response.
This one actor type spans three flavors of articulation:
- Rigid links with soft leaf volumes — a mostly-rigid articulation with soft volumes attached to selected links (e.g. an articulated robot with soft sensors on its end-effectors).
- An elastic skin over the articulation — a deformable skin wrapping the skeleton that deforms under contact (e.g. a character with an articulated skeleton and a soft body).
- A blended skin — a single surface that is soft and deforming in some regions and rigidly skeleton-following in others (e.g. a hand with soft fingertips).
Architecture
A soft-skinned actor is not an actor entity of its own; it couples two actor types:
- The articulated body actor is the skeleton: rigid links connected by joints (see Articulated Actors).
- Each nested soft actor is a deformable volume: a tetrahedral mesh with a material model and elastic parameters (see Soft Actors).
Multiple nested soft actors are supported per soft-skinned actor (DynamicArray<SoftActorParams>), so different regions can use distinct meshes or materials. Every step, the soft volumes are skinned to the current skeletal pose, then their deformation is solved on top of that posed configuration (see Energy Formulations).
Attaching Soft Volumes to the Skeleton
Each soft volume is attached to the skeleton through a two-step mechanism; both steps are always required. First, a subset of its nodes — the constrained nodes — are pinned to the articulation. Second, the soft volume is driven via linear blend skinning by a subset of skeleton links. There are two ways to specify skinning, and every nested soft actor uses one of them:
- Attach to a link — name the target link in
softAttachLinks(1-to-1 withsoftParams). The nested soft actor is skinned by a single link, hence skinning weights and indices are not explicitly provided. Contact between the nested soft actor and its attach link is disabled automatically. - Skinning data — leave
softAttachLinksempty and embed skinning weights in the soft shape, mapping its nodes across multiple links.
Contact Surface
- By default, contact is computed on the individual soft surfaces. Set
enableCollidingLinksto also collide the rigid links. - With a blended skin (
skeletonParams.skin), a single overarching mesh becomes the colliding surface, presenting one seamless surface — deforming where it is backed by a soft region and rigidly following the skeleton elsewhere. See Blended Skin.
The Three Flavors
The flavors listed above are combinations of these two choices:
| Flavor | Soft coverage | Attachment | Colliding surface |
|---|---|---|---|
| 1. Rigid + soft leaves | soft volumes on some links | softAttachLinks | soft surfaces + rigid links |
| 2. Elastic skin | skin spanning the body | skinning data | the soft skin |
| 3. Blended skin | mixed soft / rigid-follow | attach or skinning data | blended skin mesh |
Energy Formulations
Simulating a soft-skinned actor combines skinning — the geometric transform that carries a soft volume along with the skeleton — with the elastic deformation solved by the nested soft actor. Three things determine the outcome: how the two compose, how the nested soft actor is pinned to the skeleton, and where each physical energy is evaluated.
Deformation and Skinning
Using the soft actor notation, let be a node's rest (unposed) position and its elastic displacement, so is the deformed position in the rest frame. The skeleton contributes a skinning map for its current configuration — linear blend skinning over the links that drive the node:
where is link 's world transform and its skinning weight. The two compose into the posed configuration:
- Pure skinning — the volume simply follows the skeleton.
- Skinned deformation — deform in the rest frame, then skin.
- Blended skin — a per-node weight mixes the deformed and purely-skinned positions ( fully soft, rigidly following the skeleton). See Blended Skin.
Constrained Nodes
The rigid coupling to the skeleton is enforced by fixing on a subset of nodes — the constrained nodes — as zero-displacement Dirichlet boundary conditions. Those nodes then sit exactly at , following the skeleton rigidly. This gives perfect rigid attachment robustly and efficiently, with no additional coupling constraints between the two actor types, and is always required (see Attaching Soft Volumes to the Skeleton).
Posed vs. Unposed Energies
Each physical energy can be evaluated at the unposed positions or the posed positions . Evaluating everything posed is the most physically accurate. But since skinning is a geometric — not physical — deformation, some terms are cheaper (and sometimes exact) unposed. The choice is made per term: set a term's flag on the parent SoftSkinnedActorParams to evaluate it posed, or on the child SoftActorParams to evaluate it unposed.
| Energy | Posed (SoftSkinnedActorParams) | Unposed (SoftActorParams) |
|---|---|---|
Elasticity (hasStress) | physically accurate | cheaper — and exact when the nested soft actor is rigidly attached to a single link via softAttachLinks, since a rigid skinning transform preserves elastic energy |
Inertia (hasInertia) | physically accurate | cheaper, but a cruder approximation |
Gravity (hasGravity) | physically accurate | not available — keep gravity posed on the parent, or disable it |
Contact is always posed: it acts on the world-space surface and is not a choice.
- A term must not be enabled on both the parent and the child — it would be counted twice.
SoftActorParams.hasGravitymust befalse: gravity is only ever posed (onSoftSkinnedActorParams) or off.
Creating Soft-Skinned Actors
A soft-skinned actor is created in a single call to CreateSoftSkinnedActor / create_soft_skinned_actor from a SoftSkinnedActorParams, which bundles:
- a
skeletonParams(ArticulatedActorParams) — the articulated skeleton, built from paralleljointsandlinksarrays (see Articulated Actors); and - one or more
softParams(SoftActorParams) — the soft volumes, each a tetrahedral mesh with constrained nodes.
Bind each soft region to a skeleton link with softAttachLinks (1-to-1 with softParams). Name any link you attach to so it can be referenced; contact between a nested soft actor and its attach link is disabled automatically.
- C++
- Python
ArticulatedActorParams skeletonParams;
skeletonParams.name = "SoftSkinnedDoublePendulum";
// ... joints and links built as in Articulated Actors; links named
// "UpperArm"/"LowerArm" on layer "Pendulum" ...
// A nested soft actor: a tet mesh with constrained nodes (softShape). hasGravity must be
// false; this uses unposed elasticity (hasStress on the soft), accurate when rigidly attached.
SoftActorParams softParams;
softParams.name = "SoftArm";
softParams.layer = "Soft";
softParams.shape = softShape;
softParams.material.type = SoftMaterialType::NeoHookean;
softParams.material.neoHookean.youngsModulus = 1e4_r;
softParams.material.density = 500.0_r;
softParams.hasGravity = false;
softParams.hasInertia = false;
softParams.hasStress = true;
// Build the soft-skinned actor with posed gravity and inertia.
SoftSkinnedActorParams softSkinnedParams;
softSkinnedParams.skeletonParams = skeletonParams;
softSkinnedParams.softParams = {softParams};
softSkinnedParams.softAttachLinks = {"LowerArm"}; // attach the soft to the lower arm
softSkinnedParams.enableCollidingLinks = true;
softSkinnedParams.hasGravity = true;
softSkinnedParams.hasInertia = true;
softSkinnedParams.hasStress = false;
Actor* actor = scene->CreateSoftSkinnedActor(softSkinnedParams, error);
skeleton_params = mochi.ArticulatedActorParams(name="SoftSkinnedDoublePendulum")
# ... joints and links built as in Articulated Actors; links named
# "UpperArm"/"LowerArm" on layer "Pendulum" ...
# A nested soft actor: a tet mesh with constrained nodes (soft_shape). has_gravity must be
# False; this uses unposed elasticity (has_stress on the soft), accurate when rigidly attached.
soft_params = mochi.SoftActorParams(
name="SoftArm", layer="Soft", shape=soft_shape,
has_gravity=False, has_inertia=False, has_stress=True,
)
soft_params.material.type = mochi.SoftMaterialType.NEO_HOOKEAN
soft_params.material.neo_hookean.youngs_modulus = 1e4
soft_params.material.density = 500.0
# Build the soft-skinned actor with posed gravity and inertia.
actor = scene.create_soft_skinned_actor(
skeleton_params=skeleton_params,
soft_params=[soft_params],
soft_attach_links=["LowerArm"], # attach the soft to the lower arm
enable_colliding_links=True,
has_gravity=True,
has_inertia=True,
has_stress=False,
)
This example mixes the two energy formulations: gravity and inertia are posed (on the top-level articulated actor) while elasticity is unposed (hasStress on the nested soft actor). This is valid as long as no flag is true on both sides.
The same scene can be authored declaratively as a prefab (.mochi_scene JSON) and loaded with prefab::AddToScene / mochi.prefab.add_to_scene. The Soft-Skinned Double Pendulum example ships both forms.
SoftSkinnedActorParams Reference
The top-level parameters use SoftSkinnedActorParams (C++, Python).
| Field | C++ Type | Python Name | Default | Description |
|---|---|---|---|---|
skeletonParams | ArticulatedActorParams | skeleton_params | -- | The articulated skeleton. See Skeleton Parameters. |
softParams | DynamicArray<SoftActorParams> | soft_params | [] | One entry per nested soft actor. See Nested Soft Actor Parameters. |
softAttachLinks | DynamicArray<String> | soft_attach_links | [] | Names of skeleton links where nested soft actors attach. If provided, must be 1-to-1 with softParams. If empty, soft shapes must include skinning data. Contact between a nested soft actor and its attach link is disabled automatically. |
enableCollidingLinks | bool | enable_colliding_links | false | Enable internal skeleton links as colliding surfaces. If false, only the nested soft actors (or the blended skin) act as colliding surfaces. |
hasGravity | bool | has_gravity | false | Gravity for the whole actor. Each SoftActorParams entry must have hasGravity = false. |
hasInertia | bool | has_inertia | false | Inertia on posed (post-skinning) positions. If true, each SoftActorParams must have hasInertia = false. See Energy Formulations. |
hasStress | bool | has_stress | false | Elasticity on posed (post-skinning) positions. If true, each SoftActorParams must have hasStress = false. See Energy Formulations. |
Skeleton Parameters
skeletonParams is a full ArticulatedActorParams (C++, Python); see the parameter reference. Joints, links, cycles, initial velocities, and root placement all behave exactly as for a standalone articulated actor. Only two aspects are specific to soft-skinned actors:
skin(ArticulatedSkinParams) is the one field with soft-skinned-specific meaning: it enables the blended skin coupling mode. See Blended Skin.worldFromRootplaces the whole actor. Because each nested soft actor'sworldFromLocalis forced to identity, the skeleton root transform is the single source of global placement — author the soft rest mesh directly in the skeleton's rest frame.
To keep the rigid links from colliding with each other or the environment (leaving the soft skin as the contact surface), disable their contact layer. In this scene the links share the Pendulum layer:
- C++
- Python
scene->EnableLayerContactSymmetric("Pendulum", "Pendulum", false, error);
scene->EnableLayerContactSymmetric("Pendulum", "Environment", false, error);
scene.enable_layer_contact_symmetric("Pendulum", "Pendulum", enable=False)
scene.enable_layer_contact_symmetric("Pendulum", "Environment", enable=False)
Nested Soft Actor Parameters
Each entry in softParams uses SoftActorParams (C++, Python); see Soft Actors for the full field list. The members that carry soft-skinned-specific requirements:
| Member | Requirement inside a soft-skinned actor |
|---|---|
hasGravity | Must be false. Gravity is driven by SoftSkinnedActorParams.hasGravity. |
hasInertia / hasStress | Select the unposed side of the energy formulation. Must not both be true here and on the parent. |
shape | Tetrahedral mesh with constrained nodes baked in — the nodes pinned to the skeleton (always required; see Architecture). |
worldFromLocal | Forced to identity on creation; author the mesh in the skeleton rest frame. |
name | For a blended skin, must match the nested soft actor's blending data in the skin mesh. |
material | Each region may use its own model (e.g. Neo-Hookean) and parameters (density, Young's modulus). |
Blended Skin
The third flavor uses a blended skin: a single overarching triangle mesh that presents one seamless surface for the whole actor — deforming where it is backed by a soft region and rigidly following the skeleton everywhere else. This blended mesh, not the individual soft surfaces, is the colliding surface, so contact faithfully represents the actor's outer shape.
Attach it by setting skin (an ArticulatedSkinParams) on skeletonParams; the skin mesh must include blending data linking each soft region by name. The underlying soft volumes still couple to the skeleton the usual way — via softAttachLinks or skinning data — and always carry constrained nodes.
- C++
- Python
skeletonParams.skin = ArticulatedSkinParams{
.shape = skinShape, // triangle mesh with per-nested-soft-actor blending data
.layer = "Skin",
};
skeleton_params.skin = mochi.ArticulatedSkinParams(
shape=skin_shape, # triangle mesh with per-nested-soft-actor blending data
layer="Skin",
)
See ArticulatedSkinParams for the field reference.
Working with Soft-Skinned Actors at Runtime
Creating a soft-skinned actor returns the top-level articulated actor, so the full articulated runtime API — GetNumDofs, GetArticulatedShapeInfo, GetArticulatedPose, SetArticulatedJointVelocities, and so on — applies directly. The returned top-level actor also provides handles to its nested link actors and nested soft actors. Query support depends on the target actor and configuration.
Nested Link Actors and Nested Soft Actors
Enumerate the nested link actors and nested soft actors through the parent Actor and look them up with GetActor / get_actor.
- C++
- Python
auto links = actor->GetNestedLinkActors(error);
for (auto handle : links) {
Actor* link = scene->GetActor(handle); // link->GetType() == ActorType::Rigid
}
auto softs = actor->GetNestedSoftActors(error);
for (auto handle : softs) {
Actor* soft = scene->GetActor(handle); // soft->GetType() == ActorType::Soft
}
for handle in actor.get_nested_link_actors():
link = scene.get_actor(handle)
assert link.get_type() == mochi.ActorType.RIGID
for handle in actor.get_nested_soft_actors():
soft = scene.get_actor(handle)
assert soft.get_type() == mochi.ActorType.SOFT
Queries and Contact
Register queries for nested soft actor data, such as NodePositions,
ElementsDeformationGradient, and ElasticEnergy, on nested soft actors. When
no blended skin is configured, register contact queries, such as
ContactPoints and TotalContactForce, on nested soft actors as well. With a
blended skin, register contact queries on the
articulated actor; contact is reported on the skin surface rather than on the
individual nested soft actors. When link contact is enabled, nested link actors
may also support contact queries. Use is_query_supported to determine which actor supports each query.
The following example does not configure a blended skin, so it registers a
TotalContactForce query on the nested soft actor. Call scene.step
before reading the query result. Here, ball is a rigid actor placed within
reach of the swinging nested soft actor:
soft_actor = scene.get_actor(actor.get_nested_soft_actors()[0])
force_query = soft_actor.register_query(mochi.QueryType.TOTAL_CONTACT_FORCE)
scene.step(1.0 / 60.0)
force = soft_actor.get_contact_force_from_actor_world(ball) # net force from the ball
soft_actor.cancel_query(force_query)
The C++ equivalents are RegisterQuery, GetContactForceFromActorWorld, and CancelQuery.
Examples
Soft-Skinned Double Pendulum: attaches a tetrahedral soft rod via constrained nodes and softAttachLinks, with unposed elasticity.
- Python —
uv run --no-project superdex_physics/examples/example_articulations_soft_skinned_double_pendulum.py - Prefab —
superdex_physics/assets/samples/articulations_soft_skinned_double_pendulum.mochi_scene
Related Concepts
- Articulated Actors — The skeleton component of a soft-skinned actor.
- Soft Actors — The deformable skin component.
- Contact Filtering — Layer-based contact configuration for isolating skeleton and skin.
- Pose Controller — Implicit PD control for driving the articulated skeleton toward joint-space or Cartesian link targets.
- Solvers — Newton solver configuration for tuning convergence.