Example #1
0
        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);
 }
Example #3
0
        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
        }