Renaming objects in Maya (beginner)

 

[Download Script]

 

If you've ever done any scripting in Maya or even tried to clean up you scene a bit I'm sure you've run into some of Maya's quirks with how it handles object names. Specifically it gets real tricky when dealing with nodes that share duplicated names and renaming whole chains of nodes.

To help demystify what's going on I thought I'd run through the development of a 'rename' tool that will cover ways I've found to resolve these issues.

 

Node names

Because the node name is your primary handle to access objects and their properties it becomes very important that the reference you make to these objects is reliable. So with all tools I develop I make explicit measures to access and refer to the nodes through their 'long' name ( or full path )

i.e. '|pCube1|pCube2|locator1' rather then just 'locator1'

 

"All my tools run off the selection so I don't need to worry about names!" – Joe Scripter

This concept seems harmless enough and is alao somewhat adopted by a lot of Maya's prepackaged tools. However, in general, this is a pretty bad practice to follow.

There are ways you can build a tool and it's interface without falling into the trap of relying on selection lists while still giving the artists intuitive control.

 

What's the goal exactly?

We need a tool that can take a selection (or just list of objects) and rename them to a new unique name per node.

  • Collect a list of objects to process (rename)
  • Sort this this of objects based on each objects depth in the hierarchy
    • You have to process the objects from deepest to most shallow because if you rename or modify a parent object before you process it's children the long path to the child object becomes invalid

Lets say you have two objects 'pCube1' and 'pCube2'  where 'pCube1' is currently a parent of 'pCube2'

You attempt to rename 'pCube1' to 'newCube1' and 'pCube2' to 'newCube2'

The original long path to 'pCube2' was '|pCube1|pCube2' but after 'pCube1' was renamed it becomes '|newCube1|pCube2'

Therefore if you try to rename '|pCube1|pCube2' after renaming 'pCube1' Maya will error out unable to find the 'pCube2' object

  • Derive the new mapping order from sorted list (i.e. {0,1,2,3,4,5} became {4,3,1,0,5,2})
  • Step through mapping and rename each index in the unsorted object list

This can all be broken down into 3 distinct types of procedures

  1. Utility – general global procedures that can also be helpful to other tools
  2. Core – the 'guts' of the tool that handles the principle operations
  3. UI – the User Interface for the non scripted interaction with the tool

 

What could go wrong?

  • Sorting and reprocessing the object list could be slow when dealing with large number of objects (i.e. > 1000)
  • Sorting could be destructive to auto object incrementing (depending on the type of sort)

 

Enough with the worrying lets get started…


Utility procedures

 

getSelection

 

global proc string[] getSelection()
{
	return (ls("-selection", "-long", "-flatten"));



}

 

Description:

While none of my core or utility procedures rely on switching and querying selection I do a lot of interfacing with the user through their selection. So a unified selection query procedure is important. The long and flatten flags returns the objects with long paths and flattened individualized list respectively.

i.e.

 

{"locator1", "pCube1.uv[0:2]"}

 

would return as

 

{"|pCube1|pCube2|locator1", "|pCube1.uv[0]", "|pCube1.uv[1]", "|pCube1.uv[2]"}

 

 

 

renameLong

 

global proc string renameLong(string $object, string $newName)
{
	return longNameOf(rename($object, $newName));
}

 

Description:

As the default rename procedure doesn't always return the long version of the object we'll create this wrapper procedure to ensure it does.

 

 

 

stringArrayFind

global proc int stringArrayFind(string $item, string $array[])
{
	for($i = 0; $i < size($array); $i++) if($array[$i] == $item) return $i;
	return -1;
}

Description:

Pretty routine procedure that returns the first index of a given 'item' in a string array.

 

Details:

This procedure will be called constantly throughout and therefore should be as efficient as possible.

 

 

getStringArrayMapping

global proc int[] getStringArrayMapping(string $unmodified[], string $modified[])
{
    int $returnArray[] = {};

    string $searchList[] = $unmodified;

    for($i = 0; $i < size($modified); $i++)
    {
        int $foundIndex = stringArrayFind($modified[$i], $searchList);
        $returnArray[$i] = $foundIndex;
        $searchList[$foundIndex] = "";
    }

    return $returnArray;
}

