def testSkelWithoutBindPose(self): """ Tests export of a Skeleton when a bindPose is not fully setup. """ mayaFile = os.path.join(self.inputPath, "UsdExportSkeletonTest", "UsdExportSkeletonWithoutBindPose.ma") cmds.file(mayaFile, force=True, open=True) frameRange = [1, 5] usdFile = os.path.abspath('UsdExportSkeletonWithoutBindPose.usda') cmds.usdExport(mergeTransformAndShape=True, file=usdFile, shadingMode='none', frameRange=frameRange, exportSkels='auto') stage = Usd.Stage.Open(usdFile) skeleton = UsdSkel.Skeleton.Get(stage, '/cubeRig/skel/joint1') self.assertEqual(skeleton.GetBindTransformsAttr().Get(), Vt.Matrix4dArray([Gf.Matrix4d(1.0)])) self.assertEqual(skeleton.GetJointsAttr().Get(), Vt.TokenArray(['joint1'])) self.assertEqual(skeleton.GetRestTransformsAttr().Get(), Vt.Matrix4dArray([Gf.Matrix4d(1.0)])) self.assertTrue(skeleton.GetPrim().HasAPI(UsdSkel.BindingAPI)) skelBindingAPI = UsdSkel.BindingAPI(skeleton) self.assertTrue(skelBindingAPI) animSourcePrim = skelBindingAPI.GetAnimationSource() self.assertEqual(animSourcePrim.GetPath(), '/cubeRig/skel/joint1/Animation') animSource = UsdSkel.Animation(animSourcePrim) self.assertTrue(animSource) self.assertEqual(skeleton.GetJointsAttr().Get(), Vt.TokenArray(['joint1'])) self.assertEqual(animSource.GetRotationsAttr().Get(), Vt.QuatfArray([Gf.Quatf(1.0, Gf.Vec3f(0.0))])) self.assertEqual(animSource.GetScalesAttr().Get(), Vt.Vec3hArray([Gf.Vec3h(1.0)])) self.assertEqual( animSource.GetTranslationsAttr().Get(Usd.TimeCode.Default()), Vt.Vec3fArray([Gf.Vec3f(5.0, 5.0, 0.0)])) self.assertEqual( animSource.GetTranslationsAttr().Get(1.0), Vt.Vec3fArray([Gf.Vec3f(0.0, 0.0, 0.0)])) self.assertEqual( animSource.GetTranslationsAttr().Get(2.0), Vt.Vec3fArray([Gf.Vec3f(1.25, 1.25, 0.0)])) self.assertEqual( animSource.GetTranslationsAttr().Get(3.0), Vt.Vec3fArray([Gf.Vec3f(2.5, 2.5, 0.0)])) self.assertEqual( animSource.GetTranslationsAttr().Get(4.0), Vt.Vec3fArray([Gf.Vec3f(3.75, 3.75, 0.0)])) self.assertEqual( animSource.GetTranslationsAttr().Get(5.0), Vt.Vec3fArray([Gf.Vec3f(5.0, 5.0, 0.0)]))
def _create_keyframe_transform_node(self, gltf_node, animation_channels, input_sample): matrix = gltf_node.matrix if matrix: translation = Gf.Vec3f() rotation = Gf.Quatf() scale = Gf.Vec3h() usd_matrix = self._convert_to_usd_matrix(matrix) UsdSkel.DecomposeTransform(usd_matrix, translation, rotation, scale) else: translation = Gf.Vec3f(gltf_node.translation) rotation = Gf.Quatf(gltf_node.rotation[3], gltf_node.rotation[0], gltf_node.rotation[1], gltf_node.rotation[2]) scale = Gf.Vec3h(gltf_node.scale) for animation_channel in animation_channels: if animation_channel.target.path == 'translation': translation = animation_channel.sampler.get_interpolated_output_data( input_sample) elif animation_channel.target.path == 'rotation': rotation = animation_channel.sampler.get_interpolated_output_data( input_sample) elif animation_channel.target.path == 'scale': scale = animation_channel.sampler.get_interpolated_output_data( input_sample) return UsdSkel.MakeTransform(translation, rotation, scale)
def test_InstancedBlendShape(self): """Tests for correctness in the interpretation of instanced blend shapes.""" testFile = "populate.usda" stage = Usd.Stage.Open(testFile) cache = UsdSkel.Cache() skelRoot = UsdSkel.Root(stage.GetPrimAtPath("/SkelBinding")) self.assertTrue(cache.Populate(skelRoot, Usd.TraverseInstanceProxies())) # instance proxy mesh mesh = stage.GetPrimAtPath("/SkelBinding/Instance/Override") skelBinding = UsdSkel.BindingAPI(mesh) self.assertEqual(list(skelBinding.GetBlendShapesAttr().Get()), ["override"]) skel = skelBinding.GetSkeleton() skelq = cache.GetSkelQuery(skel) animq = skelq.GetAnimQuery() self.assertEqual(list(animq.GetBlendShapeOrder()), ["override", "shape"]) skinq = cache.GetSkinningQuery(mesh) self.assertTrue(skinq.HasBlendShapes()) self.assertEqual(skelBinding.GetInheritedAnimationSource(), stage.GetPrimAtPath("/Anim1")) # bug PRES-77530 # because skelBinding.GetBlendShapesAttr().Get() and # animq.GetBlendShapeOrder() have different order, # the mapper should not be null. mapper = skinq.GetBlendShapeMapper() self.assertFalse(mapper.IsNull())
def test_SkelImport(self): cmds.file(new=True, force=True) path = os.path.join(self.inputPath, "UsdImportSkeleton", "skelCube.usda") cmds.usdImport(file=path, readAnimData=True, primPath="/Root", shadingMode=[ ["none", "default"], ]) stage = Usd.Stage.Open(path) skelCache = UsdSkel.Cache() bindingSitePrim = stage.GetPrimAtPath("/Root") self.assertTrue(bindingSitePrim.IsA(UsdSkel.Root)) if Usd.GetVersion() > (0, 20, 8): skelCache.Populate(UsdSkel.Root(bindingSitePrim), Usd.PrimDefaultPredicate) else: skelCache.Populate(UsdSkel.Root(bindingSitePrim)) skel = UsdSkel.Skeleton.Get(stage, "/Root/Skeleton") self.assertTrue(skel) skelQuery = skelCache.GetSkelQuery(skel) self.assertTrue(skelQuery) meshPrim = stage.GetPrimAtPath("/Root/Cube") self.assertTrue(meshPrim) skinningQuery = skelCache.GetSkinningQuery(meshPrim) self.assertTrue(skinningQuery) jointNames = [ name.split("/")[-1] for name in skelQuery.GetJointOrder() ] joints = [_GetDepNode(n) for n in jointNames] self.assertTrue(all(joints)) self._ValidateJointTransforms(skelQuery, joints) self._ValidateJointBindPoses(skelQuery, joints) self._ValidateBindPose("Skeleton_bindPose", skelQuery, joints) self._ValidateMeshTransform(meshPrim.GetName(), skinningQuery) self._ValidateSkinClusterRig(joints=joints, skinClusterName="skinCluster_{}".format( meshPrim.GetName()), groupPartsName="skinClusterGroupParts", groupIdName="skinClusterGroupId", bindPoseName="Skeleton_bindPose", meshName=meshPrim.GetName(), usdSkelQuery=skelQuery, usdSkinningQuery=skinningQuery)
def _test_InheritedSkeletonBinding(self): """Tests for correctness in the interpretation of the inherited skel:skeleton binding.""" testFile = "populate.usda" stage = Usd.Stage.Open(testFile) rootPath = "/Skeleton" # TODO: To correctly test this, we need either a different kind of mapping, # or we need the skinning query to keep a record of the bound prim. cache = UsdSkel.Cache() root = UsdSkel.Root(stage.GetPrimAtPath(rootPath)) self.assertTrue(cache.Populate(root)) queryA = cache.GetSkinningQuery( UsdSkel.Animation.Get(stage, rootPath + "/Scope/A")) self.assertTrue(queryA) self.assertEqual(queryA.GetSkelQuery().GetPrim().GetPath(), Sdf.Path("/Anim1")) queryB = cache.GetSkinningQuery( UsdSkel.Animation.Get(stage, rootPath + "/Scope/B")) self.assertTrue(queryB) self.assertEqual(queryB.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim2")) queryC = cache.GetSkinningQuery( UsdSkel.Animation.Get(stage, rootPath + "/Scope/C")) self.assertFalse(queryC) queryD = cache.GetSkinningQuery( UsdSkel.Animation.Get(stage, rootPath + "/Scope/C")) self.assertFalse(queryD)
def main(): print("UsdSkelAppleFixup Begin.") # Create a new out usd file that sublayers the in usd file. All edits # will go to the out usd file. Start and end time is copied over. inFile = args.inusd outFile = args.outusd from pxr import Usd, UsdGeom, UsdSkel, Sdf dstLyr = Sdf.Layer.CreateNew(outFile) srcLyr = Sdf.Layer.FindOrOpen(inFile) stage = Usd.Stage.Open(dstLyr) stage.SetEditTarget(dstLyr) layerStack = dstLyr.subLayerPaths layerStack.insert(len(layerStack), inFile) start = int(srcLyr.startTimeCode) end = int(srcLyr.endTimeCode) stage.SetStartTimeCode(start) stage.SetEndTimeCode(end) # Find all the skinned meshes and copy over the animation source to them # The the skel, anim, and mesh prims. skelPrims = [ prim for prim in Usd.PrimRange(stage.GetPseudoRoot()) if prim.IsA(UsdSkel.Skeleton) ] meshPrims = [ prim for prim in Usd.PrimRange(stage.GetPseudoRoot()) if prim.IsA(UsdGeom.Mesh) ] for skelPrim in skelPrims: skelCache = UsdSkel.Cache() skel = UsdSkel.Skeleton(skelPrim) skelQuery = skelCache.GetSkelQuery(skel) skelAnimQuery = skelQuery.GetAnimQuery() skelAnim = UsdSkel.Animation(skelAnimQuery.GetPrim()) for mesh in meshPrims: rel = FindSkelBindingRel(stage, mesh) if rel: for target in rel.GetTargets(): if target == skelPrim.GetPath(): print("Copying the animationSource relationship to " + mesh.GetName()) # wire up the skeleton and animation source rels for temporary compatbility with Apple's expectations mesh.CreateRelationship( UsdSkel.Tokens.skelAnimationSource).AddTarget( skelAnim.GetPrim().GetPath()) mesh.CreateRelationship( UsdSkel.Tokens.skelSkeleton).AddTarget( skelPrim.GetPath()) dstLyr.Save()
def _convert_skin_to_usd(self, gltf_node, gltf_primitive, usd_node, usd_mesh): """Converts a glTF skin to a UsdSkel Arguments: gltf_node {dict} -- glTF node node_index {int} -- index of the glTF node usd_parent_node {UsdPrim} -- parent node of the usd node usd_mesh {[type]} -- [description] """ skel_binding_api = UsdSkel.BindingAPI(usd_mesh) gltf_skin = gltf_node.get_skin() if not gltf_skin: self.logger.warning('The glTF node has joints, but no skin associated with them') else: gltf_joint_names = [GLTF2USDUtils.convert_to_usd_friendly_node_name(joint.name) for joint in gltf_skin.get_joints()] usd_joint_names = [Sdf.Path(self._get_usd_joint_hierarchy_name(joint, gltf_skin.root_joints)) for joint in gltf_skin.get_joints()] skeleton = self._create_usd_skeleton(gltf_skin, usd_node, usd_joint_names) skeleton_animation = self._create_usd_skeleton_animation(gltf_skin, skeleton, usd_joint_names) parent_path = usd_node.GetPath() bind_matrices = [] rest_matrices = [] skeleton_root = self.stage.GetPrimAtPath(parent_path) skel_binding_api = UsdSkel.BindingAPI(usd_mesh) skel_binding_api.CreateGeomBindTransformAttr(Gf.Matrix4d(((1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)))) skel_binding_api.CreateSkeletonRel().AddTarget(skeleton.GetPath()) if skeleton_animation: skel_binding_api.CreateAnimationSourceRel().AddTarget(skeleton_animation.GetPath()) skeleton_skel_binding_api = UsdSkel.BindingAPI(skeleton) skeleton_skel_binding_api.CreateAnimationSourceRel().AddTarget(skeleton_animation.GetPath()) bind_matrices = self._compute_bind_transforms(gltf_skin) primitive_attributes = gltf_primitive.get_attributes() if 'WEIGHTS_0' in primitive_attributes and 'JOINTS_0' in primitive_attributes: total_vertex_weights = primitive_attributes['WEIGHTS_0'].get_data() total_vertex_joints = primitive_attributes['JOINTS_0'].get_data() total_joint_indices = [] total_joint_weights = [] for joint_indices, weights in zip(total_vertex_joints, total_vertex_weights): for joint_index, weight in zip(joint_indices, weights): total_joint_indices.append(joint_index) total_joint_weights.append(weight) joint_indices_attr = skel_binding_api.CreateJointIndicesPrimvar(False, 4).Set(total_joint_indices) total_joint_weights = Vt.FloatArray(total_joint_weights) UsdSkel.NormalizeWeights(total_joint_weights, 4) joint_weights_attr = skel_binding_api.CreateJointWeightsPrimvar(False, 4).Set(total_joint_weights)
def _TestSkelAnimation(self, animSchema): numFrames = 10 random.seed(0) stage = Usd.Stage.CreateInMemory() anim = animSchema.Define(stage, "/Anim") joints = Vt.TokenArray(["/A", "/B", "/C"]) blendshapes = Vt.TokenArray(["shapeA", "shapeB", "shapeC"]) anim.GetJointsAttr().Set(joints) anim.GetBlendShapesAttr().Set(blendshapes) xformsPerFrame = [[_RandomXf() for _ in xrange(len(joints))] for _ in xrange(numFrames)] for frame, xforms in enumerate(xformsPerFrame): t, r, s = UsdSkel.DecomposeTransforms(Vt.Matrix4dArray(xforms)) anim.GetTranslationsAttr().Set(t, frame) anim.GetRotationsAttr().Set(r, frame) anim.GetScalesAttr().Set(s, frame) weightsPerFrame = [[random.random() for _ in xrange(len(blendshapes))] for _ in xrange(numFrames)] for frame, weights in enumerate(weightsPerFrame): anim.GetBlendShapeWeightsAttr().Set(Vt.FloatArray(weights), frame) # Now try reading that all back via computations... cache = UsdSkel.Cache() query = cache.GetAnimQuery(anim.GetPrim()) self.assertEqual(query.GetPrim(), anim.GetPrim()) self.assertEqual(query.GetJointOrder(), joints) self.assertEqual(query.GetBlendShapeOrder(), blendshapes) self.assertTrue(query.JointTransformsMightBeTimeVarying()) self.assertEqual(query.GetJointTransformTimeSamples(), list(xrange(numFrames))) for frame, xforms in enumerate(xformsPerFrame): computedXforms = query.ComputeJointLocalTransforms(frame) self.assertArrayIsClose(computedXforms, xforms) for frame, weights in enumerate(weightsPerFrame): computedWeights = query.ComputeBlendShapeWeights(frame) self.assertArrayIsClose(computedWeights, weights)
def test_InheritedAnimBinding(self): """Tests for correctness in the interpretation of the inherited skel:animationSource binding.""" testFile = "populate.usda" stage = Usd.Stage.Open(testFile) cache = UsdSkel.Cache() root = UsdSkel.Root(stage.GetPrimAtPath("/AnimBinding")) self.assertTrue(cache.Populate(root)) query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/Scope/Inherit")) self.assertTrue(query) self.assertEqual(query.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim1")) query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/Scope/Override")) self.assertTrue(query) self.assertEqual(query.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim2")) query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/Scope/Block")) self.assertTrue(query) self.assertFalse(query.GetAnimQuery()) query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/Unbound")) self.assertTrue(query) self.assertFalse(query.GetAnimQuery()) query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/BoundToInactiveAnim")) self.assertTrue(query) self.assertFalse(query.GetAnimQuery()) # Ensure that the animationSource binding crosses instancing arcs. query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/Instance/Inherit")) self.assertTrue(query) self.assertEqual(query.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim1")) # TODO: This test case is failing: query = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, "/AnimBinding/Instance/Override")) self.assertTrue(query) self.assertTrue(query.GetAnimQuery().GetPrim().IsInMaster())
def test_AnimQuery(self): """Tests anim query retrieval.""" cache = UsdSkel.Cache() stage = Usd.Stage.CreateInMemory() anim = UsdSkel.Animation.Define(stage, "/Anim") self.assertTrue(cache.GetAnimQuery(anim)) # Backwards-compatibility. self.assertTrue(cache.GetAnimQuery(prim=anim.GetPrim())) self.assertFalse(cache.GetAnimQuery(UsdSkel.Animation()))
def test_SkelImport(self): cmds.file(new=True, force=True) path = os.path.abspath("skelCube.usda") cmds.usdImport(file=path, readAnimData=True, primPath="/Root", assemblyRep="Import", shadingMode="none") stage = Usd.Stage.Open(path) skelCache = UsdSkel.Cache() bindingSitePrim = stage.GetPrimAtPath("/Root") self.assertTrue(bindingSitePrim.IsA(UsdSkel.Root)) skelCache.Populate(UsdSkel.Root(bindingSitePrim)) skelQuery = skelCache.GetSkelQuery(bindingSitePrim) self.assertTrue(skelQuery) meshPrim = stage.GetPrimAtPath("/Root/Cube") self.assertTrue(meshPrim) skinningQuery = skelCache.GetSkinningQuery(meshPrim) self.assertTrue(skinningQuery) jointNames = [ name.split("/")[-1] for name in skelQuery.GetJointOrder() ] joints = [_GetDepNode(n) for n in jointNames] self.assertTrue(all(joints)) self._ValidateJointTransforms(skelQuery, joints) self._ValidateJointBindPoses(skelQuery, joints) self._ValidateBindPose("bindPose", skelQuery, joints) self._ValidateMeshTransform(meshPrim.GetName(), skinningQuery) self._ValidateSkinClusterRig(joints=joints, skinClusterName="skinCluster1", groupPartsName="skinClusterGroupParts", groupIdName="skinClusterGroupId", bindPoseName="bindPose", meshName=meshPrim.GetName(), usdSkelQuery=skelQuery, usdSkinningQuery=skinningQuery)
def test_ValidateTopology(self): """Test UsdSkelTopology validation""" topology = UsdSkel.Topology( MakeTargets("A", "A/B", "A/B/C", "D", "D/E")) self.assertTrue(topology) valid, reason = topology.Validate() self.assertTrue(valid) # Mis-orderered topology (parents don't come before children) topology = UsdSkel.Topology(MakeTargets("A/B", "C", "A")) self.assertTrue(topology) valid, reason = topology.Validate() self.assertFalse(valid) self.assertTrue(reason)
def testExportSkin_Posed(self): """ Checks that the skeletal skinning works when the skeleton is exported in a non-rest pose. """ cmds.currentTime(1, edit=True) usdFile = os.path.abspath('UsdExportMesh_skelPosed.usda') cmds.usdExport(mergeTransformAndShape=True, file=usdFile, shadingMode='none', exportSkin='explicit') usdFileNoSkin = os.path.abspath('UsdExportMesh_skelPosed_noskin.usda') cmds.usdExport(mergeTransformAndShape=True, file=usdFileNoSkin, shadingMode='none', exportSkin='none') stage = Usd.Stage.Open(usdFile) stageNS = Usd.Stage.Open(usdFileNoSkin) m = UsdGeom.Mesh.Get(stage, '/ExplicitSkelRoot/mesh') mNS = UsdGeom.Mesh.Get(stageNS, '/ExplicitSkelRoot/mesh') # Check Maya's output mesh versus the UsdSkel-computed result. skelRoot = UsdSkel.Root.Get(stage, '/ExplicitSkelRoot') UsdSkel.BakeSkinningLBS(skelRoot) points = m.GetPointsAttr().Get() refSkinnedPoints = mNS.GetPointsAttr().Get() self._AssertVec3fArrayAlmostEqual(points, refSkinnedPoints)
def test_Skinning(self): testFile = "skinning.usda" stage = Usd.Stage.Open(testFile) UsdSkel.BakeSkinning(stage.Traverse()) stage.GetRootLayer().Export("skinning.skinned.usda")
def test_Topology(self): """Tests basics of a UsdSkelTopology""" targets = MakeTargets( # Basic parenting. Order shouldn't matter. "A", # 0 "A/B/C", # 1 "A/B", # 2 "Nested/Without/Parent/Joint", # 2 # The direct parent path of a joint may not # be included. Will use the first valid ancestor # path that is in the set as the parent. "D", # 4 "D/E/F/G", # 5 # Abs. vs. relative paths shouldn't matter, # But we won't map relative paths to abs paths, or vice versa. "/A/B/C", # 6 "/A/B", # 7 "/A", # 8 # empty paths should be treated as roots. None, # 9, # Test invalid paths. "/", "..", "../.." ) expectedParentIndices = Vt.IntArray( [-1, 2, 0, -1, -1, 4, 7, 8, -1, -1, -1, -1, -1]) topology = UsdSkel.Topology(targets) self.assertEqual(topology.GetNumJoints(), len(targets)) self.assertEqual(topology.GetParentIndices(), expectedParentIndices) for i,parentIndex in enumerate(expectedParentIndices): self.assertEqual(parentIndex, topology.GetParent(i))
def compute_usd_transform_matrix_from_gltf_node(node): """Computes a transform matrix from a glTF node object Arguments: node {Node} -- glTF2 Node object Returns: Matrix-- USD matrix """ matrix = node.matrix if (matrix != None): return Gf.Matrix4d(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[7], matrix[8], matrix[9], matrix[10], matrix[11], matrix[12], matrix[13], matrix[14], matrix[15]) else: translation = node.translation usd_translation = Gf.Vec3f(translation[0], translation[1], translation[2]) rotation = node.rotation usd_rotation = Gf.Quatf(rotation[3], rotation[0], rotation[1], rotation[2]) scale = node.scale usd_scale = Gf.Vec3h(scale[0], scale[1], scale[2]) return UsdSkel.MakeTransform(usd_translation, usd_rotation, usd_scale)
def test_JointInfluences(self): """Tests for helpers for getting/setting joint influences.""" stage = Usd.Stage.CreateInMemory() gprim = stage.DefinePrim('/Model/Gprim') binding = UsdSkel.BindingAPI(gprim) indices = binding.CreateJointIndicesPrimvar(constant=False, elementSize=3) assert indices assert indices.GetInterpolation() == UsdGeom.Tokens.vertex assert indices.GetElementSize() == 3 weights = binding.CreateJointWeightsPrimvar(constant=True) assert weights assert weights.GetInterpolation() == UsdGeom.Tokens.constant # Should be able to re-create bindings with an alternate # interpolation and/or element size. weights = binding.CreateJointWeightsPrimvar(constant=False, elementSize=3) assert weights assert weights.GetInterpolation() == UsdGeom.Tokens.vertex assert weights.GetElementSize() == 3 assert binding.SetRigidJointInfluence(10, 0.5) indices = binding.GetJointIndicesPrimvar() weights = binding.GetJointWeightsPrimvar() assert indices.Get() == Vt.IntArray([10]) assert weights.Get() == Vt.FloatArray([0.5])
def testSkelTransforms(self): """ Tests that the computed joint transforms in USD, when tarnsformed into world space, match the world space transforms of the Maya joints. """ mayaFile = os.path.join(self.inputPath, "UsdExportSkeletonTest", "UsdExportSkeleton.ma") cmds.file(mayaFile, force=True, open=True) # frameRange = [1, 30] frameRange = [1, 3] # TODO: The joint hierarchy intentionally includes non-joint nodes, # which are expected to be ignored. However, when we try to extract # restTransforms from the dagPose, the intermediate transforms cause # problems, since they are not members of the dagPose. As a result, # no dag pose is exported. Need to come up with a way to handle this # correctly in export. print("Expect warnings about invalid restTransforms") usdFile = os.path.abspath('UsdExportSkeleton.usda') cmds.usdExport(mergeTransformAndShape=True, file=usdFile, shadingMode='none', frameRange=frameRange, exportSkels='auto') stage = Usd.Stage.Open(usdFile) root = UsdSkel.Root.Get(stage, '/SkelChar') self.assertTrue(root) skelCache = UsdSkel.Cache() skelCache.Populate(root) skel = UsdSkel.Skeleton.Get(stage, '/SkelChar/Hips') self.assertTrue(skel) skelQuery = skelCache.GetSkelQuery(skel) self.assertTrue(skelQuery) xfCache = UsdGeom.XformCache() for frame in range(*frameRange): cmds.currentTime(frame, edit=True) xfCache.SetTime(frame) skelLocalToWorld = xfCache.GetLocalToWorldTransform(skelQuery.GetPrim()) usdJointXforms = skelQuery.ComputeJointSkelTransforms(frame) for joint,usdJointXf in zip(skelQuery.GetJointOrder(), usdJointXforms): usdJointWorldXf = usdJointXf * skelLocalToWorld selList = OM.MSelectionList() selList.add(Sdf.Path(joint).name) dagPath = selList.getDagPath(0) mayaJointWorldXf = Gf.Matrix4d(*dagPath.inclusiveMatrix()) self.assertTrue(Gf.IsClose(mayaJointWorldXf, usdJointWorldXf, 1e-5))
def _compute_rest_matrix(self, gltf_node): """ Compute the rest matrix from a glTF node. The translation, rotation and scale are combined into a transformation matrix Returns: Matrix4d -- USD matrix """ xform_matrix = None matrix = gltf_node.matrix if matrix != None: xform_matrix = self._convert_to_usd_matrix(matrix) return xform_matrix else: usd_scale = Gf.Vec3h(1, 1, 1) usd_rotation = Gf.Quatf().GetIdentity() usd_translation = Gf.Vec3f(0, 0, 0) scale = gltf_node.scale usd_scale = Gf.Vec3h(scale[0], scale[1], scale[2]) rotation = gltf_node.rotation usd_rotation = Gf.Quatf(rotation[3], rotation[0], rotation[1], rotation[2]) translation = gltf_node.translation usd_translation = Gf.Vec3f(translation[0], translation[1], translation[2]) return UsdSkel.MakeTransform(usd_translation, usd_rotation, usd_scale)
def _validate_topology(paths): """Check if the order of `paths` will create a correct UsdSkelSkeleton. Args: paths (list[str]): The USD joint paths to check. e.g. ["some_joint", "some_joint/another_joint", ...]. Raises: RuntimeError: If `paths` is in an incorrect order. Returns: `pxr.UsdSkel.Topology`: The topology description that was used to validate `paths`. """ topology = UsdSkel.Topology(paths) valid, reason = topology.Validate() if not valid: raise RuntimeError( 'Topology failed because of reason "{reason}".'.format( reason=reason)) return topology
def test_BlendShapes(self): testFile = "blendshapes.usda" stage = Usd.Stage.Open(testFile) self.assertTrue(UsdSkel.BakeSkinning(stage.Traverse())) stage.GetRootLayer().Export("blendshapes.baked.usda")
def testSkelTransformDecomposition(self): """ Tests that the decomposed transform values, when recomposed, recreate the correct Maya transformation matrix. """ usdFile = os.path.abspath('UsdExportSkeleton.usda') cmds.usdExport(mergeTransformAndShape=True, file=usdFile, shadingMode='none', frameRange=[1, 30]) stage = Usd.Stage.Open(usdFile) anim = UsdSkel.PackedJointAnimation.Get(stage, '/skeleton_Hip/Animation') self.assertEqual(anim.GetJointsAttr().Get()[8], "Hip/Spine/Neck/RArm") animT = anim.GetTranslationsAttr() animR = anim.GetRotationsAttr() animS = anim.GetScalesAttr() selList = OM.MSelectionList() selList.add("RArm") rArmDagPath = selList.getDagPath(0) fnTransform = OM.MFnTransform(rArmDagPath) for i in range(1, 31): cmds.currentTime(i, edit=True) mayaXf = fnTransform.transformation().asMatrix() usdT = animT.Get(i)[8] usdR = animR.Get(i)[8] usdS = animS.Get(i)[8] usdXf = UsdSkel.MakeTransform(usdT, usdR, usdS) self._AssertMatricesClose(usdXf, Gf.Matrix4d(*mayaXf))
def test_LinearBlendSkinningWithInterval(self): testFile = "lbs.usda" stage = Usd.Stage.Open(testFile) self.assertTrue(UsdSkel.BakeSkinning( stage.Traverse(), Gf.Interval(1, 10))) stage.GetRootLayer().Export("lbs.bakedInterval.usda")
def test_ResizeInfluences(self): """Test influence resizing.""" origIndices = Vt.IntArray([1, 2, 3, 4, 5, 6, 7, 8]) # truncation indices = Vt.IntArray(origIndices) assert UsdSkel.ResizeInfluences(indices, 4, 2) self.assertEqual(indices, Vt.IntArray([1, 2, 5, 6])) # expansion indices = Vt.IntArray(origIndices) assert UsdSkel.ResizeInfluences(indices, 2, 4) self.assertEqual( indices, Vt.IntArray([1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 0, 0, 7, 8, 0, 0])) # Weight truncation. Weights should be renormalized. weights = Vt.FloatArray([3, 1, 1, 5, 6, 1, 1, 3, 7, 8, 1, 3, 1, 9, 10]) assert UsdSkel.ResizeInfluences(weights, 5, 3) self.assertEqual( weights, Vt.FloatArray([0.6, 0.2, 0.2, 0.2, 0.2, 0.6, 0.2, 0.6, 0.2])) # Weight expansion. Holes must be zero-filled. weights = Vt.FloatArray([1, 2, 3, 4, 5, 6, 7, 8, 9]) assert UsdSkel.ResizeInfluences(weights, 3, 5) self.assertEqual( weights, Vt.FloatArray([1, 2, 3, 0, 0, 4, 5, 6, 0, 0, 7, 8, 9, 0, 0])) # Should be okay to resize an empty array. array = Vt.IntArray() assert UsdSkel.ResizeInfluences(array, 10, 4) assert UsdSkel.ResizeInfluences(array, 4, 10) array = Vt.FloatArray() assert UsdSkel.ResizeInfluences(array, 10, 4) assert UsdSkel.ResizeInfluences(array, 4, 10) # Failure cases array = Vt.FloatArray([1]) print "expect a warning about an invalid array shape" assert not UsdSkel.ResizeInfluences(array, 3, 2) print "expect a warning about an invalid array shape" assert not UsdSkel.ResizeInfluences(array, 3, 0)
def test_InheritedAnimationSource(self): """Tests for correctness in the interpretation of the inherited skel:animationSource binding.""" testFile = "populate.usda" stage = Usd.Stage.Open(testFile) rootPath = "/AnimationSource" cache = UsdSkel.Cache() root = UsdSkel.Root(stage.GetPrimAtPath(rootPath)) self.assertTrue(cache.Populate(root)) queryA = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, rootPath + "/Scope/A")) self.assertTrue(queryA) self.assertEqual(queryA.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim1")) queryB = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, rootPath + "/Scope/B")) self.assertTrue(queryB) self.assertEqual(queryB.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim2")) queryC = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, rootPath + "/Scope/C")) self.assertFalse(queryC) queryD = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, rootPath + "/Scope/C")) self.assertFalse(queryD) # Ensure that the animationSource binding crosses instancing arcs. queryInstA = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, rootPath + "/Instance/A")) self.assertTrue(queryInstA) self.assertEqual(queryInstA.GetAnimQuery().GetPrim().GetPath(), Sdf.Path("/Anim1")) queryInstB = cache.GetSkelQuery( UsdSkel.Skeleton.Get(stage, rootPath + "/Instance/B")) self.assertTrue(queryInstB) self.assertTrue(queryInstB.GetAnimQuery().GetPrim().IsInMaster())
def test_ExpandConstantInfluencesToVarying(self): """Test expansion of constant influences into varying influences.""" vals = [1,2,3,4,5] for vtType in (Vt.IntArray, Vt.FloatArray): array = vtType(vals) assert UsdSkel.ExpandConstantInfluencesToVarying(array, 3) self.assertEquals(array, vtType(vals*3)) array = vtType(vals) assert UsdSkel.ExpandConstantInfluencesToVarying(array, 0) self.assertEquals(array, vtType()) # Empty array case. array = vtType() assert UsdSkel.ExpandConstantInfluencesToVarying(array, 3) self.assertEquals(array, vtType())
def test_InheritedSkeletonBinding(self): """Tests for correctness in the interpretation of the inherited skel:skeleton binding.""" testFile = "populate.usda" stage = Usd.Stage.Open(testFile) cache = UsdSkel.Cache() root = UsdSkel.Root(stage.GetPrimAtPath("/SkelBinding")) self.assertTrue(cache.Populate(root)) skel1 = UsdSkel.Skeleton.Get(stage, "/Skel1") skel2 = UsdSkel.Skeleton.Get(stage, "/Skel2") # TODO: Skel population does not currently traverse instance proxies, # so the resolved bindings below will not include skinned meshes within # instances. binding1 = cache.ComputeSkelBinding(root, skel1) self.assertEqual(binding1.GetSkeleton().GetPrim(), skel1.GetPrim()) self.assertEqual([t.GetPrim() for t in binding1.GetSkinningTargets()], [stage.GetPrimAtPath(("/SkelBinding/Scope/Inherit"))]) binding2 = cache.ComputeSkelBinding(root, skel2) self.assertEqual(binding2.GetSkeleton().GetPrim(), skel2.GetPrim()) self.assertEqual([t.GetPrim() for t in binding2.GetSkinningTargets()], [stage.GetPrimAtPath(("/SkelBinding/Scope/Override"))]) allBindings = cache.ComputeSkelBindings(root) # Expecting two resolved bindings. This should *not* include bindings # for any inactive skels. self.assertEqual(len(allBindings), 2) self.assertEqual(binding1.GetSkeleton().GetPrim(), allBindings[0].GetSkeleton().GetPrim()) self.assertEqual([t.GetPrim() for t in binding1.GetSkinningTargets()], [t.GetPrim() for t in allBindings[0].GetSkinningTargets()]) self.assertEqual(binding2.GetSkeleton().GetPrim(), allBindings[1].GetSkeleton().GetPrim()) self.assertEqual([t.GetPrim() for t in binding2.GetSkinningTargets()], [t.GetPrim() for t in allBindings[1].GetSkinningTargets()])
def testBlendShapesExport(self): # NOTE: (yliangsiew) Basic blendshape export test. om.MFileIO.newFile(True) parent = cmds.group(name="root", empty=True) base, _ = cmds.polyCube(name="base") cmds.parent(base, parent) target, _ = cmds.polyCube(name="blend") cmds.parent(target, parent) cmds.polyMoveVertex('{}.vtx[0:2]'.format(target), s=(1.0, 1.5, 1.0)) cmds.blendShape(target, base, automatic=True) cmds.select(base, replace=True) temp_file = os.path.join(self.temp_dir, 'blendshape.usda') cmds.mayaUSDExport(f=temp_file, v=True, sl=True, ebs=True, skl="auto") stage = Usd.Stage.Open(temp_file) prim = stage.GetPrimAtPath("/root/base/blendShape") offsets = prim.GetAttribute("offsets").Get() for i, coords in enumerate(offsets): coords = list(coords) # convert from GfVec3 self.assertEqual(coords, [0, -0.25 if i < 2 else 0.25, 0]) """ Sample BlendShape prim: def BlendShape "blendShape" { uniform vector3f[] normalOffsets = [(0, 0, 0), (0, 0, 0), (0, 0, 0)] uniform vector3f[] offsets = [(0, -0.25, 0), (0, -0.25, 0), (0, 0.25, 0)] uniform int[] pointIndices = [0, 1, 2] } """ # NOTE: (yliangsiew) Test simple inbetween setup. om.MFileIO.open(self.scene_path, None, True) cmds.select("basic_cube_2_inbetweens_no_anim|base", r=True) cmds.mayaUSDExport(f=temp_file, v=True, sl=True, ebs=True, skl="auto", skn="auto") stage = Usd.Stage.Open(temp_file) prim = stage.GetPrimAtPath("/basic_cube_2_inbetweens_no_anim/base/pCube2") blendShape = UsdSkel.BlendShape(prim) inbetweens = blendShape.GetInbetweens() self.assertEqual(len(inbetweens), 2) # NOTE: (yliangsiew) This particular setup has two additional inbetweens. # NOTE: (yliangsiew) Test simple multiple targets setup. om.MFileIO.open(self.scene_path, None, True) cmds.select("basic_cube_4_blendshapes_no_anim|base", r=True) cmds.mayaUSDExport(f=temp_file, v=True, sl=True, ebs=True, skl="auto", skn="auto") stage = Usd.Stage.Open(temp_file) prim = stage.GetPrimAtPath("/basic_cube_4_blendshapes_no_anim/base") blendShapes = prim.GetChildren() for bs in blendShapes: self.assertEqual(bs.GetTypeName(), 'BlendShape')
def test_SortInfluences(self): """Test influence sorting.""" indices = Vt.IntArray([1,2,3,4, 5,6,7,8, 9,10,11,12]) weights = Vt.FloatArray([3,1,4,2, 1,2,3,4, 4,3,2,1]) expectedIndices = Vt.IntArray([3,1,4,2, 8,7,6,5, 9,10,11,12]) expectedWeights = Vt.FloatArray([4,3,2,1, 4,3,2,1, 4,3,2,1]) assert UsdSkel.SortInfluences(indices, weights, 4) self.assertEqual(indices, expectedIndices) self.assertEqual(weights, expectedWeights) # Check case of numInfluencesPerComponent=1 indices = Vt.IntArray([1,2,3,4]) weights = Vt.FloatArray([3,1,4,2]) assert UsdSkel.SortInfluences(indices, weights, 1) assert indices == Vt.IntArray([1,2,3,4]) assert weights == Vt.FloatArray([3,1,4,2])
def test_NormalizeWeights(self): """Test weight normalization.""" # Basic weight normalization. weights = Vt.FloatArray([3,1,1,0, 4,3,2,1, 40,30,20,10, 0,0,0,0]) assert UsdSkel.NormalizeWeights(weights, 4) self.assertEqual(weights, Vt.FloatArray([0.6,0.2,0.2,0.0, 0.4,0.3,0.2,0.1, 0.4,0.3,0.2,0.1, 0, 0, 0, 0])) # Should be okay to normalize an empty array. assert UsdSkel.NormalizeWeights(Vt.FloatArray(), 4) # Failure cases. print("expect a warning about an invalid array shape") assert not UsdSkel.NormalizeWeights(Vt.FloatArray([1]), 4) print("expect a warning about an invalid array shape") assert not UsdSkel.NormalizeWeights(Vt.FloatArray([1]), 0)