🗿 Static Objects

A Static object group holds meshes that do not deform in the solver: colliders, ground planes, mannequins, props, anything that should influence the simulation without being simulated itself. Static groups share the same panel, transfer, and bake flow as Solid / Shell / Rod groups, but with a smaller material-parameter surface and two exclusive ways to drive motion.

This page centralizes everything specific to the Static type:

Creating a Static Group

Click Create Group on the Dynamics Groups panel, then change Type to Static. The group card updates to reflect the Static surface:

  • The Assigned Objects list accepts only meshes (curves are rejected because they only make sense for Rod groups).

  • The pin region is relabeled Transform (with a driver icon replacing the pin icon). No vertex-group pins are possible on a Static group; instead this box holds per-object Static ops.

  • The Material Params box collapses down to just Friction and the Contact rows (see Contact parameters).

  • The default overlay color is blue (0, 0, 0.75).

Everything else (duplicating the group, per-object Include checkboxes, removing objects, deleting the group) works the same as for Solid/Shell/Rod groups. See Object Groups for the shared UI surface.

Moving a Static Object

Static meshes do not deform in the simulation, but they can still translate, rotate, scale, or follow an armature pose over time. There are three mutually exclusive ways to drive that motion:

  1. Static ops. UI-assigned Move By / Spin / Scale entries edited per assigned object inside the group’s Transform sub-box. You give each op a time range and a delta, axis, or factor.

  2. Blender transform keyframes. The usual way you animate an object in Blender: select it, hit I on a frame, and pick Location, Rotation, or Scale. Any keyframes you set on the object’s own transform channels get picked up automatically. The add-on samples the world transform at each keyframe and ships the track, including Bezier handles, so eases you see in the Graph Editor carry over to the simulation.

  3. Captured deformation. For objects whose mesh shape changes over time, an Armature modifier driven by a posed rig, a Lattice or Mesh Deform cage, animated Shape Keys, and the like, use the Capture Deformation button to record the animation onto the collider.

Warning

Only one source of motion per object at a time. If an assigned Static mesh has Blender transform keyframes, the add-on uses those and ignores that object’s Static ops list. The UI flags this with the label “Object has Blender keyframes; these ops will be ignored” above the ops list. A captured deformation takes priority over both: while a deformation cache is present for the object, its Static ops and transform keyframes are ignored, because the cache already includes any rigid parent motion in the recorded vertex positions.

Note

Shape Keys and other mesh-level animation are only honored through Capture Deformation. A Static mesh with Shape Key animation will not move in the solver unless you click Capture Deformation, and the add-on will refuse to upload it with an explicit error.

Use Static ops when the motion is scripted and easy to describe with a few time ranges: a sliding floor plate that moves from A to B between frame 30 and 60, a spinning turntable, a shrinking platform. Use Blender keyframes when the motion lives in Blender’s own timeline already at the object level: a prop animated by hand in the Graph Editor, a collider parented to a Camera. Use Captured deformation when the motion is inside the mesh (a posed armature, a deforming lattice, a Shape Key), not just on the object transform.

The Transform Sub-Box

On a Static group, the region that would be Pins on other group types is relabeled Transform. Expanding it shows:

  1. A per-assigned-object list: the same object list as the group card above. Which object you select here determines which object’s static-ops list is being edited in the box below.

  2. A warning row: visible only when the selected object has Blender transform fcurves; tells you its ops will be ignored.

  3. The static-ops list: Move By / Spin / Scale entries with + (add menu), (remove), and up/down reorder buttons.

  4. The per-op editor with fields for the active row:

    • Start / End: Blender frames; the op is active across the closed range.

    • Transition: Linear or Smooth (smoothstep).

    • Delta (m): Move By only; (x, y, z) translation in world units.

    • Axis / Angular Velocity (°/s): Spin only; pivots around the object’s origin.

    • Factor: Scale only; uniform scale multiplier around the object’s origin.

Ops compose in list order: if you stack a Move By and a Spin whose time ranges overlap, the object translates and rotates simultaneously inside the overlap. Outside every op’s time range the object rests at its un-modified transform (the pose it was at when you assigned it to the group).

Static Ops Reference

Op

Fields

Pivot

Notes

MOVE_BY

delta (x, y, z)

N/A

