コード例 #1
0
 SkelBindingSample ReadUsdWeights(Scene scene)
 {
     if (scene == null)
     {
         return(null);
     }
     try {
         var sample = new SkelBindingSample();
         scene.Read(m_usdMeshPath, sample);
         return(sample);
     } catch (System.Exception ex) {
         Debug.LogException(ex);
         return(null);
     }
 }
コード例 #2
0
        void OnEnable()
        {
            InitUsd.Initialize();

            if (string.IsNullOrEmpty(m_usdMeshPath))
            {
                m_usdMeshPath = UnityTypeConverter.GetPath(transform);
            }

            var    scene   = GetScene();
            var    binding = ReadUsdWeights(scene);
            string skelRootPath;
            var    skeleton = ReadUsdSkeleton(scene, out skelRootPath);

            if (binding == null)
            {
                binding = new SkelBindingSample();
            }

            var mesh = GetComponent <SkinnedMeshRenderer>().sharedMesh;

            // Process classic four-bone weights first.
            var sb = new System.Text.StringBuilder();

#if UNITY_2019_1_OR_NEWER
            var bonesPerVert   = mesh.GetBonesPerVertex();
            int weightsPerBone = 0;
            foreach (int count in bonesPerVert)
            {
                weightsPerBone = weightsPerBone > count ? weightsPerBone : count;
            }
            var boneWeights = mesh.GetAllBoneWeights();
            sb.AppendLine("Many-bone indices: (" + boneWeights.Length + " * 4)");
            int bone = 0;
            int bi   = 0;
            int wi   = 0;
            foreach (var weight in boneWeights)
            {
                if (wi == 0)
                {
                    sb.Append("i: " + bone + " [");
                }

                sb.Append(weight.boneIndex + GetUsdBoneData(bi, wi, binding.jointIndices) + ",");

                wi++;
                if (wi == weightsPerBone)
                {
                    sb.Append("]\n");
                    bi++;
                    wi = 0;
                }

                if (bonesPerVert[bi] != weightsPerBone)
                {
                    // TODO: Unity supports a variable number of weights per bone, but USD does not.
                    // Therefore, the number of weights may be greater in USD than in Unity. Currently
                    // the way this works does not correctly handle that case.
                    Debug.LogWarning("Unity bone count issue, see code comment for details.");
                }

                bone++;
            }
            Debug.Log(sb.ToString());

            bone = 0;
            bi   = 0;
            wi   = 0;
            sb   = new System.Text.StringBuilder();
            sb.AppendLine("Many-bone weights: (" + boneWeights.Length + " * 4)");
            foreach (var weight in boneWeights)
            {
                if (wi == 0)
                {
                    sb.Append("i: " + bone + " [");
                }
                sb.Append(weight.weight + GetUsdBoneData(bi, wi, binding.jointWeights) + ",");

                wi++;
                if (wi == weightsPerBone)
                {
                    sb.Append("]\n");
                    bi++;
                    wi = 0;
                }
                bone++;
            }
            Debug.Log(sb.ToString());
#else
            sb.AppendLine("Legacy 4-bone indices: (" + mesh.boneWeights.Length + " * 4)");
            int bone = 0;
            foreach (var weight in mesh.boneWeights)
            {
                sb.Append("[");
                sb.Append(weight.boneIndex0 + GetUsdBoneData(bone, 0, binding.jointIndices) + ",");
                sb.Append(weight.boneIndex1 + GetUsdBoneData(bone, 1, binding.jointIndices) + ",");
                sb.Append(weight.boneIndex2 + GetUsdBoneData(bone, 2, binding.jointIndices) + ",");
                sb.Append(weight.boneIndex3 + GetUsdBoneData(bone, 3, binding.jointIndices) + "]\n");
                bone++;
            }
            Debug.Log(sb.ToString());

            bone = 0;
            sb   = new System.Text.StringBuilder();
            sb.AppendLine("Legacy 4-bone weights: (" + mesh.boneWeights.Length + " * 4)");
            foreach (var weight in mesh.boneWeights)
            {
                sb.Append("[");
                sb.Append(weight.weight0 + GetUsdBoneData(bone, 0, binding.jointWeights) + ",");
                sb.Append(weight.weight1 + GetUsdBoneData(bone, 1, binding.jointWeights) + ",");
                sb.Append(weight.weight2 + GetUsdBoneData(bone, 2, binding.jointWeights) + ",");
                sb.Append(weight.weight3 + GetUsdBoneData(bone, 3, binding.jointWeights) + "]\n");
                bone++;
            }
            Debug.Log(sb.ToString());
#endif


            sb = new System.Text.StringBuilder();
            var bones    = GetComponent <SkinnedMeshRenderer>().bones;
            var rootBone = GetComponent <SkinnedMeshRenderer>().rootBone;
            var root     = UnityTypeConverter.GetPath(rootBone);
            sb.AppendLine("Bones: (" + bones.Length + ")");
            sb.AppendLine("Root Bone: " + root);
            int i = 0;
            foreach (var boneXf in bones)
            {
                sb.AppendLine(UnityTypeConverter.GetPath(boneXf));
                if (binding.joints != null)
                {
                    sb.AppendLine(root + "\\" + binding.joints[i++] + "\n");
                }
            }
            Debug.Log(sb.ToString());

            sb = new System.Text.StringBuilder();
            sb.AppendLine("Bind Transforms: (" + mesh.bindposes.Length + ")");
            i = -1;
            var options = new SceneImportOptions();
            options.changeHandedness = m_basisTransform;
            foreach (var boneXf in bones)
            {
                i++;
                var bindPose = mesh.bindposes[i];
                var bonePath = UnityTypeConverter.GetPath(boneXf);
                sb.AppendLine("Pose[" + i + "] " + bonePath);
                sb.AppendLine(bindPose.ToString());

                if (skeleton.bindTransforms != null)
                {
                    if (string.IsNullOrEmpty(skelRootPath))
                    {
                        continue;
                    }

                    bonePath = bonePath.Substring(skelRootPath.Length);
                    bonePath = bonePath.TrimStart('/');
                    foreach (var joint in skeleton.joints)
                    {
                        if (joint == bonePath)
                        {
                            var usdMat = skeleton.bindTransforms[i];
                            XformImporter.ImportXform(ref usdMat, options);
                            sb.AppendLine(usdMat.ToString() + "\n");
                            bonePath = null;
                            break;
                        }
                    }

                    if (string.IsNullOrEmpty(bonePath))
                    {
                        continue;
                    }
                    sb.Append("Bone not found in USD: " + bonePath + "\n\n");
                }
            }
            Debug.Log(sb.ToString());
        }
