← Journal

Replacing Copies with Instances in Maya

Replacing Copies with Instances in Maya

Sometimes when working in Maya, artists can find themselves duplicating objects without considering how heavy the scene can become. As tech artists, we often need to process scenes to optimize them for performance, and one of the most effective optimizations is converting duplicate copies into instances.

The Problem

When artists duplicate objects in Maya using Ctrl+D, they create full copies of the geometry. If you have 100 trees in a scene, that's 100 separate meshes eating up memory. Instances, on the other hand, reference the same geometry data, dramatically reducing memory usage.

Building a Duplicate Finder Class

Here's how we can build a Python tool to automatically find and replace duplicates with instances:

Step 1: Build a DefaultDict of Lists

We'll use Python's defaultdict to group meshes by their vertex positions. The key insight is that identical meshes will have identical vertex positions.

from collections import defaultdict
import maya.cmds as cmds

class DuplicateFinder:
    def __init__(self):
        self.duplicates = defaultdict(list)
    
    def get_vertex_positions(self, mesh):
        """Get all vertex positions as a hashable tuple"""
        vertices = cmds.xform(
            f"{mesh}.vtx[*]",
            query=True,
            worldSpace=True,
            translation=True
        )
        # Round to avoid floating point precision issues
        rounded = [round(v, 4) for v in vertices]
        return tuple(rounded)
    
    def find_duplicates(self):
        """Find all duplicate meshes in the scene"""
        all_meshes = cmds.ls(type='mesh', long=True)
        
        for mesh in all_meshes:
            # Skip intermediate objects
            if cmds.getAttr(f"{mesh}.intermediateObject"):
                continue
            
            # Get transform node
            transform = cmds.listRelatives(mesh, parent=True, fullPath=True)[0]
            
            # Use vertex positions as key
            positions = self.get_vertex_positions(mesh)
            self.duplicates[positions].append(transform)
        
        # Filter out unique meshes
        return {k: v for k, v in self.duplicates.items() if len(v) > 1}

Step 2: Convert to Instances

Once we've found the duplicates, we can replace them with instances:

def convert_to_instances(self, duplicates_dict):
    """Convert duplicate meshes to instances"""
    for positions, transforms in duplicates_dict.items():
        # Keep the first one as the master
        master = transforms[0]
        
        # Convert the rest to instances
        for duplicate in transforms[1:]:
            # Get transform data
            pos = cmds.xform(duplicate, query=True, worldSpace=True, translation=True)
            rot = cmds.xform(duplicate, query=True, worldSpace=True, rotation=True)
            scale = cmds.xform(duplicate, query=True, worldSpace=True, scale=True)
            
            # Create instance
            instance = cmds.instance(master)[0]
            
            # Apply transform
            cmds.xform(instance, worldSpace=True, translation=pos)
            cmds.xform(instance, worldSpace=True, rotation=rot)
            cmds.xform(instance, worldSpace=True, scale=scale)
            
            # Delete the duplicate
            cmds.delete(duplicate)
            
        print(f"Converted {len(transforms)-1} duplicates to instances of {master}")

Usage

# Find and convert duplicates
finder = DuplicateFinder()
duplicates = finder.find_duplicates()
finder.convert_to_instances(duplicates)

Results

On a typical environment scene with 500+ objects, this can reduce memory usage by 60-70% and significantly improve viewport performance. The key is identifying truly identical meshes - even a single vertex difference will prevent the conversion.

Caveats

  • This only works for meshes with identical topology
  • Materials and UV sets must also match
  • Consider freezing transforms before running this tool
  • Always backup your scene first!

This tool has saved countless hours in optimization passes and is now part of our standard scene cleanup pipeline.