Translate the whole object by delta over the range.

SPIN

spin_axis (x, y, z), spin_angular_velocity

Object origin

Rotate around spin_axis at °/s.

SCALE

scale_factor

Object origin

Uniform scale; < 1 shrinks, > 1 grows.

Common fields on every op: frame_start, frame_end, transition (LINEAR / SMOOTH), and show_overlay (toggle the viewport preview).

Armature-Driven Static Objects

When a Static mesh moves because of something inside the mesh, typically an Armature modifier on a body model, but also a Lattice or Mesh Deform cage, animated Shape Keys, or similar setups, the add-on cannot pick up that motion from object keyframes alone. You have to record the animation onto the collider using the Capture Deformation button.

When you assign such an object to a Static group, the panel recognizes the animation and the button row activates with a hint underneath it:

Dynamics Groups panel showing a Static group "Mannequin" with the deforming Cube assigned. The Capture Deformation button is enabled, the Clear Deformation Cache button is grayed out, and a hint reads "Deforming modifier detected; capture to encode".

The Static group row immediately after assigning an animated mesh. Capture Deformation is enabled; Clear Deformation Cache is grayed out because nothing has been recorded yet; the hint “Deforming modifier detected; capture to encode” tells you what to do next.

The Capture Deformation Button

Close-up of the Capture Deformation button highlighted with a red box. The button sits to the left of the Clear Deformation Cache button on the row directly under Bake Animation / Bake Single Frame.

The Capture Deformation button. Press it after assigning an animated mesh to a Static group, and press it again any time the animation changes.

Click Capture Deformation to record the animation. The add-on plays through the action and stores the per-frame shape of the mesh on the object. After the recording finishes, the panel replaces the hint with the number of frames captured:

Same panel after Capture Deformation finished. The hint label is replaced with "Deform cache: 60 frame(s)" and the Clear Deformation Cache button is now enabled.

After capturing. The hint is replaced with Deform cache: 60 frame(s), and Clear Deformation Cache is now enabled.

Warning

You must press Capture Deformation again whenever the animation changes. The recording is a snapshot taken at the moment you clicked the button. It does not update on its own. If you tweak the armature pose, edit the action’s keyframes, change the modifier stack, edit the rest mesh, or alter parent or constraint chains, the recording is now out of date and the solver will keep using the old motion. Re-press Capture Deformation before the next Transfer so the simulation sees the current animation.

The Clear Deformation Cache Button

Close-up of the Clear Deformation Cache button highlighted with a red box. The button sits to the right of the Capture Deformation button.

Clear Deformation Cache discards the recording for the selected object. The panel returns to the pre-capture state so Capture Deformation is the only live button on the row.

Use Clear Deformation Cache when you no longer want the recorded animation, for instance when you turn the object back into a rigid collider, or when you have edited the mesh in a way that changed its vertex count and need to start over.

Contact Parameters

Static groups expose only the contact-relevant subset of material parameters. Everything deformation-related (density, Young’s modulus, Poisson ratio, bend, shrink, strain limit, inflate, stitch, plasticity, velocity overwrite) is hidden.

UI label

Python / TOML key

Default

Description

Friction

friction

0.5

Coulomb friction coefficient between this mesh and other groups.

Contact Gap

contact_gap

0.001

Absolute contact gap distance, in Blender units.

Contact Offset

contact_offset

0.0

Absolute contact offset, in Blender units.

Use Group Bounding Box Diagonal

use_group_bounding_box_diagonal

True

When true, contact distances are ratios of the group’s bbox diagonal.

Contact Gap Ratio

contact_gap_rat

0.001

Contact gap as a fraction of the group’s bounding-box diagonal.

Contact Offset Ratio

contact_offset_rat

0.0

Contact offset as a fraction of the group’s bounding-box diagonal.

See Material Parameters for the full story on absolute vs ratio contact gap, and Static in the shipped material-profile TOML for a minimal example.

Note

Static groups have no collision windows. The Collision Active Duration Windows control, which mutes contact on dynamic objects for chosen frame ranges, is not exposed for Static groups; their meshes collide for the entire timeline. If you need a Static collider to come and go mid-shot, animate its visibility, drive it out of the way with a Static op, or use a per-collider Active Duration on an Invisible Collider instead.

