Skip to content

Instantly share code, notes, and snippets.

@jagt
Last active March 22, 2023 07:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jagt/27ccba6e1837606c0e2e2675bccd0bf6 to your computer and use it in GitHub Desktop.
Save jagt/27ccba6e1837606c0e2e2675bccd0bf6 to your computer and use it in GitHub Desktop.
Standalone script for exporting camera animation w/ custom attributes.
'''
export camera standalone
1. put in `C:/Users/<user>/Documents/maya/scripts`
2. open maya and select the camera in outliner
3. menu 'custom->Export Selected Camera'
'''
from maya import cmds
from maya import mel
def make_kwargs(overrides, **kwargs):
kwargs.update(overrides)
return kwargs
def get_first_shape(transform):
'''return its first shape'''
shapes = cmds.listRelatives(transform, c=True, ni=True, shapes=True)
return shapes[0] if shapes else None
def bake_objects_results(objects, **kwargs):
startTime = cmds.playbackOptions(min=True, q=True)
endTime = cmds.playbackOptions(max=True, q=True)
cmds.bakeResults(objects, **make_kwargs(
kwargs,
simulation=True,
# 'hierarchy' here means expand this bake to the hierarchy above/below/both or none
hierarchy="below",
shape=True,
sampleBy=1.0,
disableImplicitControl=True,
time=(startTime, endTime)
))
def constraints_rst(source, target, mo=False):
'''constraints translate/rotation/scale from source to target, returns all constraints'''
constraints = []
# parent constraint is basically translate+rotation
if mo:
constraints.extend( cmds.pointConstraint(source, target, mo=True, weight=1) )
constraints.extend( cmds.orientConstraint(source, target, mo=True, weight=1) )
constraints.extend( cmds.scaleConstraint(source, target, mo=True, weight=1) )
else:
constraints.extend( cmds.pointConstraint(source, target, offset=[0,0,0], mo=False, weight=1) )
constraints.extend( cmds.orientConstraint(source, target, offset=[0,0,0], mo=False, weight=1) )
constraints.extend( cmds.scaleConstraint(source, target, offset=[1,1,1], mo=False, weight=1) )
# not sure why this doesn't work
return constraints
def _setup_camer_fov_expr(group, cameraShape):
# setup attr and expressions
# turns out expression is one of the things that mel can do but python can't
cmds.addAttr( longName='Export_CameraNearClip', attributeType='float', readable=True, keyable=True)
cmds.addAttr( longName='Export_CameraFarClip', attributeType='float', readable=True, keyable=True)
cmds.addAttr( longName='Export_CameraVerticalFOV', attributeType='float', readable=True, keyable=True)
cmds.addAttr( longName='Export_CameraHorizontalFOV', attributeType='float', readable=True, keyable=True)
cmds.expression(s = u'%s.Export_CameraNearClip = %s.nearClipPlane' % (group, cameraShape))
cmds.expression(s = u'%s.Export_CameraFarClip = %s.farClipPlane' % (group, cameraShape))
FOV_EXPR = u'''
{0}.{3} = 2.0 * atan((0.5 * {1}.{2}) / ({1}.focalLength * 0.03937)) * 57.29578
'''
cmds.expression(s =FOV_EXPR.format(group, cameraShape, 'verticalFilmAperture', 'Export_CameraVerticalFOV'))
cmds.expression(s =FOV_EXPR.format(group, cameraShape, 'horizontalFilmAperture', 'Export_CameraHorizontalFOV'))
def _setup_camera_physics_expr(group, cameraShape):
# setup attr and expressions
# turns out expression is one of the things that mel can do but python can't
cmds.addAttr( longName='Export_HorizontalFilmAperture', attributeType='float', readable=True, keyable=True )
cmds.addAttr( longName='Export_VerticalFilmAperture', attributeType='float', readable=True, keyable=True )
cmds.addAttr( longName='Export_FocalLength', attributeType='float', readable=True, keyable=True )
cmds.expression(s = u'%s.Export_HorizontalFilmAperture = %s.horizontalFilmAperture' % (group, cameraShape))
cmds.expression(s = u'%s.Export_VerticalFilmAperture = %s.verticalFilmAperture' % (group, cameraShape))
cmds.expression(s = u'%s.Export_FocalLength = %s.focalLength' % (group, cameraShape))
def bake_animation_to(source, target, hierarchy='none', simulation=False):
'''
# https://www.highend3d.com/maya/script/make-anim-global-for-maya
copy all animation to, note that this don't do simulation and should really be
used against already baked animations
'''
constraints = constraints_rst(source, target)
bake_objects_results([target], hierarchy=hierarchy, simulation=simulation)
cmds.delete(constraints)
def bake_camera_animation_to_global_physics(camera, name):
group = cmds.group(name=name, empty=True)
cameraShape = get_first_shape(camera)
_setup_camer_fov_expr(group, cameraShape)
_setup_camera_physics_expr(group, cameraShape)
bake_animation_to(camera, group, simulation=True)
def export_selected_static_mesh_animation(file_path):
# UE4 can't use static mesh animation anyway, export as animation only
# and import it in engine later
_export_animation_fbx(file_path, animation_only=True)
# clear when done
cmds.select(cl=True)
def _export_commons():
# defaults
# https://docs.unrealengine.com/en-us/Engine/Content/FBX/BestPractices
mel.eval("FBXProperty Export|IncludeGrp|Geometry|SmoothingGroups -v true;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|expHardEdges -v false;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|TangentsandBinormals -v false;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|SmoothMesh -v false;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|SelectionSet -v false;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|BlindData -v false;")
mel.eval('FBXProperty Export|IncludeGrp|Geometry|GeometryNurbsSurfaceAs -v "Interactive Display Mesh";')
mel.eval("FBXProperty Export|IncludeGrp|Geometry|Instances -v false;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|ContainerObjects -v true;")
mel.eval("FBXProperty Export|IncludeGrp|Geometry|Triangulate -v false;")
# unused things
# actually we don't really have referenced constaants
mel.eval("FBXExportReferencedAssetsContent -v false")
mel.eval("FBXExportConstraints -v false")
mel.eval("FBXExportCameras -v false")
mel.eval("FBXExportLights -v false")
mel.eval("FBXExportEmbeddedTextures -v false")
mel.eval("FBXExportInputConnections -v false")
mel.eval("FBXExportSmoothingGroups -v false")
mel.eval("FBXExportSmoothMesh -v false")
def _execute_export_selected(file_path):
file_path = file_path.replace('\\', '/')
mel.eval('FBXExport -f "{0}" -s;'.format(file_path))
def _export_animation_fbx(file_path, animation_only=False):
_export_commons()
# start = str(cmds.playbackOptions(ast=True, q=True))
# end = str(cmds.playbackOptions(aet=True, q=True))
if animation_only:
mel.eval("FBXExportAnimationOnly -v true;")
else:
mel.eval("FBXExportAnimationOnly -v false;")
# should've been baked already
mel.eval("FBXExportBakeComplexAnimation -v false;")
_execute_export_selected(file_path)
def export_camera_animation(root, file_path):
assert not cmds.listRelatives(root, p=True), '%s should be global object' % root
cmds.select(cl=True)
cmds.select(root)
# additionally select all keyable attributes for exporting custom attribute
# ! note that this exported fbx won't have the curve animated when imported
# in maya, as the connection is lost in current settings. but the curve is actually
# exported
channel_box = mel.eval('global string $gChannelBoxName; $temp=$gChannelBoxName;')
attrs = [u'%s.%s' % (root, attr) for attr in cmds.listAttr(root, keyable=True)]
cmds.channelBox(channel_box, e=1, select=attrs)
_export_animation_fbx(file_path, animation_only=True)
def _main():
'''main that called from maya'''
EXPORT_NAME = 'ExportCamera'
camera = cmds.ls(sl=1)
if not camera:
cmds.confirmDialog(title='Error', message=u'nothing is selected, select a camera to export')
return
cameraShape = get_first_shape(camera)
if not cameraShape or not cmds.objectType(cameraShape, isType=u'camera'):
cmds.confirmDialog(title='Error', message=u'%s is not a camera, select a camera to export.' % camera)
return
bake_camera_animation_to_global_physics(cmds.ls(sl=1), EXPORT_NAME)
ret = cmds.fileDialog2(ds=2, fm=0, ff="FBX (*.fbx);;", caption='Export Camera FBX Path')
if not ret: return
file_path = ret[0]
export_camera_animation(EXPORT_NAME, file_path)
def setup():
# add a new menu
cmds.setParent('MayaWindow')
cmds.menu(label='custom')
cmds.menuItem(label='Export Selected Camera', command='import export_camera_standalone; export_camera_standalone._main()')
python("import export_camera_standalone; export_camera_standalone.setup()");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment