I am trying to export fbx from maya so that the materials assigned to the mesh show up in unreal in a specific order. Maya doesn’t really have a concept of material slot order and so far I could not find a way to wrangle the material order. I’ve seen some threads that refer to adding “skin##” to the material names to force ordering, but was wondering if others have a solution that is name agnostic.
yes. In our case the artists assume that the specific material element will always have a specific material in UE. For example slot one is always brick material, slot two is always metal, slot three is always glass and so on.
I ended up using the “append skin## to material name” method and it seems to work. I was hoping for a more elegant solution.
The ordering of material slot in UE depends on the ordering of objects in Maya outliner… based from what I see… so I am just curious how “append skin## to material name” method works?
To reorder objects in Maya outliner, use the reorder command.
I have my own fbx exporter and I recently wrote some code to do what you described. You can try my Python code below:
import pymel.core as pm
class OutlinerOrdering:
def __init__(self):
# Take a snapshot of outliner ordering on creation
self.scene_hierarchy = []
# Top level transforms ordering
top_level_transforms = pm.ls(assemblies=True)
self.scene_hierarchy.append(top_level_transforms)
# Process the Scene
self._traverse_transforms(top_level_transforms)
def _get_child_transforms(self, obj):
'''GetChildren() without the shape nodes or other thing, just want child of transform type'''
childs = []
if isinstance(obj, pm.nodetypes.Transform):
# this include joints, constraints etc, even though they are not transforms themselves
# they inherit from transform
for child in obj.getChildren():
if isinstance(child, pm.nodetypes.Transform):
childs.append(child)
return childs
def _traverse_transforms(self, objs):
'''Recursive function to traverse the objs hierarchy to fill in the self.scene_hierarchy'''
for o in objs:
childs = self._get_child_transforms(o)
if childs:
self.scene_hierarchy.append(childs)
self._traverse_transforms(childs)
def reorder_objs_in_outliner(self, new_order):
'''
Pass a list of pynode or object names, to order them in outliner
Parameters:
new_order (list): A list of pynodes or strings eg.['aaa', 'bbb']
'''
new_order = reversed(new_order)
for obj in new_order:
if pm.objExists(obj):
pm.reorder(obj, front=True)
def restore_outliner_ordering(self):
for objs in self.scene_hierarchy:
self.reorder_objs_in_outliner(objs)
def restore(self):
'''just a shorter name to restore_outliner_ordering'''
self.restore_outliner_ordering()
You can try it on a test scene:
... Run this line of code first: ...
outliner_ordering = OutlinerOrdering()
... then made some changes to the ordering of objects in the outliner ...
... then run the code below to see how the ordering are restored back to how it was ...
outliner_ordering.restore()
Below is how I use it in my export code:
# eg.
# objs = [aaa, bbb, ccc] # these are pymel objects and are ordered the way I want
# objs = [aaa, ccc, bbb] # I can reorder them
# Reorder object in outliner before export
outliner_ordering = OutlinerOrdering() # Take a snapshot of current outliner ordering
outliner_ordering.reorder_objs_in_outliner(objs) # I pass my objs here to reorder them in outliner
# Fbx export settings
mel.eval('FBXExportSkins -v 1;')
... etc ...
# Export Fbx
pm.select(clear=True)
pm.select(objs)
pm.mel.FBXExport(f=path, s=True)
# Restore outliner ordering after export
outliner_ordering.restore()
# NOTE :
# pSource : unreal.StreamableRenderAsset
sourceMaterials = MeshMaterialHelper.GetMeshMaterials(pSource)
buildSrcMatMapping : collections.Callable[[],dict[unreal.Name, int]] = \
lambda : dict((v,k) for k, v in enumerate(mat for mat in sourceMaterials))