Baking Behavior

Bake Animation walks through active groups in slot order (object_group_0object_group_31) and processes every assigned object. Static groups are included: if a Static collider was driven by Blender transform keyframes and therefore carried a ContactSolverCache modifier and .pc2 file after a Fetch, both are cleaned up during bake even though the Static object itself has no simulated deformation. Bake never touches object-level transform fcurves, only the per-frame PC2 data.

If the Static object is driven by Static ops (no fcurves), there is nothing to bake on it; the motion lives on the solver side, and re-running Transfer + Run produces the same motion deterministically. Bake is only meaningful for Static objects that have fetched per-frame vertex data.

See Baking Animation for the full bake flow.

Snap and Merge

Static objects are valid endpoints for Snap and Merge. The common case is snapping a Shell garment to a Static mannequin so the cloth’s nearest vertices touch the body before the solve begins; the pair is then registered as a merge pair for cross-group stitching, with the contact gap picked from the Shell ↔ Static pairing. See Snap and Merge for the operator and its options.

Python / MCP API

Create a Static group the same way as any other:

from bl_ext.user_default.ppf_contact_solver.ops.api import solver

floor = solver.create_group("Floor", type="STATIC")
floor.add("Ground")
floor.param.friction = 0.8

Static ops are not yet on the fluent solver surface; drive them either through Blender’s raw operators or through the MCP handlers.

Blender operators (one op per call; the group_index is the slot from 0 to 31, not the UI display number):

import bpy

bpy.ops.object.add_static_op(group_index=0, op_type="MOVE_BY")
bpy.ops.object.remove_static_op(group_index=0)
bpy.ops.object.move_static_op(group_index=0, direction=-1)  # reorder up

The operators edit whichever assigned object is currently selected in the group’s assigned-objects list; set group.assigned_objects_index first to pick a specific object.

MCP handlers (identify the object by name, not by list index):

add_static_op(group_uuid, object_name, op_type,
              frame_start=..., frame_end=..., transition="LINEAR",
              delta=[x,y,z] | spin_axis=[x,y,z], spin_angular_velocity=deg_per_s
                                                | scale_factor=f)
remove_static_op(group_uuid, object_name, index)
list_static_ops(group_uuid, object_name)
clear_static_ops(group_uuid, object_name)

See the MCP Tool Reference for the full signatures.

For the Blender-keyframe route there is no add-on-specific API at all. Key the object’s transform in Blender as you normally would (I in the viewport, the Graph Editor, constraints baked to fcurves, or obj.keyframe_insert(data_path="location", frame=...) from Python) and the encoder picks it up at Transfer time.

Under the hood

Mutual exclusion

The encoder checks each Static object in order:

  1. If the object has a populated deformation cache (a PC2 written by Capture Deformation), emit it as static_deform_animation and override the per-frame transform with identity. The cache already includes any rigid parent motion in the per-vertex stream, so emitting transform_animation or static_ops alongside it would double-count.

  2. Else, if the object is deforming (its modifier stack would move vertices and the depsgraph confirms it does), refuse the upload with an explicit error. Shipping it as a rest-pose collider would silently mislead the artist.

  3. Else if obj.animation_data.action has any transform fcurve, extract sparse (time, translation, quaternion, scale) keyframes plus per-segment Bezier-handle data and send them as transform_animation.

  4. Else if the matching AssignedObject has a non-empty static_ops collection, serialize those ops (frames converted to seconds using the scene FPS, axes swapped into solver orientation) as static_ops.

  5. Else send the object with no animation: a rigid, unmoving collider.

The first match wins; the other channels are dropped. This is why the UI can warn “these ops will be ignored” as soon as fcurves appear on the object, and why Capture Deformation is required (rather than just helpful) for an Armature-driven collider.

Time conversion

Frame values in the UI and MCP handlers are 1-based Blender frames. The encoder converts to seconds as (frame - 1) / fps, using the scene’s effective FPS (scene.render.fps or the add-on’s frame_rate override; see Scene Parameters).

Assigned-object wiring

Static ops live on the AssignedObject record, not on the group or the mesh. That is why the Transform sub-box shows one ops list per selected object; deleting an object from the group (or unchecking its Include box) drops its ops from the next transfer without touching any other object in the group.