Skip to main content

VRChat Avatar Transfer Conversion Details

A list of conversions that VRChatAvatarTransfer performs on a VRChat avatar prefab. This page describes what happens internally when you press the Convert button on Use VRChat Avatars.

Overview

Drag the prefab you want to convert into the VRChat Avatar Transfer window, which you can open from Tools > VRChat Avatar Transfer > Transfer. This prefab is not modified.

  1. Convert VRChat dynamic bones (PhysBone) to VRM10 SpringBone
  2. Convert VRChat Constraint components to Unity standard Constraints
  3. Duplicate the FX layer AnimatorController of VRCAvatarDescriptor and apply it to the root Animator
    • Replace VRCAvatarParameterDriver in the duplicated FX with AvatarParameterDriver
    • Replace VRCAnimatorTrackingControl in the duplicated FX with AvatarAnimatorTrackingControl
  4. Add a component to the root depending on the avatar type
    • VRCFT v2-compatible avatar (FX has FT/v2/ parameters) → add VRCFTAvatar
    • Otherwise → add VRCAvatar and port viseme lip sync / blink / the Expressions menu
  5. Strip VRChat-only components (VRCAvatarDescriptor, PipelineManager) and Missing Scripts

1. PhysBone → VRM10 SpringBone

Source (VRChat)Target (VRM10)
VRCPhysBoneVRM10SpringBoneJoint (attached to each bone along the chain)
VRCPhysBoneColliderVRM10SpringBoneCollider
(none)Adds Vrm10Instance to the root and registers entries in SpringBone.Springs / ColliderGroups

Key characteristics:

  • Because the leaf joint falls outside the VRM10 SpringBone simulation range, a <bone-name>_end Transform is generated as a child of the leaf and registered as the tail.
  • Branching bone structures are supported. Branch points are included as the terminal joint of the parent chain, and each child starts a new Spring from the head.
  • Colliders are grouped under a dedicated ColliderGroup GameObject placed directly under Vrm10Instance.
  • Vrm10Instance.UpdateType is set to Update (with LateUpdate, results would lag by one frame after the animation).

Parameter Mapping

VRC PhysBoneVRM10 SpringBone JointConversion
pull × springm_stiffnessForcepull * (1 - spring) * 4
pull × springm_dragForceclamp01(1 - pull * spring)
gravitym_gravityPower / m_gravityDirabs(gravity) * 20, direction from sign
radiusradiusCurve)m_jointRadiusAs-is
limitTypem_anglelimitTypeAngle→Cone / Hinge→Hinge / Polar→Spherical
limitRotation (× each Curve)m_limitSpaceOffsetEuler angles converted to Quaternion
maxAngleXm_pitchDegrees→Radians
maxAngleZm_yawApplied only when Spherical

Limitations

  • Colliders: Colliders with shape Plane and insideBounds=true are skipped because VRM10 SpringBone has no matching type.
  • immobile: Ignored because there is no equivalent concept in VRM10 SpringBone.
  • VRC stiffness (Advanced): Not included in the mapping above.

2. VRC Constraint → Unity Constraint

Source (VRChat)Target (Unity standard)
VRCParentConstraintParentConstraint
VRCPositionConstraintPositionConstraint
VRCRotationConstraintRotationConstraint
VRCScaleConstraintScaleConstraint
VRCAimConstraintAimConstraint
VRCLookAtConstraintLookAtConstraint

weight / constraintActive / locked, plus each source's transform and weight, per-axis affect flags (such as AffectsPositionX), *AtRest / *Offset, and Aim-family fields (aimVector / upVector / worldUpType) are carried over to the extent possible.

Limitations

  • FreezeToWorld: Skipped because there is no equivalent in Unity Constraints.
  • Non-self TargetTransform: Skipped because Unity Constraints always control the host GameObject itself.

3. Applying the FX AnimatorController