コード例 #3
0
        /// <summary>
        /// Rebuilds the USD scene as Unity GameObjects, with a limited budget per update.
        /// </summary>
        public static IEnumerator BuildScene(Scene scene,
                                             GameObject root,
                                             SceneImportOptions importOptions,
                                             PrimMap primMap,
                                             float targetFrameMilliseconds,
                                             bool composingSubtree)
        {
            var timer       = new System.Diagnostics.Stopwatch();
            var usdPrimRoot = new pxr.SdfPath(importOptions.usdRootPath);

            // Setting an arbitrary fudge factor of 20% is very non-scientific, however it's better than
            // nothing. The correct way to hit a deadline is to predict how long each iteration actually
            // takes and then return early if the estimated time is over budget.
            float targetTime = targetFrameMilliseconds * .8f;

            timer.Start();

            // Reconstruct the USD hierarchy as Unity GameObjects.
            // A PrimMap is returned for tracking the USD <-> Unity mapping.
            Profiler.BeginSample("USD: Build Hierarchy");
            if (importOptions.importHierarchy || importOptions.forceRebuild)
            {
                // When a USD file is fully RE-imported, all exsiting USD data must be removed. The old
                // assumption was that the root would never have much more than the UsdAsset component
                // itself, however it's now clear that the root may also have meaningful USD data added
                // too.
                //
                // TODO(jcowles): This feels like a workaround. What we really want here is an "undo"
                // process for changes made to the root GameObject. For example, to clean up non-USD
                // components which may have been added (e.g. what if a mesh is imported to the root?
                // currently the MeshRenderer etc will remain after re-import).
                RemoveComponent <UsdAssemblyRoot>(root);
                RemoveComponent <UsdVariantSet>(root);
                RemoveComponent <UsdModelRoot>(root);
                RemoveComponent <UsdLayerStack>(root);
                RemoveComponent <UsdPayload>(root);
                RemoveComponent <UsdPrimSource>(root);

                primMap.Clear();
                HierarchyBuilder.BuildGameObjects(scene,
                                                  root,
                                                  usdPrimRoot,
                                                  scene.Find(usdPrimRoot.ToString(), "UsdSchemaBase"),
                                                  primMap,
                                                  importOptions);
            }
            Profiler.EndSample();

            if (ShouldYield(targetTime, timer))
            {
                yield return(null); ResetTimer(timer);
            }

            Profiler.BeginSample("USD: Post Process Hierarchy");
            foreach (var processor in root.GetComponents <IImportPostProcessHierarchy>())
            {
                try {
                    processor.PostProcessHierarchy(primMap, importOptions);
                } catch (System.Exception ex) {
                    Debug.LogException(ex);
                }
            }
            Profiler.EndSample();

            if (ShouldYield(targetTime, timer))
            {
                yield return(null); ResetTimer(timer);
            }

            //
            // Pre-process UsdSkelRoots.
            //

            var skelRoots = new List <pxr.UsdSkelRoot>();

            if (importOptions.importSkinning)
            {
                Profiler.BeginSample("USD: Process UsdSkelRoots");
                foreach (var path in primMap.SkelRoots)
                {
                    try {
                        var skelRootPrim = scene.GetPrimAtPath(path);
                        if (!skelRootPrim)
                        {
                            Debug.LogWarning("SkelRoot prim not found: " + path);
                            continue;
                        }
                        var skelRoot = new pxr.UsdSkelRoot(skelRootPrim);
                        if (!skelRoot)
                        {
                            Debug.LogWarning("SkelRoot prim not SkelRoot type: " + path);
                            continue;
                        }

                        skelRoots.Add(skelRoot);
                        GameObject go = primMap[path];
                        ImporterBase.GetOrAddComponent <Animator>(go, true);
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error pre-processing SkelRoot <" + path + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
                Profiler.EndSample();
            }

            //
            // Import known prim types.
            //

            // Materials.
            Profiler.BeginSample("USD: Build Materials");
            if (importOptions.ShouldBindMaterials)
            {
                foreach (var pathAndSample in scene.ReadAll <MaterialSample>(primMap.Materials))
                {
                    try {
                        var mat = MaterialImporter.BuildMaterial(scene,
                                                                 pathAndSample.path,
                                                                 pathAndSample.sample,
                                                                 importOptions);
                        if (mat != null)
                        {
                            importOptions.materialMap[pathAndSample.path] = mat;
                        }
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing material <" + pathAndSample.path + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
            }
            Profiler.EndSample();

            //
            // Start threads.
            //
            ReadAllJob <XformSample> readXforms;

            if (importOptions.importTransforms)
            {
                readXforms = new ReadAllJob <XformSample>(scene, primMap.Xforms);
#if UNITY_2018_1_OR_NEWER
                readXforms.Schedule(primMap.Xforms.Length, 4);
#else
                readXforms.Run();
#endif
            }
            if (importOptions.importMeshes)
            {
                ActiveMeshImporter.BeginReading(scene, primMap);
            }
#if UNITY_2018_1_OR_NEWER
            JobHandle.ScheduleBatchedJobs();
#endif


            // Xforms.
            //
            // Note that we are specifically filtering on XformSample, not Xformable, this way only
            // Xforms are processed to avoid doing that work redundantly.
            if (importOptions.importTransforms)
            {
                Profiler.BeginSample("USD: Build Xforms");
                foreach (var pathAndSample in readXforms)
                {
                    try {
                        if (pathAndSample.path == usdPrimRoot)
                        {
                            // Never read the xform from the USD root, that will be authored in Unity.
                            continue;
                        }
                        GameObject go = primMap[pathAndSample.path];
                        NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                        XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing xform <" + pathAndSample.path + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }

                foreach (var pathAndSample in scene.ReadAll <XformSample>(primMap.SkelRoots))
                {
                    try {
                        if (pathAndSample.path == usdPrimRoot)
                        {
                            // Never read the xform from the USD root, that will be authored in Unity.
                            continue;
                        }
                        GameObject go = primMap[pathAndSample.path];
                        NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                        XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing xform <" + pathAndSample.path + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }

                if (importOptions.importSkinning)
                {
                    foreach (var pathAndSample in scene.ReadAll <XformSample>(primMap.Skeletons))
                    {
                        try {
                            if (pathAndSample.path == usdPrimRoot)
                            {
                                // Never read the xform from the USD root, that will be authored in Unity.
                                continue;
                            }
                            GameObject go = primMap[pathAndSample.path];
                            NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                            XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                        } catch (System.Exception ex) {
                            Debug.LogException(
                                new ImportException("Error processing xform <" + pathAndSample.path + ">", ex));
                        }

                        if (ShouldYield(targetTime, timer))
                        {
                            yield return(null); ResetTimer(timer);
                        }
                    }
                }

                Profiler.EndSample();
            }

            // Meshes.
            if (importOptions.importMeshes)
            {
                Profiler.BeginSample("USD: Build Meshes");
                IEnumerator it = ActiveMeshImporter.Import(scene, primMap, importOptions);

                while (it.MoveNext())
                {
                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
                Profiler.EndSample();

                // Cubes.
                Profiler.BeginSample("USD: Build Cubes");
                foreach (var pathAndSample in scene.ReadAll <CubeSample>(primMap.Cubes))
                {
                    try {
                        GameObject go = primMap[pathAndSample.path];
                        NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                        XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                        CubeImporter.BuildCube(pathAndSample.sample, go, importOptions);
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing cube <" + pathAndSample.path + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
                Profiler.EndSample();
            }

            // Cameras.
            if (importOptions.importCameras)
            {
                Profiler.BeginSample("USD: Cameras");
                foreach (var pathAndSample in scene.ReadAll <CameraSample>(primMap.Cameras))
                {
                    try {
                        GameObject go = primMap[pathAndSample.path];
                        NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                        XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);

                        // The camera has many value-type parameters that need to be handled correctly when not
                        // not animated. For now, only the camera transform will animate, until this is fixed.
                        if (scene.AccessMask == null || scene.IsPopulatingAccessMask)
                        {
                            CameraImporter.BuildCamera(pathAndSample.sample, go, importOptions);
                        }
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing camera <" + pathAndSample.path + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
                Profiler.EndSample();
            }

            // Build out masters for instancing.
            Profiler.BeginSample("USD: Build Instances");
            foreach (var masterRootPath in primMap.GetMasterRootPaths())
            {
                try {
                    Transform masterRootXf = primMap[masterRootPath].transform;

                    // Transforms
                    if (importOptions.importTransforms)
                    {
                        Profiler.BeginSample("USD: Build Xforms");
                        foreach (var pathAndSample in scene.ReadAll <XformSample>(masterRootPath))
                        {
                            try {
                                GameObject go = primMap[pathAndSample.path];
                                NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                                XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                            } catch (System.Exception ex) {
                                Debug.LogException(
                                    new ImportException("Error processing xform <" + pathAndSample.path + ">", ex));
                            }
                        }

                        foreach (var pathAndSample in scene.ReadAll <XformSample>(masterRootPath))
                        {
                            try {
                                GameObject go = primMap[pathAndSample.path];
                                NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                                XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                            } catch (System.Exception ex) {
                                Debug.LogException(
                                    new ImportException("Error processing xform <" + pathAndSample.path + ">", ex));
                            }
                        }

                        foreach (var pathAndSample in scene.ReadAll <XformSample>(primMap.Skeletons))
                        {
                            try {
                                GameObject go = primMap[pathAndSample.path];
                                NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                                XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                            } catch (System.Exception ex) {
                                Debug.LogException(
                                    new ImportException("Error processing xform <" + pathAndSample.path + ">", ex));
                            }
                        }
                        Profiler.EndSample();
                    }

                    // Meshes.
                    if (importOptions.importMeshes)
                    {
                        Profiler.BeginSample("USD: Build Meshes");
                        foreach (var pathAndSample in scene.ReadAll <MeshSample>(masterRootPath))
                        {
                            try {
                                GameObject go = primMap[pathAndSample.path];
                                NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                                XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                                var subsets = MeshImporter.ReadGeomSubsets(scene, pathAndSample.path);
                                MeshImporter.BuildMesh(pathAndSample.path, pathAndSample.sample, subsets, go, importOptions);
                            } catch (System.Exception ex) {
                                Debug.LogException(
                                    new ImportException("Error processing mesh <" + pathAndSample.path + ">", ex));
                            }
                        }
                        Profiler.EndSample();

                        // Cubes.
                        Profiler.BeginSample("USD: Build Cubes");
                        foreach (var pathAndSample in scene.ReadAll <CubeSample>(masterRootPath))
                        {
                            try {
                                GameObject go = primMap[pathAndSample.path];
                                NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                                XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                                CubeImporter.BuildCube(pathAndSample.sample, go, importOptions);
                            } catch (System.Exception ex) {
                                Debug.LogException(
                                    new ImportException("Error processing cube <" + pathAndSample.path + ">", ex));
                            }
                        }
                        Profiler.EndSample();
                    }

                    // Cameras.
                    if (importOptions.importCameras)
                    {
                        Profiler.BeginSample("USD: Build Cameras");
                        foreach (var pathAndSample in scene.ReadAll <CameraSample>(masterRootPath))
                        {
                            try {
                                GameObject go = primMap[pathAndSample.path];
                                NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions);
                                XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene);
                                CameraImporter.BuildCamera(pathAndSample.sample, go, importOptions);
                            } catch (System.Exception ex) {
                                Debug.LogException(
                                    new ImportException("Error processing camera <" + pathAndSample.path + ">", ex));
                            }
                        }
                        Profiler.EndSample();
                    }
                } catch (System.Exception ex) {
                    Debug.LogException(
                        new ImportException("Error processing master <" + masterRootPath + ">", ex));
                }

                if (ShouldYield(targetTime, timer))
                {
                    yield return(null); ResetTimer(timer);
                }
            } // Instances.
            Profiler.EndSample();

            //
            // Post-process dependencies: materials and bones.
            //

            Profiler.BeginSample("USD: Process Material Bindings");
            try {
                // TODO: Currently ProcessMaterialBindings runs too long and will go over budget for any
                // large scene. However, pulling the loop into this code feels wrong in terms of
                // responsibilities.

                // Process all material bindings in a single vectorized request.
                MaterialImporter.ProcessMaterialBindings(scene, importOptions);
            } catch (System.Exception ex) {
                Debug.LogException(new ImportException("Failed in ProcessMaterialBindings", ex));
            }
            Profiler.EndSample();

            if (ShouldYield(targetTime, timer))
            {
                yield return(null); ResetTimer(timer);
            }

            //
            // SkinnedMesh bone bindings.
            //
            if (importOptions.importSkinning)
            {
                Profiler.BeginSample("USD: Build Skeletons");
                var skeletonSamples = new Dictionary <pxr.SdfPath, SkeletonSample>();
                foreach (var skelRoot in skelRoots)
                {
                    try {
                        var bindings = new pxr.UsdSkelBindingVector();
                        if (!primMap.SkelBindings.TryGetValue(skelRoot.GetPath(), out bindings))
                        {
                            Debug.LogWarning("No bindings found skelRoot: " + skelRoot.GetPath());
                        }

                        if (bindings.Count == 0)
                        {
                            Debug.LogWarning("No bindings found skelRoot: " + skelRoot.GetPath());
                        }

                        foreach (var skelBinding in bindings)
                        {
                            // The SkelRoot will likely have a skeleton binding, but it's inherited, so the bound
                            // skeleton isn't actually known until it's queried from the binding. Still, we would
                            // like not to reprocess skeletons redundantly, so skeletons are cached into a
                            // dictionary.

                            Profiler.BeginSample("Build Bind Transforms");
                            var            skelPath   = skelBinding.GetSkeleton().GetPath();
                            SkeletonSample skelSample = null;
                            if (!skeletonSamples.TryGetValue(skelPath, out skelSample))
                            {
                                skelSample = new SkeletonSample();

                                Profiler.BeginSample("Read Skeleton");
                                scene.Read(skelPath, skelSample);
                                Profiler.EndSample();

                                skeletonSamples.Add(skelPath, skelSample);

                                // Unity uses the inverse bindTransform, since that's actually what's needed for
                                // skinning. Do that once here, so each skinned mesh doesn't need to do it
                                // redundantly.
                                SkeletonImporter.BuildBindTransforms(skelPath, skelSample, importOptions);

                                var bindXforms = new pxr.VtMatrix4dArray();

                                var prim = scene.GetPrimAtPath(skelPath);
                                var skel = new pxr.UsdSkelSkeleton(prim);

                                Profiler.BeginSample("Get SkelQuery");
                                pxr.UsdSkelSkeletonQuery skelQuery = primMap.SkelCache.GetSkelQuery(skel);
                                Profiler.EndSample();

                                Profiler.BeginSample("Get JointWorldBind Transforms");
                                if (!skelQuery.GetJointWorldBindTransforms(bindXforms))
                                {
                                    throw new ImportException("Failed to compute binding trnsforms for <" + skelPath + ">");
                                }
                                Profiler.EndSample();

                                SkeletonImporter.BuildDebugBindTransforms(skelSample, primMap[skelPath], importOptions);
                            }
                            Profiler.EndSample();

                            if (importOptions.importSkinWeights)
                            {
                                //
                                // Apply skinning weights to each skinned mesh.
                                //
                                Profiler.BeginSample("Apply Skin Weights");
                                foreach (var skinningQuery in skelBinding.GetSkinningTargetsAsVector())
                                {
                                    var meshPath = skinningQuery.GetPrim().GetPath();
                                    try {
                                        var skelBindingSample = new SkelBindingSample();
                                        var goMesh            = primMap[meshPath];

                                        scene.Read(meshPath, skelBindingSample);

                                        Profiler.BeginSample("Build Skinned Mesh");
                                        SkeletonImporter.BuildSkinnedMesh(
                                            meshPath,
                                            skelPath,
                                            skelSample,
                                            skelBindingSample,
                                            goMesh,
                                            primMap,
                                            importOptions);
                                        Profiler.EndSample();

                                        // In terms of performance, this is almost free.
                                        goMesh.GetComponent <SkinnedMeshRenderer>().rootBone = primMap[skelPath].transform.GetChild(0);
                                    } catch (System.Exception ex) {
                                        Debug.LogException(new ImportException("Error skinning mesh: " + meshPath, ex));
                                    }
                                }
                                Profiler.EndSample();
                            }
                        }
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing SkelRoot <" + skelRoot.GetPath() + ">", ex));
                    }
                } // foreach SkelRoot
                Profiler.EndSample();

                if (ShouldYield(targetTime, timer))
                {
                    yield return(null); ResetTimer(timer);
                }

                //
                // Bone transforms.
                //
                Profiler.BeginSample("USD: Pose Bones");
                foreach (var pathAndSample in skeletonSamples)
                {
                    var skelPath = pathAndSample.Key;

                    try {
                        var prim = scene.GetPrimAtPath(skelPath);
                        var skel = new pxr.UsdSkelSkeleton(prim);

                        pxr.UsdSkelSkeletonQuery skelQuery = primMap.SkelCache.GetSkelQuery(skel);
                        var joints     = skelQuery.GetJointOrder();
                        var restXforms = new pxr.VtMatrix4dArray();
                        var time       = scene.Time.HasValue ? scene.Time.Value : pxr.UsdTimeCode.Default();

                        Profiler.BeginSample("Compute Joint Local Transforms");
                        if (!skelQuery.ComputeJointLocalTransforms(restXforms, time, atRest: false))
                        {
                            throw new ImportException("Failed to compute bind trnsforms for <" + skelPath + ">");
                        }
                        Profiler.EndSample();

                        Profiler.BeginSample("Build Bones");
                        for (int i = 0; i < joints.size(); i++)
                        {
                            var jointPath = scene.GetSdfPath(joints[i]);
                            if (joints[i] == "/")
                            {
                                jointPath = skelPath;
                            }
                            else if (jointPath.IsAbsolutePath())
                            {
                                Debug.LogException(new System.Exception("Unexpected absolute joint path: " + jointPath));
                                jointPath = new pxr.SdfPath(joints[i].ToString().TrimStart('/'));
                                jointPath = skelPath.AppendPath(jointPath);
                            }
                            else
                            {
                                jointPath = skelPath.AppendPath(jointPath);
                            }
                            var goBone = primMap[jointPath];

                            Profiler.BeginSample("Convert Matrix");
                            var restXform = UnityTypeConverter.FromMatrix(restXforms[i]);
                            Profiler.EndSample();

                            Profiler.BeginSample("Build Bone");
                            SkeletonImporter.BuildSkeletonBone(skelPath, goBone, restXform, joints, importOptions);
                            Profiler.EndSample();
                        }
                        Profiler.EndSample();
                    } catch (System.Exception ex) {
                        Debug.LogException(
                            new ImportException("Error processing SkelRoot <" + skelPath + ">", ex));
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
                Profiler.EndSample();
            }

            //
            // Apply instancing.
            //
            if (importOptions.importSceneInstances)
            {
                Profiler.BeginSample("USD: Build Scene-Instances");
                try {
                    // Build scene instances.
                    InstanceImporter.BuildSceneInstances(primMap, importOptions);
                } catch (System.Exception ex) {
                    Debug.LogException(new ImportException("Failed in BuildSceneInstances", ex));
                }
                Profiler.EndSample();
            }

            if (ShouldYield(targetTime, timer))
            {
                yield return(null); ResetTimer(timer);
            }

            // Build point instances.
            if (importOptions.importPointInstances)
            {
                Profiler.BeginSample("USD: Build Point-Instances");
                // TODO: right now all point instancer data is read, but we only need prototypes and indices.
                foreach (var pathAndSample in scene.ReadAll <PointInstancerSample>())
                {
                    try {
                        GameObject instancerGo = primMap[pathAndSample.path];

                        // Now build the point instances.
                        InstanceImporter.BuildPointInstances(scene,
                                                             primMap,
                                                             pathAndSample.path,
                                                             pathAndSample.sample,
                                                             instancerGo,
                                                             importOptions);
                    } catch (System.Exception ex) {
                        Debug.LogError("Error processing point instancer <" + pathAndSample.path + ">: " + ex.Message);
                    }

                    if (ShouldYield(targetTime, timer))
                    {
                        yield return(null); ResetTimer(timer);
                    }
                }
                Profiler.EndSample();
            }

            //
            // Apply root transform corrections.
            //
            Profiler.BeginSample("USD: Build Root Transforms");
            if (!composingSubtree)
            {
                if (!root)
                {
                    // There is no single root,
                    // Apply root transform corrections to all imported root prims.
                    foreach (KeyValuePair <pxr.SdfPath, GameObject> kvp in primMap)
                    {
                        if (kvp.Key.IsRootPrimPath() && kvp.Value != null)
                        {
                            // The root object at which the USD scene will be reconstructed.
                            // It may need a Z-up to Y-up conversion and a right- to left-handed change of basis.
                            XformImporter.BuildSceneRoot(scene, kvp.Value.transform, importOptions);
                        }
                    }
                }
                else
                {
                    // There is only one root, apply a single transform correction.
                    XformImporter.BuildSceneRoot(scene, root.transform, importOptions);
                }
            }
            Profiler.EndSample();

            Profiler.BeginSample("USD: Post Process Components");
            foreach (var processor in root.GetComponents <IImportPostProcessComponents>())
            {
                try {
                    processor.PostProcessComponents(primMap, importOptions);
                } catch (System.Exception ex) {
                    Debug.LogException(ex);
                }
            }
            Profiler.EndSample();
        }
コード例 #4
0
        static void ExportSkelWeights(Scene scene,
                                      string path,
                                      Mesh unityMesh,
                                      Transform rootBone,
                                      Transform[] bones,
                                      string skeletonPath)
        {
            var sample = new SkelBindingSample();

            sample.geomBindTransform.value = Matrix4x4.identity;
            sample.joints = new string[bones.Length];
            if (!string.IsNullOrEmpty(skeletonPath))
            {
                sample.skeleton.targetPaths = new string[] { skeletonPath };
            }

            int b        = 0;
            var rootPath = UnityTypeConverter.GetPath(rootBone);

            foreach (Transform bone in bones)
            {
                var bonePath = UnityTypeConverter.GetPath(bone);
                if (bonePath == rootPath)
                {
                    sample.joints[b++] = "/";
                }
                else
                {
                    sample.joints[b++] = bonePath.Replace(rootPath + "/", "");
                }
            }

            int i = 0;
            int w = 0;

            b = 0;
#if UNITY_2019
            var  bonesPerVertex   = unityMesh.GetBonesPerVertex();
            var  unityBoneWeights = unityMesh.GetAllBoneWeights();
            byte maxWeightCount   = 0;
            foreach (var c in bonesPerVertex)
            {
                maxWeightCount = maxWeightCount > c ? maxWeightCount : c;
            }

            sample.jointIndices.value         = new int[bonesPerVertex.Length * maxWeightCount];
            sample.jointIndices.elementSize   = maxWeightCount;
            sample.jointIndices.interpolation = PrimvarInterpolation.Vertex;

            sample.jointWeights.value         = new float[bonesPerVertex.Length * maxWeightCount];
            sample.jointWeights.elementSize   = maxWeightCount;
            sample.jointWeights.interpolation = PrimvarInterpolation.Vertex;

            foreach (var weightCount in bonesPerVertex)
            {
                for (int j = 0; j < weightCount; j++)
                {
                    var bw = unityBoneWeights[b++];
                    sample.jointIndices.value[i++] = bw.boneIndex;
                    sample.jointWeights.value[w++] = bw.weight;
                }

                // Unity allows a variable number of weights per bone, but we've made the array square,
                // which means we may need to skip a few indicies, if this vert doesn't use the max number
                // of weights.
                i += maxWeightCount - weightCount;
                w += maxWeightCount - weightCount;
            }
#else
            var unityBoneWeights = unityMesh.boneWeights;

            if (unityBoneWeights.Length == 0)
            {
                Debug.LogWarning("Found zero bone weights at: " + path);
                return;
            }

            sample.jointIndices.value         = new int[unityBoneWeights.Length * 4];
            sample.jointIndices.elementSize   = 4;
            sample.jointIndices.interpolation = PrimvarInterpolation.Vertex;

            sample.jointWeights.value         = new float[unityBoneWeights.Length * 4];
            sample.jointWeights.elementSize   = 4;
            sample.jointWeights.interpolation = PrimvarInterpolation.Vertex;

            foreach (var bone in unityBoneWeights)
            {
                sample.jointIndices.value[i++] = bone.boneIndex0;
                sample.jointIndices.value[i++] = bone.boneIndex1;
                sample.jointIndices.value[i++] = bone.boneIndex2;
                sample.jointIndices.value[i++] = bone.boneIndex3;
                sample.jointWeights.value[w++] = bone.weight0;
                sample.jointWeights.value[w++] = bone.weight1;
                sample.jointWeights.value[w++] = bone.weight2;
                sample.jointWeights.value[w++] = bone.weight3;
            }
#endif
            scene.Write(path, sample);
        }
コード例 #5
0
        public static void BuildSkinnedMesh(string meshPath,
                                            string skelPath,
                                            SkeletonSample skeleton,
                                            SkelBindingSample meshBinding,
                                            GameObject go,
                                            PrimMap primMap,
                                            SceneImportOptions options)
        {
            string[] joints = meshBinding.joints;

            // WARNING: Do not mutate skeleton values.
            string[] skelJoints = skeleton.joints;
            bool     isConstant = meshBinding.jointWeights.interpolation == PrimvarInterpolation.Constant;

            if (joints == null || joints.Length == 0)
            {
                if (skelJoints == null || skelJoints.Length == 0)
                {
                    throw new Exception("Joints array empty: " + meshPath);
                }
                else
                {
                    joints = skelJoints;
                }
            }

            // 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");
            }

            var mesh   = smr.sharedMesh;
            var geomXf = meshBinding.geomBindTransform.value;

            // 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;

            int[]   indices = meshBinding.jointIndices.value;
            float[] weights = meshBinding.jointWeights.value;

            // 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 * meshBinding.jointWeights.elementSize, Allocator.Persistent);
            for (int i = 0; i < mesh.vertexCount; i++)
            {
                int unityIndex = i * meshBinding.jointWeights.elementSize;
                int usdIndex   = isConstant
                     ? 0
                     : unityIndex;

                bonesPerVertex[i] = (byte)meshBinding.jointWeights.elementSize;

                for (int wi = 0; wi < meshBinding.jointWeights.elementSize; wi++)
                {
                    var bw = boneWeights1[unityIndex + wi];
                    bw.boneIndex = indices[usdIndex + wi];
                    bw.weight    = weights[usdIndex + wi];
                    boneWeights1[unityIndex + wi] = bw;
                }
            }
            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 * meshBinding.jointWeights.elementSize;

                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 (meshBinding.jointIndices.elementSize >= 2)
                {
                    boneWeight.boneIndex1 = indices[usdIndex + 1];
                    boneWeight.weight1    = weights[usdIndex + 1];
                }
                if (meshBinding.jointIndices.elementSize >= 3)
                {
                    boneWeight.boneIndex2 = indices[usdIndex + 2];
                    boneWeight.weight2    = weights[usdIndex + 2];
                }
                if (meshBinding.jointIndices.elementSize >= 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
        }