Пример #1
0
        /// <summary>
        /// Imports the USD scene incrementally, setting a fixed time budget per frame for import
        /// operations. Uses StartCoroutine.
        /// </summary>
        public void ImportUsdAsCoroutine(GameObject goRoot,
                                         string usdFilePath,
                                         double time,
                                         SceneImportOptions importOptions,
                                         float targetFrameMilliseconds)
        {
            InitUsd.Initialize();
            var scene = Scene.Open(usdFilePath);

            if (scene == null)
            {
                throw new Exception("Failed to open: " + usdFilePath);
            }

            scene.Time = time;
            if (scene == null)
            {
                throw new Exception("Null USD Scene");
            }

            scene.SetInterpolation(importOptions.interpolate ?
                                   Scene.InterpolationMode.Linear :
                                   Scene.InterpolationMode.Held);
            var primMap  = new PrimMap();
            var importer = SceneImporter.BuildScene(scene,
                                                    goRoot,
                                                    importOptions,
                                                    primMap,
                                                    targetFrameMilliseconds,
                                                    composingSubtree: false);

            StartCoroutine(importer);
        }
        public override void OnInspectorGUI()
        {
            var layerStack = (UsdLayerStack)this.target;

            base.DrawDefaultInspector();

            GUILayout.Space(10);

            if (GUILayout.Button("Save Overrides to Target Layer"))
            {
                InitUsd.Initialize();
                layerStack.SaveToLayer();
            }

            if (GUILayout.Button("Save Layer Stack"))
            {
                InitUsd.Initialize();
                Scene scene = Scene.Open(layerStack.GetComponent <UsdAsset>().usdFullPath);
                try {
                    layerStack.SaveLayerStack(scene, layerStack.m_layerStack);
                } finally {
                    scene.Close();
                    scene = null;
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Returns the USD.NET.Scene object for this USD file.
        /// The caller is NOT expected to close the scene.
        /// </summary>
        public Scene GetScene()
        {
            InitUsd.Initialize();

            if (m_lastScene == null || m_lastScene.Stage == null || SceneFileChanged())
            {
                pxr.UsdStage stage = null;
                if (string.IsNullOrEmpty(usdFullPath))
                {
                    return(null);
                }
                if (m_payloadPolicy == PayloadPolicy.DontLoadPayloads)
                {
                    stage = pxr.UsdStage.Open(usdFullPath, pxr.UsdStage.InitialLoadSet.LoadNone);
                }
                else
                {
                    stage = pxr.UsdStage.Open(usdFullPath, pxr.UsdStage.InitialLoadSet.LoadAll);
                }

                m_lastScene      = Scene.Open(stage);
                m_lastPrimMap    = null;
                m_lastAccessMask = null;

                // TODO: This is potentially horrible in terms of performance, LoadAndUnload should be used
                // instead, but the binding is not complete.
                foreach (var payload in GetComponentsInParent <UsdPayload>())
                {
                    var primSrc = payload.GetComponent <UsdPrimSource>();
                    if (payload.IsLoaded && m_payloadPolicy == PayloadPolicy.DontLoadPayloads)
                    {
                        var prim = m_lastScene.GetPrimAtPath(primSrc.m_usdPrimPath);
                        if (prim == null || !prim)
                        {
                            continue;
                        }
                        prim.Load();
                    }
                    else
                    {
                        var prim = m_lastScene.GetPrimAtPath(primSrc.m_usdPrimPath);
                        if (prim == null || !prim)
                        {
                            continue;
                        }
                        prim.Unload();
                    }
                }

                // Re-apply variant selection state, similar to prim load state.
                foreach (var variants in GetComponentsInChildren <UsdVariantSet>())
                {
                    ApplyVariantSelectionState(m_lastScene, variants);
                }
            }

            m_lastScene.Time = m_usdTimeOffset;
            m_lastScene.SetInterpolation(m_interpolation);
            return(m_lastScene);
        }
Пример #4
0
        public static Scene InitForSave(string defaultName, string fileExtension = "usd,usda,usdc")
        {
            var filePath = EditorUtility.SaveFilePanel("Export USD File", "", defaultName, fileExtension);

            if (filePath == null || filePath.Length == 0)
            {
                return(null);
            }

            var fileDir = Path.GetDirectoryName(filePath);

            if (!Directory.Exists(fileDir))
            {
                var di = Directory.CreateDirectory(fileDir);
                if (!di.Exists)
                {
                    Debug.LogError("Failed to create directory: " + fileDir);
                    return(null);
                }
            }

            InitUsd.Initialize();
            var scene = Scene.Create(filePath);

            scene.Time      = 0;
            scene.StartTime = 0;
            scene.EndTime   = 0;
            return(scene);
        }
        public static Scene InitForSave(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                return(null);
            }

            var fileDir = Path.GetDirectoryName(filePath);

            if (!Directory.Exists(fileDir))
            {
                var di = Directory.CreateDirectory(fileDir);
                if (!di.Exists)
                {
                    Debug.LogError("Failed to create directory: " + fileDir);
                    return(null);
                }
            }

            InitUsd.Initialize();
            var scene = Scene.Create(filePath);

            scene.Time      = 0;
            scene.StartTime = 0;
            scene.EndTime   = 0;
            return(scene);
        }
Пример #6
0
        /// <summary>
        /// Loads or unloads the given payload object. Throws an exception if game object deos not have
        /// a UsdPrimSource behaviour.
        /// </summary>
        public void SetPayloadState(GameObject go, bool isLoaded)
        {
            var primSrc = go.GetComponent <UsdPrimSource>();

            if (!primSrc)
            {
                throw new Exception("UsdPrimSource not found: " + UnityTypeConverter.GetPath(go.transform));
            }

            var usdPrimPath = primSrc.m_usdPrimPath;

            InitUsd.Initialize();
            var scene = GetScene();

            if (scene == null)
            {
                throw new Exception("Failed to open: " + usdFullPath);
            }

            var prim = scene.GetPrimAtPath(usdPrimPath);

            if (prim == null || !prim)
            {
                throw new Exception("Prim not found: " + usdPrimPath);
            }

            foreach (var child in go.transform.GetComponentsInChildren <UsdPrimSource>().ToList())
            {
                if (!child || child.gameObject == go)
                {
                    continue;
                }

                GameObject.DestroyImmediate(child.gameObject);
            }

            if (!isLoaded)
            {
                prim.Unload();
                return;
            }
            else
            {
                prim.Load();
            }

            SceneImportOptions importOptions = new SceneImportOptions();

            this.StateToOptions(ref importOptions);
            importOptions.usdRootPath = prim.GetPath();
            SceneImporter.ImportUsd(go, scene, new PrimMap(), true, importOptions);
        }
Пример #7
0
        public static Scene InitForOpen()
        {
            string path = EditorUtility.OpenFilePanel("Import USD File", "", "usd,usda,usdc,abc");

            if (path == null || path.Length == 0)
            {
                return(null);
            }
            InitUsd.Initialize();
            var stage = pxr.UsdStage.Open(path, pxr.UsdStage.InitialLoadSet.LoadNone);

            return(Scene.Open(stage));
        }
Пример #8
0
        public static Scene InitForOpen(string path = "", UsdStage.InitialLoadSet loadSet = pxr.UsdStage.InitialLoadSet.LoadNone)
        {
#if UNITY_EDITOR
            if (String.IsNullOrEmpty(path) && !UnityEngine.Application.isPlaying)
            {
                path = EditorUtility.OpenFilePanel("Import USD File", "", "usd,usda,usdc,abc");
            }
#endif

            if (String.IsNullOrEmpty(path))
            {
                return(null);
            }

            InitUsd.Initialize();
            // var editingStage = new EditingStage(path);
            var stage = pxr.UsdStage.Open(path, loadSet);
            return(Scene.Open(stage));
        }
Пример #9
0
        /// <summary>
        /// Sets the variant selections in USD at the given prim path based on the selections parameter.
        /// </summary>
        /// <param name="go">The gameObject at the root of the variant set.</param>
        /// <param name="usdPrimPath">The USD prim at which to set the variant selection.</param>
        /// <param name="selections">A collection of (variant set, selection) pairs.</param>
        /// <remarks>
        /// A USD prim can have zero or more variant sets, for example a single prim amy have
        /// "modelingVariant" and "shadingVariant" sets. Each set can have their own slection.
        /// </remarks>
        /// <example>
        /// If two sets with selections are modelingVariant=CupWithHandle and shadingVariant=BrightBlue,
        /// resulting in a bright blue cup with a handle. In this example, the selections dictionary
        /// would contain:
        ///  { "modelingVariant" = "CupWithHandle",
        ///    "shadingVariant" = "BrightBlue" }
        /// </example>
        public void SetVariantSelection(GameObject go,
                                        string usdPrimPath,
                                        Dictionary <string, string> selections)
        {
            InitUsd.Initialize();
            var scene = GetScene();

            if (scene == null)
            {
                throw new Exception("Failed to open: " + usdFullPath);
            }

            var prim = scene.GetPrimAtPath(usdPrimPath);

            if (prim == null || !prim)
            {
                throw new Exception("Prim not found: " + usdPrimPath);
            }

            var varSets = prim.GetVariantSets();

            foreach (var sel in selections)
            {
                if (!varSets.HasVariantSet(sel.Key))
                {
                    throw new Exception("Unknown varient set: " + sel.Key + " at " + usdPrimPath);
                }

                varSets.GetVariantSet(sel.Key).SetVariantSelection(sel.Value);
            }

            // TODO: sparsely remove prims, rather than blowing away all the children.
            foreach (Transform child in go.transform)
            {
                GameObject.DestroyImmediate(child.gameObject);
            }

            SceneImportOptions importOptions = new SceneImportOptions();

            this.StateToOptions(ref importOptions);
            importOptions.usdRootPath = prim.GetPath();
            SceneImporter.ImportUsd(go, scene, new PrimMap(), true, importOptions);
        }
Пример #10
0
        // ------------------------------------------------------------------------------------------ //
        // Recording Control.
        // ------------------------------------------------------------------------------------------ //

        public void BeginRecording(double currentTime, GameObject root)
        {
            InitUsd.Initialize();
            _root = root;

            if (!root)
            {
                Debug.LogError("ExportRoot not assigned.");
                return;
            }

            if (Clip.UsdScene != null)
            {
                Clip.UsdScene.Close();
                Clip.UsdScene = null;
            }

            // Keep the current directory to restore it at the end.
            currentDir = Directory.GetCurrentDirectory();
            var localScale = root.transform.localScale;

            try
            {
                if (string.IsNullOrEmpty(Clip.m_usdFile))
                {
                    Clip.UsdScene = Scene.Create();
                }
                else if (Clip.IsUSDZ)
                {
                    // Setup a temporary directory to export the wanted USD file and zip it.
                    string tmpDirPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                    usdzTemporaryDir = Directory.CreateDirectory(tmpDirPath);

                    // Get the usd file name to export and the usdz file name of the archive.
                    usdcFileName = Path.GetFileNameWithoutExtension(Clip.m_usdFile) + ".usdc";
                    usdzFileName = Path.GetFileName(Clip.m_usdFile);
                    var fi = new FileInfo(Clip.m_usdFile);
                    usdzFilePath = fi.FullName;

                    // Set the current working directory to the tmp directory to export with relative paths.
                    Directory.SetCurrentDirectory(tmpDirPath);

                    Clip.UsdScene = UsdzExporter.InitForSave(usdcFileName);
                }
                else
                {
                    Clip.UsdScene = Scene.Create(Clip.m_usdFile);
                }

                // Set the frame rate in USD  as well.
                //
                // This both set the "time samples per second" and the playback rate.
                // Setting times samples per second allows the authoring code to express samples as integer
                // values, avoiding floating point error; so by setting FrameRate = 60, the samples written
                // at time=0 through time=59 represent the first second of playback.
                //
                // Stage.TimeCodesPerSecond is set implicitly to 1 / FrameRate.
                //m_usdScene.FrameRate = Clip.frame;

                // When authoring in terms of seconds, at any frame rate the samles written at
                // time = 0.0 through time = 1.0 represent the first second of playback. The framerate
                // above will only be used as a target frame rate.
                //if (m_timeUnits == TimeCode.Seconds) {
                //  m_usdScene.Stage.SetTimeCodesPerSecond(1);
                //}

                // Regardless of the actual sampling rate (e.g. Timeline playback speed), we are converting
                // the timecode from seconds to frames with a sampling rate of 60 FPS. This has the nice quality
                // of adding additional numerical stability.
                // In the event that the timeline is not configured for 60 FPS playback, we rely on USD's linear
                // interpolation mode to up-sample to 60 FPS.
                Clip.UsdScene.FrameRate = kExportFrameRate;
                Clip.UsdScene.Stage.SetInterpolationType(pxr.UsdInterpolationType.UsdInterpolationTypeLinear);

                // For simplicity in this example, adding game objects while recording is not supported.
                Clip.Context                 = new ExportContext();
                Clip.Context.scene           = Clip.UsdScene;
                Clip.Context.basisTransform  = Clip.m_convertHandedness;
                Clip.Context.activePolicy    = Clip.m_activePolicy;
                Clip.Context.exportMaterials = Clip.m_exportMaterials;
                // USDZ is in centimeters.
                Clip.Context.scale = Clip.IsUSDZ ? 100.0f : 1.0f;

                Clip.UsdScene.StartTime = currentTime * kExportFrameRate;

                // Export the "default" frame, that is, all data which doesn't vary over time.
                Clip.UsdScene.Time = null;

                SceneExporter.SyncExportContext(root, Clip.Context);
                SceneExporter.Export(root,
                                     Clip.Context,
                                     zeroRootTransform: false);
            }
            catch
            {
                if (Clip.UsdScene != null)
                {
                    Debug.LogError("Set scene to null");
                    Clip.UsdScene.Close();
                    Clip.UsdScene = null;
                }

                if (Clip.IsUSDZ)
                {
                    usdzTemporaryDir.Delete(recursive: true);
                }

                throw;
            }
            finally
            {
                Directory.SetCurrentDirectory(currentDir);
            }
        }
Пример #11
0
        public static void ExportUsdz(string usdzFilePath,
                                      GameObject root)
        {
            // Ensure USD is initialized before changing CWD.
            // This does not protect us against external changes to CWD, so we are actively looking for
            // a more robust solution with UPM devs.
            InitUsd.Initialize();

            // Keep the current directory to restore it at the end.
            var currentDir = Directory.GetCurrentDirectory();

            // Setup a temporary directory to export the wanted USD file and zip it.
            string        tmpDirPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            DirectoryInfo tmpDir     = Directory.CreateDirectory(tmpDirPath);

            // Get the usd file name to export and the usdz file name of the archive.
            string usdcFileName = Path.GetFileNameWithoutExtension(usdzFilePath) + ".usdc";
            string usdzFileName = Path.GetFileName(usdzFilePath);

            try
            {
                // Set the current working directory to the tmp directory to export with relative paths.
                Directory.SetCurrentDirectory(tmpDirPath);

                // Create the tmp .usd scene, into which the data will be exported.
                Scene   scene      = ExportHelpers.InitForSave(Path.Combine(tmpDirPath, usdcFileName));
                Vector3 localScale = root.transform.localScale;

                try
                {
                    // USDZ is in centimeters.
                    root.transform.localScale = localScale * 100;

                    // Export the temp scene.
                    SceneExporter.Export(root,
                                         scene,
                                         BasisTransformation.SlowAndSafe, // Required by ARKit
                                         exportUnvarying: true,
                                         zeroRootTransform: false,
                                         exportMaterials: true);
                }
                finally
                {
                    // Undo temp scale.
                    root.transform.localScale = localScale;

                    // Flush any in-flight edits and release the scene so the file can be deleted.
                    scene.Save();
                    scene.Close();
                    scene = null;
                }

                SdfAssetPath assetPath = new SdfAssetPath(usdcFileName);
                bool         success   = pxr.UsdCs.UsdUtilsCreateNewARKitUsdzPackage(assetPath, usdzFileName);

                if (!success)
                {
                    Debug.LogError("Couldn't export " + root.name + " to the usdz file: " + usdzFilePath);
                    return;
                }

                File.Copy(usdzFileName, usdzFilePath, overwrite: true);
            }
            finally
            {
                // Clean up temp files.
                Directory.SetCurrentDirectory(currentDir);
                tmpDir.Delete(recursive: true);
            }
        }
Пример #12
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());
        }
 // Called when the owning graph starts playing
 public override void OnGraphStart(Playable playable)
 {
     InitUsd.Initialize();
 }
