/

Maya – Package/Serialize animation from objects

I’ve always needed a way of serializing animated and/or static values out of Maya and so I thought I would provide one of my tools to do just that.

These are the options that are supported:

• Serialize all objects in the scene or only the passed objects.

• Serialize all attributes on those objects or only the passed attributes if they exist on the objects.

• Serialize all attribute types or only the passed attribute types.

• Serialize all values or only values that are keyed.

As noted in my GitLab description, I use my own .json formatter to save this out. If there’s interest in that, please let me know in the comments!

def package_animation_from_objects(objects=None, attributes=None, attribute_types=None, keys_only=False):
	"""
	Create a data block of animated values or static values on transforms per attr per object.
	Only transform objects are included for scene wide packaging and should be the only objects given.
	This will also capture static values if no animation is on an attribute, unless "keys_only" is True.
	:param objects: A list of full object names. If no objects are passed, the entire scene is packaged.
	:type objects: list
	:param attributes: A list of specific attrs (without the period prefix) to write out.
	Any attrs found in an object will be recorded.
	If no attrs are passed, all attrs on the object will be packaged.
	:type attributes: list
	:param attribute_types: A list of specific long name attribute types to write out.
	If no attrs are passed, all attr types are pulled from the objects themselves.
	:type attribute_types: list
	:param keys_only: If True, only capture attrs with keyed animation.
	:type keys_only:  bool
	:return: returns a dict of data.
	:rtype: dict
	"""
	# Create the data bucket.
	animation_data = dict()

	if objects is None:
		# Scene wide object types.
		objects = cmds.ls(exactType="transform")
	if attributes is not None:
		all_attributes = attributes

	for anim_object in objects:
		# Find the namespace.
		if ":" in anim_object:
			namespace = anim_object.rsplit(":", 1)[0]
		# No namespace, enter as root.
		else:
			namespace = ":"

		if attributes is None:
			# Get all attributes in the channel box
			all_attributes = [x for x in cmds.listAttr(anim_object, write=True, hasData=True, visible=True, keyable=True)]

		# If we only want specific attr types, filter them out here.
		if attribute_types is not None:
			# Get the type of attr
			all_attributes = [x for x in all_attributes if cmds.attributeQuery(x, node=anim_object, attributeType=True) in attribute_types]

		# Get all requested attrs.
		for attr in all_attributes:
			# Verify the attr is indeed on this object
			if attr in cmds.listAttr(anim_object, settable=True):
				# Get the type of attr
				attr_type = cmds.attributeQuery(attr, node=anim_object, attributeType=True)
				# Get all the frames this attr is keyed on.
				all_frames = cmds.keyframe(anim_object, query=True, attribute=attr)

				if all_frames or keys_only is False:
					# Create a data entry.
					if namespace not in animation_data.keys():
						animation_data[namespace] = dict()
					if anim_object not in animation_data[namespace].keys():
						animation_data[namespace][anim_object] = dict()

				if all_frames:
					# Enums need to return all values as well as the current index per keyframe.
					if attr_type == "enum":
						indices = cmds.keyframe(anim_object, query=True, attribute=attr, valueChange=True)
						value = str(cmds.attributeQuery(attr, node=anim_object, listEnum=True)[0]).split(":")
						# Special handling for enums
						animation_data[namespace][anim_object]["{}.{}".format(anim_object, attr)] = {
							"ANIMATED": True,
							"TYPE": attr_type,
							"DATA": value,
							"INDEX": {all_frames[index]: int(indices[index]) for index in range(len(all_frames))}
						}

					# Normal attrs
					else:
						# Get the keyed values
						all_values = cmds.keyframe(anim_object, query=True, attribute=attr, valueChange=True)
						# Store the animation by attr, in a dict of frame: value.
						animation_data[namespace][anim_object]["{}.{}".format(anim_object, attr)] = {
							"ANIMATED": True,
							"TYPE": attr_type,
							"DATA": {all_frames[index]: all_values[index] for index in range(len(all_frames))}
						}

				elif keys_only is False:
					# Enums need to return current index and all options
					if attr_type == "enum":
						index = cmds.getAttr(anim_object + ".{}".format(attr))
						value = str(cmds.attributeQuery(attr, node=anim_object, listEnum=True)[0]).split(":")
						# Special handling for enums
						animation_data[namespace][anim_object]["{}.{}".format(anim_object, attr)] = {
							"ANIMATED": False,
							"TYPE": attr_type,
							"DATA": value,
							"INDEX": index
						}

					# Normal attrs
					else:
						value = cmds.getAttr(anim_object + ".{}".format(attr))
						animation_data[namespace][anim_object]["{}.{}".format(anim_object, attr)] = {
							"ANIMATED": False,
							"TYPE": attr_type,
							"DATA": value
						}

	return animation_data

Similar Posts

Leave a Reply

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