Retargeting¶
Remap the 15 visemes onto another rig's blendshape names and weights — ARKit, Rhubarb, Preston-Blair, VRM, CC4, Ready Player Me, and more — via the built-in presets or a custom mapping. See the Retargeting guide for the preset tables and worked examples.
openfacefx.retarget
¶
Retarget viseme channels onto another rig's blendshape convention.
A mapping sends each Oculus-15 viseme to one or more target shapes with a weight scale::
{"PP": [("mouthClose", 0.9), ("mouthPressLeft", 0.35)], ...}
retarget resamples on the union of the contributing channels' key times
(linear interpolation, matching intended playback), sums the scaled
contributions per target, clamps to [0, 1], and returns a new FaceTrack.
Presets for common rigs live in PRESETS; they are plain data — copy and
tweak for your character.
A rig that lacks some of a preset's target shapes can pass available= (an
iterable of the shapes it actually has). Any mapped target not in that set is
rerouted through a per-preset PRESET_FALLBACKS table so its weight
redistributes instead of vanishing (e.g. a tongue-less ARKit rig sends
tongueOut to a small jawOpen); a target with no fallback rule is
dropped. Provenance and the fallback tables: docs/retargeting.md.
Mapping = Dict[str, Sequence[Tuple[str, float]]]
module-attribute
¶
PRESETS: Dict[str, Mapping] = {'arkit': _ARKIT, 'rhubarb': _RHUBARB, 'preston_blair': _PRESTON_BLAIR, 'vrm': _VRM, 'vrm0': _VRM0, 'cc4': _CC4, 'readyplayerme': _READY_PLAYER_ME}
module-attribute
¶
PRESET_FALLBACKS: Dict[str, Mapping] = {'rhubarb': {'G': (('A', 1.0),), 'H': (('C', 1.0),), 'X': (('A', 1.0),)}, 'arkit': {'tongueOut': (('jawOpen', 0.2),)}}
module-attribute
¶
retarget(track: FaceTrack, mapping: Mapping, available: Optional[Iterable[str]] = None, fallbacks: Optional[Mapping] = None) -> FaceTrack
¶
Return a new FaceTrack with channels renamed/combined per mapping.
Source channels absent from the mapping are dropped (deliberately: a rig that lacks a shape should not receive its weight).
available is an iterable of the target shape names the rig actually has;
any mapped target outside it reroutes through fallbacks (a
{target: [(replacement, scale), ...]} table, typically
PRESET_FALLBACKS[name]) so weight redistributes rather than dropping
silently. available=None (default) disables filtering and is identical
to a plain rename/combine.
Source code in src/openfacefx/retarget.py
rename_only(prefix: str = '', names: Dict[str, str] = None) -> Mapping
¶
Build a 1:1 mapping that renames each viseme, e.g. viseme_PP.