Maya Python | Need math help with moving objects to match published locations

Hey guys, this is my first post here so please be easy!

I am having some trouble with 3d math, but I could be overthinking it. I’ll try to simplify my topic.

Say we have one rigged asset, “CarA”.
Let’s also say we have two shots using this asset which is used in different locations in world space. Animators could move these anywhere in the scene.

Now we add lighting to the scene; head lights/tail lights. Lights go on the CarA asset, constrained to the curves specifically designated for light constraints.

What we would like to do is publish any data, transformational, rotational or otherwise into json, as well as export lights into a separate Maya file. When the lighting artist opens the second shot, we want to be able to “import lighting” which applies the lights in the same position on the asset, but using the position of CarA asset in the shot that would be animated somewhere completely different.

I’ve got everything setup and working except the “new position” math, I just cannot figure out the math needed to find the “new position” to place the lights for the next shots CarA asset.

My thought process was to save the trans/rot values for the curve transform as well as the light transform on publish:

constraint_data[light_shape] = {
	# Name of the constraint.
	"CONSTRAINT_NAME": constraint,
	# Maya Object type of the constraint.
	"CONSTRAINT_TYPE": cmds.nodeType(constraint),
	# Lights parent transform.
	"LIGHT_PARENT": light_parent,
	# Maya object the light is constrained to/from?
	"CONSTRAINT_OBJECT": curve_transform,
	# Get the transformation and rotation axis of both curve and light.
	"LIGHT_TRANSLATION": cmds.xform(light_parent, query=True, worldSpace=True, translation=True),
	"LIGHT_ROTATION": cmds.xform(light_parent, query=True, worldSpace=True, rotation=True),
	"CONSTRAINT_OBJECT_TRANSLATION": cmds.xform(curve_transform, query=True, worldSpace=True, translation=True),
	"CONSTRAINT_OBJECT_ROTATION": cmds.xform(curve_transform, query=True, worldSpace=True, rotation=True),

Then when importing we just calculate the “offset” or the “difference” from where the light and asset was at the time of publish, to where the asset is now. That way we can figure out where to place the light and then we can just set the rotation and re-parent it as it was when published.

light_parent = publish_data[light_shape]["LIGHT_PARENT"]
		constraint_object = publish_data[light_shape]["CONSTRAINT_OBJECT"]
		light_translation = publish_data[light_shape]["LIGHT_TRANSLATION"]
		light_rotation = publish_data[light_shape]["LIGHT_ROTATION"]
		constraint_object_translation = publish_data[light_shape]["CONSTRAINT_OBJECT_TRANSLATION"]
		constraint_object_rotation = publish_data[light_shape]["CONSTRAINT_OBJECT_ROTATION"]
		if cmds.objExists(constraint_object) and cmds.objExists(light_parent):
			# Get the transformational and rotational values of the objects new position to calculate offset and re-positioning of lights.
			new_constraint_object_pivot = cmds.xform(constraint_object, query=True, worldSpace=True, translation=True)
			new_constraint_object_rotation = cmds.xform(constraint_object, query=True, worldSpace=True, rotation=True)
			# Calculate the transformation offset, used for re-positioning the light to the correct location in world space.
			transformation_offset = (
				(abs(constraint_object_translation[0]) - abs(light_translation[0])),
				(abs(constraint_object_translation[1]) - abs(light_translation[1])),
				(abs(constraint_object_translation[2]) - abs(light_translation[2]))
			new_transformation_position = (
				(new_constraint_object_pivot[0] + transformation_offset[0]),
				(new_constraint_object_pivot[1] + transformation_offset[1]),
				(new_constraint_object_pivot[2] + transformation_offset[2])
			# Calculate the rotational offset, used for re-positioning the lights rotation to the correct location in world space.
			rotation_offset = (
				(abs(constraint_object_rotation[0]) - abs(light_rotation[0])),
				(abs(constraint_object_rotation[1]) - abs(light_rotation[1])),
				(abs(constraint_object_rotation[2]) - abs(light_rotation[2]))
			new_rotation_position = (
				(new_constraint_object_rotation[0] - rotation_offset[0]),
				(new_constraint_object_rotation[1] - rotation_offset[1]),
				(new_constraint_object_rotation[2] - rotation_offset[2])
			# Move the light to the new transformation position.
			cmds.xform(light_parent, translation=new_transformation_position, worldSpace=True)
			# Set Rotation.
			cmds.xform(light_parent, rotation=new_rotation_position, worldSpace=True)

			# Once positions are matched, apply constraints to objects.

But I think I am missing some axis orientations (This is 3d math after all). Obviously it’s easier to just have lights in the asset etc but this whole attempt is to try and make everything modular.

Any help on the last bit of math would be greatly appreciated!

Thank you!

What’s up with those abs() function calls. They don’t seem like they’re needed as some things might be in negative positions and that’s perfectly fine.

What I think you want to do is first find the positions/rotations/scales of the car.
Then subtract the pos/rot/scale vectors from the lights transform data. This gives you an offset from the car to the lights in which you should now just need to add the position of any new car to the lights and you’ll get the lights in the same place as they were with the other car but using a new position.

Why are you trying to deal with manual offset differences in the first place?
If it’s a car, the lights are gonna be in the same places relative to the car. And they’re already constrained to parts of the car in-file … So why not just store what object the lights are constrained to, and rebuild the constraint? Or make a parent to all the lights that you can constrain to the main body controller of the car that will just put the lights where they need to go?

When we export animation to alembic, our rigs define a bunch of transform nulls that will get exported along with the geo for exactly this use-case.

If it’s more complicated than that, then so much of what you will have to do here depends on exactly how you built the car and light rigs, and how your animation export process works, and what passes between different departments.

1 Like

I would just store each relative matrix between the car’s main controller and each light xform node, and on import, then use a multMatrix + decomposeMatrix, adding the previous stored mtx as offset.
see here:

Or as tfox said, just export the lights parented to a null at the car’s main controller position, and constrain them with normal maya constraints or matrix nodes as stated above, which I like better.