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