Ben Traje
← Back to maya

Batch Transfer Skin Weights to LODs in Maya using Python

27 Apr 26 (1mo ago)

To save time in weighting different meshes, the workflow is skin a single level of detail (LOD) and transfer the weights other LODs. For games, it is usually the main LODs but for VFX usually the mid or even lores version.

Not limited to different level-of-details (LODS). Any object that needs a rough but quick transfer of weights between objects such as separate objects like props attached to the body.

import maya.cmds as cmds

def batch_transfer_lod_weights(lod_pairs_list):
    print("=== Starting Batch LOD Skin Transfer ===")
    
    # We will use this dictionary to remember source data so we don't query Maya twice for the same LOD0
    source_cache = {}

    # Loop through each pair in your list
    for pair in lod_pairs_list:
        # Safety check: Ensure there are exactly 2 items in the list
        if len(pair) != 2:
            print(f"Warning: Invalid pair {pair}. Needs exactly 2 items (Source, Target). Skipping.")
            continue
            
        source_obj = pair[0]
        target_obj = pair[1]
        
        print(f"\n--- Processing: {source_obj} -> {target_obj} ---")
        
        # 1. Verify target exists first (fail fast)
        if not cmds.objExists(target_obj):
            print(f"Warning: Target '{target_obj}' does not exist. Skipping.")
            continue
            
        # 2. Check if we already processed this source
        if source_obj not in source_cache:
            # If it's not in the cache, verify it exists and grab the skin data
            if not cmds.objExists(source_obj):
                print(f"Warning: Source '{source_obj}' does not exist. Skipping.")
                continue
                
            source_history = cmds.listHistory(source_obj, pruneDagObjects=True) or []
            skin_clusters = cmds.ls(source_history, type="skinCluster")

            if not skin_clusters:
                print(f"Warning: No skin cluster found on '{source_obj}'. Skipping.")
                continue
            
            temp_skin = skin_clusters[0]
            temp_joints = cmds.skinCluster(temp_skin, query=True, influence=True)

            if not temp_joints:
                print(f"Warning: No bind joints found on skin cluster '{temp_skin}'. Skipping.")
                continue
                
            # Save this data to our cache so we can reuse it instantly next time
            source_cache[source_obj] = {
                'skin': temp_skin,
                'joints': temp_joints
            }
            print(f"Data for '{source_obj}' cached.")
        
        # 3. Retrieve the skin and joint data from the cache
        cached_data = source_cache.get(source_obj)
        if not cached_data:
            continue # Skip if caching failed earlier
            
        source_skin = cached_data['skin']
        bind_joints = cached_data['joints']

        # 4. Check if the target is already skinned. Unbind if necessary.
        target_history = cmds.listHistory(target_obj, pruneDagObjects=True) or []
        if cmds.ls(target_history, type="skinCluster"):
            print(f"'{target_obj}' is already bound. Unbinding...")
            cmds.skinCluster(target_obj, edit=True, unbind=True)

        # 5. Bind the target object to the exact same joints
        target_skin_nodes = cmds.skinCluster(
            bind_joints, 
            target_obj, 
            toSelectedBones=True, 
            bindMethod=0, 
            skinMethod=0, 
            normalizeWeights=1
        )
        
        target_skin = target_skin_nodes[0]

        # 6. Copy the skin weights
        cmds.copySkinWeights(
            sourceSkin=source_skin,
            destinationSkin=target_skin,
            noMirror=True,
            surfaceAssociation='closestPoint',
            influenceAssociation='name'
        )

        print(f"Success: '{target_obj}' complete!")
        
    print("\n=== Batch LOD Skin Transfer Complete! ===")


# --- CONFIGURATION ---
# Edit this list with your own mesh names. 
# Each entry must be a list containing exactly two items: ["Source", "Target"]

lod_data_list = [
    ["Eye_LOD0", "Eye_LOD1"],
    ["Eye_LOD0", "Eye_LOD2"],
    
    ["Outfit_LOD0", "Outfit_LOD1"],
    ["Outfit_LOD0", "Outfit_LOD2"],
    
    ["Bell_LOD0", "Bell_LOD1"],
    ["Bell_LOD0", "Bell_LOD2"],
    
    ["MainBody_LOD0", "MainBody_LOD1"],
    ["MainBody_LOD0", "MainBody_LOD2"]
]

# Execute the script
batch_transfer_lod_weights(lod_data_list)