public UsdSkelSkinningQuery GetSkinningQuery(UsdPrim prim) { UsdSkelSkinningQuery ret = new UsdSkelSkinningQuery(UsdCsPINVOKE.UsdSkelCache_GetSkinningQuery(swigCPtr, UsdPrim.getCPtr(prim)), true); if (UsdCsPINVOKE.SWIGPendingException.Pending) { throw UsdCsPINVOKE.SWIGPendingException.Retrieve(); } return(ret); }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(UsdSkelSkinningQuery obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }
public System.Collections.IEnumerator Import(Scene scene, PrimMap primMap, SceneImportOptions importOptions) { if (importOptions.importSkinning) { Profiler.BeginSample("USD: Populate SkelCache"); foreach (var path in primMap.SkelRoots) { var prim = scene.GetPrimAtPath(path); if (!prim) { continue; } var skelRoot = new UsdSkelRoot(prim); if (!skelRoot) { continue; } } Profiler.EndSample(); } System.Reflection.MemberInfo faceVertexCounts = null; System.Reflection.MemberInfo faceVertexIndices = null; System.Reflection.MemberInfo orientation = null; System.Reflection.MemberInfo purpose = null; System.Reflection.MemberInfo visibility = null; if (scene.AccessMask != null && scene.IsPopulatingAccessMask) { var meshType = typeof(MeshSample); faceVertexCounts = meshType.GetMember("faceVertexCounts")[0]; faceVertexIndices = meshType.GetMember("faceVertexIndices")[0]; orientation = meshType.GetMember("orientation")[0]; purpose = meshType.GetMember("purpose")[0]; visibility = meshType.GetMember("visibility")[0]; } foreach (var pathAndSample in m_readMeshesJob) { if (scene.AccessMask != null && scene.IsPopulatingAccessMask) { HashSet <System.Reflection.MemberInfo> members; if (scene.AccessMask.Included.TryGetValue(pathAndSample.path, out members)) { if (members.Contains(faceVertexCounts) || members.Contains(orientation) || members.Contains(faceVertexIndices)) { members.Add(faceVertexCounts); members.Add(faceVertexIndices); members.Add(orientation); } if (pathAndSample.sample.purpose != Purpose.Default && !members.Contains(purpose)) { members.Add(purpose); } if (pathAndSample.sample.visibility != Visibility.Inherited && !members.Contains(visibility)) { members.Add(visibility); } } } Profiler.BeginSample("USD: Build Meshes"); try { GameObject go = primMap[pathAndSample.path]; NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions); if (importOptions.importTransforms) { Profiler.BeginSample("Build Mesh Xform"); XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); Profiler.EndSample(); } Profiler.BeginSample("Read Mesh Subsets"); MeshImporter.GeometrySubsets subsets = null; if (primMap == null || !primMap.MeshSubsets.TryGetValue(pathAndSample.path, out subsets)) { subsets = MeshImporter.ReadGeomSubsets(scene, pathAndSample.path); } Profiler.EndSample(); UsdSkelSkinningQuery skinningQuery; if (importOptions.importHierarchy) { if (importOptions.importSkinning && primMap.SkelCache != null) { // This is pre-cached as part of calling skelCache.Populate and IsValid indicates if we // have the data required to setup a skinned mesh. Profiler.BeginSample("Get Skinning Query"); skinningQuery = new UsdSkelSkinningQuery(); primMap.SkinningQueries[pathAndSample.path] = primMap.SkelCache.GetSkinningQuery(scene.GetPrimAtPath(pathAndSample.path)); Profiler.EndSample(); } if (importOptions.importMeshes) { primMap.MeshSubsets[pathAndSample.path] = MeshImporter.ReadGeomSubsets(scene, pathAndSample.path); } } if (importOptions.importSkinning) { primMap.SkinningQueries.TryGetValue(pathAndSample.path, out skinningQuery); /* * Profiler.BeginSample("Get Skinning Query"); * skinningQuery = primMap.SkelCache.GetSkinningQuery(scene.GetPrimAtPath(pathAndSample.path)); * Profiler.EndSample(); */ } if (importOptions.importSkinning && primMap.SkelCache != null && primMap.SkinningQueries.TryGetValue(pathAndSample.path, out skinningQuery) && skinningQuery.IsValid()) { Profiler.BeginSample("USD: Build Skinned Mesh"); m_skinnedMeshImporter(pathAndSample.path, pathAndSample.sample, subsets, go, importOptions); Profiler.EndSample(); } else { Profiler.BeginSample("USD: Build Mesh"); m_meshImporter(pathAndSample.path, pathAndSample.sample, subsets, go, importOptions); Profiler.EndSample(); } } catch (Exception ex) { Debug.LogException( new SceneImporter.ImportException( "Error processing mesh <" + pathAndSample.path + ">", ex)); } Profiler.EndSample(); yield return(null); } // foreach mesh }
public static void BuildSkinnedMesh(string meshPath, string skelPath, SkeletonSample skeleton, UsdSkelSkinningQuery skinningQuery, GameObject go, PrimMap primMap, SceneImportOptions options) { // The mesh renderer must already exist, since hte mesh also must already exist. var smr = go.GetComponent <SkinnedMeshRenderer>(); if (!smr) { throw new Exception( "Error importing " + meshPath + " SkinnnedMeshRenderer not present on GameObject" ); } // Get and validate the joint weights and indices informations. UsdGeomPrimvar jointWeights = skinningQuery.GetJointWeightsPrimvar(); UsdGeomPrimvar jointIndices = skinningQuery.GetJointIndicesPrimvar(); if (!jointWeights.IsDefined() || !jointIndices.IsDefined()) { throw new Exception("Joints information (indices and/or weights) are missing for: " + meshPath); } // TODO: Both indices and weights attributes can be animated. It's not handled yet. // TODO: Having something that convert a UsdGeomPrimvar into a PrimvarSample could help simplify this code. int[] indices = IntrinsicTypeConverter.FromVtArray((VtIntArray)jointIndices.GetAttr().Get()); int indicesElementSize = jointIndices.GetElementSize(); pxr.TfToken indicesInterpolation = jointIndices.GetInterpolation(); if (indices.Length == 0 || indicesElementSize == 0 || indices.Length % indicesElementSize != 0 || !pxr.UsdGeomPrimvar.IsValidInterpolation(indicesInterpolation)) { throw new Exception("Joint indices information are invalid or empty for: " + meshPath); } float[] weights = IntrinsicTypeConverter.FromVtArray((VtFloatArray)jointWeights.GetAttr().Get()); int weightsElementSize = jointWeights.GetElementSize(); pxr.TfToken weightsInterpolation = jointWeights.GetInterpolation(); if (weights.Length == 0 || weightsElementSize == 0 || weights.Length % weightsElementSize != 0 || !pxr.UsdGeomPrimvar.IsValidInterpolation(weightsInterpolation)) { throw new Exception("Joints weights information are invalid or empty for: " + meshPath); } // Get and validate the local list of joints. VtTokenArray jointsAttr = new VtTokenArray(); skinningQuery.GetJointOrder(jointsAttr); // If jointsAttr wasn't define, GetJointOrder return an empty array and FromVtArray as well. string[] joints = IntrinsicTypeConverter.FromVtArray(jointsAttr); // WARNING: Do not mutate skeleton values. string[] skelJoints = skeleton.joints; if (joints == null || joints.Length == 0) { if (skelJoints == null || skelJoints.Length == 0) { throw new Exception("Joints array empty: " + meshPath); } else { joints = skelJoints; } } var mesh = smr.sharedMesh; // TODO: bind transform attribute can be animated. It's not handled yet. Matrix4x4 geomXf = UnityTypeConverter.FromMatrix(skinningQuery.GetGeomBindTransform()); // If the joints list is a different length than the bind transforms, then this is likely // a mesh using a subset of the total bones in the skeleton and the bindTransforms must be // reconstructed. var bindPoses = skeleton.bindTransforms; if (!JointsMatch(skeleton.joints, joints)) { var boneToPose = new Dictionary <string, Matrix4x4>(); bindPoses = new Matrix4x4[joints.Length]; for (int i = 0; i < skelJoints.Length; i++) { boneToPose[skelJoints[i]] = skeleton.bindTransforms[i]; } for (int i = 0; i < joints.Length; i++) { bindPoses[i] = boneToPose[joints[i]]; } } // When geomXf is identity, we can take a shortcut and just use the exact skeleton bindPoses. if (!ImporterBase.ApproximatelyEqual(geomXf, Matrix4x4.identity)) { // Note that the bind poses were transformed when the skeleton was imported, but the // geomBindTransform is per-mesh, so it must be transformed here so it is in the same space // as the bind pose. XformImporter.ImportXform(ref geomXf, options); // Make a copy only if we haven't already copied the bind poses earlier. if (bindPoses == skeleton.bindTransforms) { var newBindPoses = new Matrix4x4[skeleton.bindTransforms.Length]; Array.Copy(bindPoses, newBindPoses, bindPoses.Length); bindPoses = newBindPoses; } // Concatenate the geometry bind transform with the skeleton bind poses. for (int i = 0; i < bindPoses.Length; i++) { // The geometry transform should be applied to the points before any other transform, // hence the right hand multiply here. bindPoses[i] = bindPoses[i] * geomXf; } } mesh.bindposes = bindPoses; var bones = new Transform[joints.Length]; var sdfSkelPath = new SdfPath(skelPath); for (int i = 0; i < joints.Length; i++) { var jointPath = new SdfPath(joints[i]); if (joints[i] == "/") { jointPath = sdfSkelPath; } else if (jointPath.IsAbsolutePath()) { Debug.LogException(new Exception("Unexpected absolute joint path: " + jointPath)); jointPath = new SdfPath(joints[i].TrimStart('/')); jointPath = sdfSkelPath.AppendPath(jointPath); } else { jointPath = sdfSkelPath.AppendPath(jointPath); } var jointGo = primMap[jointPath]; if (!jointGo) { Debug.LogError("Error importing " + meshPath + " " + "Joint not found: " + joints[i]); continue; } bones[i] = jointGo.transform; } smr.bones = bones; bool isConstant = weightsInterpolation.GetString() == pxr.UsdGeomTokens.constant; // Unity 2019 supports many-bone rigs, older versions of Unity only support four bones. #if UNITY_2019 var bonesPerVertex = new NativeArray <byte>(mesh.vertexCount, Allocator.Persistent); var boneWeights1 = new NativeArray <BoneWeight1>(mesh.vertexCount * weightsElementSize, Allocator.Persistent); for (int i = 0; i < mesh.vertexCount; i++) { int unityIndex = i * weightsElementSize; int usdIndex = isConstant ? 0 : unityIndex; bonesPerVertex[i] = (byte)weightsElementSize; for (int wi = 0; wi < weightsElementSize; wi++) { var bw = boneWeights1[unityIndex + wi]; bw.boneIndex = indices[usdIndex + wi]; bw.weight = weights[usdIndex + wi]; boneWeights1[unityIndex + wi] = bw; } } // TODO: Investigate if bone weights should be normalized before this line. mesh.SetBoneWeights(bonesPerVertex, boneWeights1); bonesPerVertex.Dispose(); boneWeights1.Dispose(); #else var boneWeights = new BoneWeight[mesh.vertexCount]; for (int i = 0; i < boneWeights.Length; i++) { // When interpolation is constant, the base usdIndex should always be zero. // When non-constant, the offset is the index times the number of weights per vertex. int usdIndex = isConstant ? 0 : i * weightsElementSize; var boneWeight = boneWeights[i]; if (usdIndex >= indices.Length) { Debug.Log("UsdIndex out of bounds: " + usdIndex + " indices.Length: " + indices.Length + " boneWeights.Length: " + boneWeights.Length + " mesh: " + meshPath); } boneWeight.boneIndex0 = indices[usdIndex]; boneWeight.weight0 = weights[usdIndex]; if (indicesElementSize >= 2) { boneWeight.boneIndex1 = indices[usdIndex + 1]; boneWeight.weight1 = weights[usdIndex + 1]; } if (indicesElementSize >= 3) { boneWeight.boneIndex2 = indices[usdIndex + 2]; boneWeight.weight2 = weights[usdIndex + 2]; } if (indicesElementSize >= 4) { boneWeight.boneIndex3 = indices[usdIndex + 3]; boneWeight.weight3 = weights[usdIndex + 3]; } // If weights are less than 1, Unity will not automatically renormalize. // If weights are greater than 1, Unity will renormalize. // Only normalize when less than one to make it easier to diff bone weights which were // round-tripped and were being normalized by Unity. float sum = boneWeight.weight0 + boneWeight.weight1 + boneWeight.weight2 + boneWeight.weight3; if (sum < 1) { boneWeight.weight0 /= sum; boneWeight.weight1 /= sum; boneWeight.weight2 /= sum; boneWeight.weight3 /= sum; } boneWeights[i] = boneWeight; } mesh.boneWeights = boneWeights; #endif }