Пример #14
0
        // ------------------------------------------------------------------------------------------ //
        // Recording Control.
        // ------------------------------------------------------------------------------------------ //

        public void BeginRecording(double currentTime, GameObject root)
        {
            InitUsd.Initialize();

            if (!root)
            {
                Debug.LogError("ExportRoot not assigned.");
                return;
            }

            if (Clip.UsdScene != null)
            {
                Clip.UsdScene.Close();
                Clip.UsdScene = null;
            }

            try {
                if (string.IsNullOrEmpty(Clip.m_usdFile))
                {
                    Clip.UsdScene = Scene.Create();
                }
                else
                {
                    Clip.UsdScene = Scene.Create(Clip.m_usdFile);
                }

                // Set the frame rate in USD  as well.
                //
                // This both set the "time samples per second" and the playback rate.
                // Setting times samples per second allows the authoring code to express samples as integer
                // values, avoiding floating point error; so by setting FrameRate = 60, the samples written
                // at time=0 through time=59 represent the first second of playback.
                //
                // Stage.TimeCodesPerSecond is set implicitly to 1 / FrameRate.
                //m_usdScene.FrameRate = Clip.frame;

                // When authoring in terms of seconds, at any frame rate the samles written at
                // time = 0.0 through time = 1.0 represent the first second of playback. The framerate
                // above will only be used as a target frame rate.
                //if (m_timeUnits == TimeCode.Seconds) {
                //  m_usdScene.Stage.SetTimeCodesPerSecond(1);
                //}

                // TODO: How does one extract the time mode (frames or seconds) from the Timeline?
                Clip.UsdScene.Stage.SetFramesPerSecond(30);
                Clip.UsdScene.Stage.SetTimeCodesPerSecond(1);

                // For simplicity in this example, adding game objects while recording is not supported.
                Clip.Context                 = new ExportContext();
                Clip.Context.scene           = Clip.UsdScene;
                Clip.Context.basisTransform  = Clip.m_convertHandedness;
                Clip.Context.activePolicy    = Clip.m_activePolicy;
                Clip.Context.exportMaterials = Clip.m_exportMaterials;

                Clip.UsdScene.StartTime = currentTime;

                // Export the "default" frame, that is, all data which doesn't vary over time.
                Clip.UsdScene.Time = null;
                SceneExporter.SyncExportContext(root, Clip.Context);
                SceneExporter.Export(root,
                                     Clip.Context,
                                     zeroRootTransform: false);
            } catch {
                if (Clip.UsdScene != null)
                {
                    Debug.LogError("Set scene to null");
                    Clip.UsdScene.Close();
                    Clip.UsdScene = null;
                }
                throw;
            }
        }