The converter looks up the FX layer in VRCAvatarDescriptor.baseAnimationLayers, duplicates the assigned AnimatorController into the output folder, and then assigns it to Animator.runtimeAnimatorController on the root. The original FX controller asset is not modified.

This allows expression and toggle-style animations that worked on VRChat to play back from VirgoMotionStudio as well.

note

If the FX layer is empty, nothing is applied. If there is no Animator on the root, the application step is also skipped. The duplicated FX controller is saved to the output folder as <avatar-name>.FX.controller.

3-1. VRCAvatarParameterDriver → AvatarParameterDriver

The VRCAvatarParameterDriver (StateMachineBehaviour) attached to each State / StateMachine of the duplicated FX controller is replaced with AvatarParameterDriver, which does not depend on the VRChat SDK.

  • The Set / Add / Random / Copy ChangeType values are carried over as-is.
  • Parameters such as value / valueMin / valueMax / chance / sourceMin / sourceMax / destMin / destMax / convertRange are also copied with the same meaning.
  • The localOnly field is kept only for compatibility; since this package has no concept of network sync, it is always applied.
  • When Copy's convertRange is true, [sourceMin, sourceMax] is linearly mapped to [destMin, destMax].
  • Parameters are applied on OnStateEnter, so the firing timing matches VRChat.

3-2. VRCAnimatorTrackingControl → AvatarAnimatorTrackingControl

Likewise, VRCAnimatorTrackingControl on States / StateMachines is replaced with AvatarAnimatorTrackingControl.

Carried-over targets
trackingHead / trackingHip
trackingLeftHand / trackingRightHand
trackingLeftFoot / trackingRightFoot
trackingLeftFingers / trackingRightFingers
trackingEyes / trackingMouth

The TrackingType (NoChange / Tracking / Animation) of each target is mapped as-is.

4. Adding Avatar Components

The converter inspects the Animator parameters of the duplicated FX controller and switches which component is added to the root depending on the avatar type.

4-1. Detecting VRCFT v2-Compatible Avatars

If even one parameter name in the FX controller starts with FT/v2/, the avatar is treated as one built with Adjerry91's VRC Face Tracking Template v2.

  • VRCFT v2-compatible: Adds VRCFTAvatar to the root. It converts ARKit's 52 blendshape values into FT/v2/... parameters and writes them directly to the Animator. Because the FT template drives expressions within the Animator, the subsequent viseme / blink / Expressions porting is not applied.
  • Otherwise: Adds VRCAvatar to the root. VRCAvatar is a bridge that drives the FX controller's Viseme / Voice parameters from ARKit, and it ports the following information before VRCAvatarDescriptor is removed.

4-2. Viseme Lip Sync (VRCAvatar)

Only when VRCAvatarDescriptor.lipSync is VisemeBlendShape, the following vowel blendshapes are ported from VisemeSkinnedMesh and VisemeBlendShapes to VRCAvatar.

  • The 5 vowels aa / E / ih / oh / ou
  • sil has no dedicated blendshape, so it is treated as -1 (mouth closed)
  • Consonant visemes are out of scope (only the 5 vowels estimated from ARKit are supported)

If none of the vowels are found, porting is skipped and VRCAvatar falls back to driving the Viseme/Voice parameters via the Animator.

When VRCAvatarDescriptor.enableEyeLook is enabled and customEyeLookSettings.eyelidType is Blendshapes, the blink blendshape is ported to VRCAvatar.

  • Only the index of eyelidsBlendshapes[0] (the Blink slot) is used
  • "Looking Up" / "Looking Down" are out of scope (gaze is driven by the Eye bones)

5. Stripping VRChat-Only Components

Because these components cannot be referenced outside VRChat, they are removed from the entire hierarchy:

  • VRCAvatarDescriptor
  • PipelineManager

In addition, third-party Missing Scripts left on the original VRChat avatar (components that do not exist in this project) are also removed, because SaveAsPrefabAsset refuses to save a prefab that contains Missing Scripts.