Scripting scaling a prim in relation to another prim's pivot

Hi, I’m scripting scaling a mesh prim (“Source”) in relation to an external pivot of another prim (“Target_Pivot”)…:

…so that when I scale the actual “Source” prim, it doesn’t scale from its internal pivot equally on all axes, but in relation to that external pivot, like so (for show and tell purposes, here I mimic my wanted bahavior by scaling “Target_Pivot” with “Source” as its child, but I’d like to be able to script scaling “Source” in relation to “Target_Pivot” regardless of their hierarchy):

https://streamable.com/yqu652

Here’s my python script (it doesn’t change the scale of source_local_matrix, only scale of transform, which isnt in the scene really):

import omni.usd
from pxr import Gf, Sdf, UsdGeom, Usd

stage = omni.usd.get_context().get_stage()

# * Gets Source cube's matrix and translation
source_prim_to_scale = stage.GetPrimAtPath("/Source")
source_xformable = UsdGeom.Xformable(source_prim_to_scale)
source_local_matrix = source_xformable.GetLocalTransformation()

#  * Gets Target Pivot's matrix and trnaslation
target_pivot_to_scale_to = stage.GetPrimAtPath("/Target_Pivot")
target_xformable = UsdGeom.Xformable(target_pivot_to_scale_to)
target_pivot_local_matrix = target_xformable.GetLocalTransformation()

#  * Applies scaling to the prim with pivot using Gf.Transform
transform = Gf.Transform(source_local_matrix)
pivot_in_prim_space = source_local_matrix.TransformDir(target_pivot_local_matrix.ExtractTranslation())

transform.SetPivotPosition(pivot_in_prim_space)
transform.SetScale(Gf.Vec3d(1.0, 2.0, 1.0))
# ^ doesn't change the scale of source_local_matrix, only scale of transform, which isnt in the scene really

One line that does change the “Source” mesh’s scale is…:

source_prim_to_scale.GetAttribute("xformOp:scale").Set(Gf.Vec3d(1.0, 2.0, 1.0)) 

… but it does so in relation to source_prim_to_scale’s internal pivot (so it scales the “Source” mesh from it’s central pivot, on both ends), not the external target_pivot_to_scale_to on just one end.

I’d appreciate any suggestions ^^

@mzaras if you were to establish a parent/child hierarchy in such a way where the mesh prim is the child of the xform (you can do so via dragging and dropping the mesh prim onto the xform inside of the Stage panel), you would then be able to scale the xform to produce the result you are looking for.

and, just to think out loud - anther way to accomplish this without this hierarchy is to utilize the Pivot Tool and modify the pivot:

https://docs.omniverse.nvidia.com/extensions/latest/ext_core/ext_pivot-tool.html

1 Like

Hi @Simplychenable, thanks for your reply. I’m trying to avoid dependence on hierarchy and I’m scripting in python instead of using UI, but your link gave me an idea:

When I add Pivot TransformOp using the UI:

I can then move the Source cube’s pivot:

When I try to replicate it using a script however, Translate:pivot seemingly gets added just the same, but it doesn’t actually translate the cube’s pivot, but the cube itself (it acts as another Translate):

https://gemoo.com/tools/upload-video/share/583365550766895104?codeId=M0Gd6ggm58ONZ&card=583365546761334784

import omni.usd
from pxr import Gf, Sdf, UsdGeom, Usd

stage = omni.usd.get_context().get_stage()
time = Usd.TimeCode.Default()

# * Gets Source cube's matrix and translation
source_prim_to_scale = stage.GetPrimAtPath("/Source")
source_xformable = UsdGeom.Xformable(source_prim_to_scale)
source_local_matrix = source_xformable.GetLocalTransformation()

# # * Gets Target Pivot's matrix and trnaslation
target_pivot_to_scale_to = stage.GetPrimAtPath("/Target_Pivot")
target_xformable = UsdGeom.Xformable(target_pivot_to_scale_to)
target_pivot_local_matrix = target_xformable.GetLocalTransformation()


source_xformable.AddXformOp(UsdGeom.XformOp.TypeTranslate, UsdGeom.XformOp.PrecisionDouble, "pivot")

# here I'm changing the xformOpOrder bc to mimic "xformOp:translate:pivot" getting added right after "xformOp:translate" in xformOpOrder if I add it manually but it doesn't do anything in the end
new_xformop_order = [UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:translate")), 
                     UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:translate:pivot")), 
                     UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:rotateXYZ")),
                     UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:scale"))]
source_xformable.SetXformOpOrder(new_xformop_order)

I’ve tried both:

source_xformable.AddXformOp(UsdGeom.XformOp.TypeTranslate, UsdGeom.XformOp.PrecisionDouble, "pivot")

and

source_xformable.AddTranslateOp(UsdGeom.XformOp.PrecisionDouble, "pivot")

to same result.

Both Translate:pivot transformOps (be it added manually or through script) get the same path:
/Source.xformOp:translate:pivot

I’ve been consulting OpenUSD documentationhttps://openusd.org/release/api/class_usd_geom_xformable.html#ad6dfc740dcec052482489647af9ed36b

I figured it out, I added this line at the end of the script:

source_xformable.AddXformOp(UsdGeom.XformOp.TypeTranslate, UsdGeom.XformOp.PrecisionDouble, "pivot", isInverseOp=True)

the “isInverseOp=True” inverts the transformation of translate:pivot done to the mesh itself, leaving the transformation done to the pivot itself.

Here’s the python script to add xformOp:translate:pivot to a mesh and moving it to external prim’s pivot location so that the mesh can scale in relation to that pivot, not to its default center pivot:

import omni.usd
from pxr import Gf, Sdf, UsdGeom, Usd

stage = omni.usd.get_context().get_stage()

# * Gets Source cube's matrix and translation
source_prim_to_scale = stage.GetPrimAtPath("/Source")
source_xformable = UsdGeom.Xformable(source_prim_to_scale)
source_local_matrix = source_xformable.GetLocalTransformation()

# # * Gets Target Pivot's matrix and trnaslation
target_pivot_to_scale_to = stage.GetPrimAtPath("/Target_Pivot")
target_xformable = UsdGeom.Xformable(target_pivot_to_scale_to)
target_pivot_local_matrix = target_xformable.GetLocalTransformation()
target_translation = target_pivot_local_matrix.ExtractTranslation()

source_xformable.AddXformOp(UsdGeom.XformOp.TypeTranslate, UsdGeom.XformOp.PrecisionDouble, "pivot")

new_xformop_order = [UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:translate")), 
                     UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:translate:pivot")), 
                     UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:rotateXYZ")),
                     UsdGeom.XformOp(source_prim_to_scale.GetAttribute("xformOp:scale"))]
source_xformable.SetXformOpOrder(new_xformop_order)

source_xformable.AddXformOp(UsdGeom.XformOp.TypeTranslate, UsdGeom.XformOp.PrecisionDouble, "pivot", isInverseOp=True)

source_prim_to_scale.GetAttribute("xformOp:translate:pivot").Set(target_translation)

Thank you @Simplychenable for pointing me to the Pivot tool, that helped me crack this :)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.