Description:

This procedure does a nice little trick of giving you the index mapping between $unmodified and $modified string arrays.  What this basically means is it gives you how the order of strings has changed between the two given arrays.

Why is this needed?

If you reorder an array of objects there's no way to tell what order they were originally in and thus destroying the users specific selection order.  So we create a list of indexes that provide a 'mapping' between the original list and the modified list.

i.e.

If you sent this procedure a string array

$unmodified[] = {"dog", "mouse", "cat"}

and a string array

$modified[] = {"mouse", "cat", "dog"}

This procedure would return the index (int) array

$returnArray = {1, 2, 0}

as index '1' represented "mouse", index '2' represented "cat" and index '3' represented "dog" in the $unmodified array.

 

Details:

The following line is added for performance. Setting the found index to a zero length string or "" will speed up the string comparison inside the 'stringArrayFind' procedure:

$searchList[$foundIndex] = "";

 

 

Core procedures

 

renameObjects

 

global proc string[] renameObjects(string $unsortedObjs[], string $newName)
{
	string $sortedObjs[] = sort($unsortedObjs);
	int $mapping[] = getStringArrayMapping($unsortedObjs, $sortedObjs);

	for($iMap = ( size($mapping) - 1 ); $iMap >= 0; $iMap--)
	{
		int $eachIndex = $mapping[$iMap];
		$unsortedObjs[$eachIndex] = renameLong($unsortedObjs[$eachIndex], $newName);
	}

	return $unsortedObjs; // return renamed objects
}

 

Description:

This is the entry point for the rename routine and so it handles the main iteration over the specified objects.

 

Details:

Here we utilize Maya's default alphnumeric 'sort' routine.  While not exactly the best result, it is fast and gets us on the way to renaming objects quickly:

string $sortedObjs[] = sort($unsortedObjs);

 

Lets pass this newly sorted list through our string array mapper and see how it was reordered:

int $mapping[] = getStringArrayMapping($unsortedObjs, $sortedObjs);

 

Now we have the mapping we can step backwards through the mapping and process each object.  We step backwards because we want to rename objects at the deepest level in the hierarchy first:

for($iMap = ( size($mapping) - 1 ); $iMap >= 0; $iMap--)

 

For each of the objects get the current index according to the index map:

int $eachIndex = $mapping[$iMap];

 

Then rename the object at the identified index and reassign it back into the original $unsortedObjs array:

$unsortedObjs[$eachIndex] = renameLong($unsortedObjs[$eachIndex], $newName);

 

 

UI procedures

 

renameObjectsUI

global proc string[] renameObjectsUI()
{
	string $result = promptDialog(
	"-title", "Enter New Name",
	"-message", "Name:",
	"-text", "myObjects",
	"-button", "OK", "-button", "Cancel",
	"-defaultButton", "OK", "-cancelButton", "Cancel",
	"-dismissString", "Cancel");

	if ($result == "OK") 
	{
		string $newName = promptDialog("-query", "-text");
		return renameObjects(getSelection(), $newName);
	}
	return {};

}

Description:

Here we utilize the quick 'promptDialog' UI procedure.  This dialog simplifies the required scripting when all you need is a quick text input.  If you need anything more sophisticated then you'd have to use a regular window and form layout.

 

Details:

The following line is what's called once the user presses the OK button:

return renameObjects(getSelection(), $newName);

It just takes the current selection along with the new name and passes it directly to the 'renameObjects' procedure.

 

Thats it!

[Download Script]

Conclusions / next steps?

 

  • This script is a handy quick renamer but doesn't always produce unique names for all objects
  • The UI is over simplified and could be improved with more options
  • Might be good to provide masking or filtering options
  • Numerical increments with good padding options
  • Search and replace through selected objects or whole scene

 

Check out the next stage of this tool in "Renaming objects in Maya (intermediate)"

Leave a Reply

Your email address will not be published. Required fields are marked *