public virtual void ImportParametersFromUsd(Scene scene, string materialPath, MaterialSample materialSample, PreviewSurfaceSample previewSurf, SceneImportOptions options) { var primvars = new List <string>(); string uvPrimvar = null; ImportColorOrMap(scene, previewSurf.diffuseColor, false, options, ref DiffuseMap, ref Diffuse, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ImportColorOrMap(scene, previewSurf.emissiveColor, false, options, ref EmissionMap, ref Emission, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ImportValueOrMap(scene, previewSurf.normal, true, options, ref NormalMap, ref Normal, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ImportValueOrMap(scene, previewSurf.displacement, false, options, ref DisplacementMap, ref Displacement, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ImportValueOrMap(scene, previewSurf.occlusion, false, options, ref OcclusionMap, ref Occlusion, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ImportValueOrMap(scene, previewSurf.roughness, false, options, ref RoughnessMap, ref Roughness, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ImportValueOrMap(scene, previewSurf.clearcoat, false, options, ref ClearcoatMap, ref Clearcoat, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); ClearcoatRoughness = previewSurf.clearcoatRoughness.defaultValue; if (previewSurf.useSpecularWorkflow.defaultValue == 1) { ImportColorOrMap(scene, previewSurf.specularColor, false, options, ref SpecularMap, ref Specular, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); } else { ImportValueOrMap(scene, previewSurf.metallic, false, options, ref MetallicMap, ref Metallic, out uvPrimvar); MergePrimvars(uvPrimvar, primvars); } options.materialMap.SetPrimvars(materialPath, primvars); }
/// <summary> /// Optimizes the given import options for fast playback. This assumes that the asset was /// previously imported, therefore it disables import of the material and scene hierarchy. /// </summary> public static void PrepOptionsForTimeChange(ref SceneImportOptions options) { options.forceRebuild = false; options.materialImportMode = MaterialImportMode.None; options.meshOptions.debugShowSkeletonBindPose = false; options.meshOptions.debugShowSkeletonRestPose = false; options.meshOptions.generateLightmapUVs = false; options.importSkinWeights = false; // Note that tangent and Normals must be updated when the mesh deforms. options.importHierarchy = false; options.meshOptions.texcoord0 = ImportMode.Ignore; options.meshOptions.texcoord1 = ImportMode.Ignore; options.meshOptions.texcoord2 = ImportMode.Ignore; options.meshOptions.texcoord3 = ImportMode.Ignore; }
protected void ImportValueOrMap <T>(Scene scene, Connectable <T> usdParam, bool isNormalMap, SceneImportOptions options, ref Texture2D map, ref T?value, out string uvPrimvar) where T : struct { uvPrimvar = null; if (usdParam.IsConnected()) { map = MaterialImporter.ImportConnectedTexture(scene, usdParam, isNormalMap, options, out uvPrimvar); } else { value = usdParam.defaultValue; } }
public static void BuildSkeletonBone(string skelPath, GameObject go, Matrix4x4 restXform, VtTokenArray joints, SceneImportOptions importOptions) { // Perform change of basis, if needed. XformImporter.ImportXform(ref restXform, importOptions); // Decompose into TSR. Vector3 pos = Vector3.zero; Quaternion rot = Quaternion.identity; Vector3 scale = Vector3.one; if (!UnityTypeConverter.Decompose(restXform, out pos, out rot, out scale)) { throw new Exception("Failed to decompose bind trnsforms for <" + skelPath + ">"); } go.transform.localScale = scale; go.transform.localRotation = rot; go.transform.localPosition = pos; var cubeDebugName = "usdSkel_restPose_debug_cube"; if (importOptions.meshOptions.debugShowSkeletonRestPose) { var cube = go.transform.Find(cubeDebugName); if (!cube) { cube = GameObject.CreatePrimitive(PrimitiveType.Cube).transform; cube.name = cubeDebugName; cube.SetParent(go.transform, worldPositionStays: false); cube.localScale = Vector3.one * 2; } } else { var existing = go.transform.Find(cubeDebugName); if (existing) { GameObject.DestroyImmediate(existing.gameObject); } } }
/// <summary> /// Computes the bound material using UsdShade's inherited binding logic. /// If a material is bound, the request callback is executed to enable the caller to bind the /// material to the Unity geometry. /// </summary> public static void ProcessMaterialBindings(Scene scene, SceneImportOptions importOptions) { var requests = importOptions.materialMap.ClearRequestedBindings(); var prims = new pxr.UsdPrimVector(); foreach (var pathAndRequest in requests) { var prim = scene.GetPrimAtPath(pathAndRequest.Key); if (prim == null) { continue; } prims.Add(prim); } var matVector = pxr.UsdShadeMaterialBindingAPI.ComputeBoundMaterials(prims, materialBindToken); var matIndex = -1; foreach (pxr.UsdShadeMaterial usdMat in matVector) { matIndex++; Material unityMat = importOptions.materialMap[usdMat.GetPath()]; if (unityMat == null) { continue; } // PERF: this is slow and garbage-y. string meshPath = prims[matIndex].GetPath(); if (!requests.ContainsKey(meshPath)) { Debug.LogError("Source object key not found: " + meshPath); continue; } System.Collections.Generic.List <string> primvars = importOptions.materialMap.GetPrimvars(usdMat.GetPath()); requests[meshPath](scene, unityMat, primvars); } }
public static void MenuImportAsPrefab() { var scene = InitForOpen(); if (scene == null) { return; } string path = scene.FilePath; // Time-varying data is not supported and often scenes are written without "Default" time // values, which makes setting an arbitrary time safer (because if only default was authored // the time will be ignored and values will resolve to default time automatically). scene.Time = 1.0; var importOptions = new SceneImportOptions(); importOptions.projectAssetPath = GetSelectedAssetPath(); importOptions.changeHandedness = BasisTransformation.SlowAndSafe; importOptions.materialImportMode = MaterialImportMode.ImportDisplayColor; importOptions.usdRootPath = GetDefaultRoot(scene); var invalidChars = Path.GetInvalidFileNameChars(); var prefabName = string.Join("_", GetPrefabName(path).Split(invalidChars, System.StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.'); string prefabPath = importOptions.projectAssetPath + prefabName + ".prefab"; prefabPath = AssetDatabase.GenerateUniqueAssetPath(prefabPath); string clipName = Path.GetFileNameWithoutExtension(path); var go = new GameObject(GetObjectName(importOptions.usdRootPath, path)); try { UsdToGameObject(go, scene, importOptions); SceneImporter.SavePrefab(go, prefabPath, clipName, importOptions); } finally { GameObject.DestroyImmediate(go); scene.Close(); } }
/// <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); }
/// <summary> /// If there is a Payload authored on this prim, expose it so the user can change the /// load state. /// </summary> static void AddPayload(GameObject go, HierInfo info, SceneImportOptions options) { var pl = go.GetComponent <UsdPayload>(); if (!info.hasPayload) { if (pl) { Component.DestroyImmediate(pl); } return; } if (!pl) { pl = go.AddComponent <UsdPayload>(); pl.SetInitialState(options.payloadPolicy == PayloadPolicy.LoadAll); } }
public static GameObject ImportSceneAsGameObject(Scene scene, GameObject parent = null, SceneImportOptions importOptions = null) { if (scene == null || scene.Stage == null) { Debug.LogError("The USD Scene needs to be opened before being imported."); return(null); } string path = scene.FilePath; // Time-varying data is not supported and often scenes are written without "Default" time // values, which makes setting an arbitrary time safer (because if only default was authored // the time will be ignored and values will resolve to default time automatically). scene.Time = 1.0; if (importOptions == null) { importOptions = new SceneImportOptions(); importOptions.usdRootPath = GetDefaultRoot(scene); } GameObject root = new GameObject(GetObjectName(importOptions.usdRootPath, path)); if (parent != null) { root.transform.SetParent(parent.transform); } try { UsdToGameObject(root, scene, importOptions); return(root); } catch (SceneImporter.ImportException) { #if UNITY_EDITOR Object.DestroyImmediate(root); #else Object.Destroy(root); #endif return(null); } }
protected void ImportColorOrMap(Scene scene, Connectable <Vector3> usdParam, bool isNormalMap, SceneImportOptions options, ref Texture2D map, ref Color?value, out string uvPrimvar) { uvPrimvar = null; if (usdParam.IsConnected()) { map = MaterialImporter.ImportConnectedTexture(scene, usdParam, isNormalMap, options, out uvPrimvar); } else { var rgb = usdParam.defaultValue; value = new Color(rgb.x, rgb.y, rgb.z).gamma; } }
public static void ImportUsd(GameObject goRoot, Scene scene, PrimMap primMap, bool composingSubtree, SceneImportOptions importOptions) { if (scene == null) { throw new ImportException("Null USD Scene"); } scene.SetInterpolation(importOptions.interpolate ? Scene.InterpolationMode.Linear : Scene.InterpolationMode.Held); SceneImporter.BuildScene(scene, goRoot, importOptions, primMap, composingSubtree); }
static HierInfo[] BuildObjectLists(Scene scene, GameObject unityRoot, SdfPath usdRoot, PrimMap map, SceneImportOptions options) { if (map.SkelCache == null) { // Note that UsdSkelCache is thread safe and can be populated from multiple threads. map.SkelCache = new UsdSkelCache(); // The skelBindings dictionary, however, is not thread safe and must be populated after the // hierarchy discovery thread joins, in ProcessPaths. map.SkelBindings = new Dictionary <SdfPath, UsdSkelBindingVector>(); } BeginReading(scene, usdRoot, map, options).Complete(); ProcessPaths(ReadHierJob.result, scene, unityRoot, usdRoot, map, options); return(ReadHierJob.result); }
public static void MenuImportAsTimelineClip() { var scene = InitForOpen(); if (scene == null) { return; } string path = scene.FilePath; var invalidChars = Path.GetInvalidFileNameChars(); var prefabName = string.Join("_", GetPrefabName(path).Split(invalidChars, System.StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.'); string prefabPath = GetSelectedAssetPath() + prefabName + ".prefab"; prefabPath = AssetDatabase.GenerateUniqueAssetPath(prefabPath); string clipName = Path.GetFileNameWithoutExtension(path); var importOptions = new SceneImportOptions(); importOptions.projectAssetPath = GetSelectedAssetPath(); importOptions.changeHandedness = BasisTransformation.FastWithNegativeScale; importOptions.materialImportMode = MaterialImportMode.ImportDisplayColor; importOptions.usdRootPath = GetDefaultRoot(scene); var go = new GameObject(GetObjectName(importOptions.usdRootPath, path)); try { // Ensure we have at least one GameObject with the import settings. XformImporter.BuildSceneRoot(scene, go.transform, importOptions); SceneImporter.SavePrefab(go, prefabPath, clipName, importOptions); } finally { GameObject.DestroyImmediate(go); } }
/// <summary> /// Copies the transform value from USD to Unity, optionally changing handedness in the /// process. /// </summary> public static void BuildXform(pxr.SdfPath path, XformableSample usdXf, GameObject go, SceneImportOptions options, Scene scene) { // If there is an access mask and it's not initially being populated, check to see if the // transform for this object actually varies over time, if not, we can simply return. if (scene.AccessMask != null && !scene.IsPopulatingAccessMask) { System.Reflection.MemberInfo transformMember = null; transformMember = usdXf.GetType().GetMember("transform")[0]; HashSet <System.Reflection.MemberInfo> members; if (!scene.AccessMask.Included.TryGetValue(path, out members) || !members.Contains(transformMember)) { return; } } BuildXform(usdXf.transform, go, options); }
/// <summary> /// Rebuilds the USD scene as Unity GameObjects, maintaining a mapping from USD to Unity. /// </summary> public static PrimMap BuildScene(Scene scene, GameObject root, SceneImportOptions importOptions, PrimMap primMap, bool composingSubtree) { try { Profiler.BeginSample("USD: Build Scene"); var builder = BuildScene(scene, root, importOptions, primMap, 0, composingSubtree); while (builder.MoveNext()) { } return(primMap); } finally { Profiler.EndSample(); } }
static void MenuExportLoadFromUsd() { var scene = InitForOpen(); if (scene == null) { return; } string path = scene.FilePath; // Time-varying data is not supported and often scenes are written without "Default" time // values, which makes setting an arbitrary time safer (because if only default was authored // the time will be ignored and values will resolve to default time automatically). scene.Time = 1.0; var importOptions = new SceneImportOptions(); importOptions.projectAssetPath = GetSelectedAssetPath(); importOptions.changeHandedness = BasisTransformation.SlowAndSafe; importOptions.materialImportMode = MaterialImportMode.ImportDisplayColor; importOptions.usdRootPath = GetDefaultRoot(scene); importOptions.importMonoBehaviours = true; GameObject root = new GameObject(GetObjectName(importOptions.usdRootPath, path)); if (Selection.gameObjects.Length > 0) { root.transform.SetParent(Selection.gameObjects[0].transform); } try { UsdToGameObject(root, scene, importOptions); } finally { scene.Close(); } }
/// <summary> /// Given an array of bone names (HierInfo.skelJoints), creates GameObjects under unityRoot. /// </summary> static void ExpandSkeleton(HierInfo info, GameObject unityRoot, SdfPath usdRoot, UsdPrim prim, PrimMap map, SceneImportOptions options) { foreach (var joint in info.skelJoints) { var path = joint; GameObject parentGo = null; if (!map.TryGetValue(path.GetParentPath(), out parentGo)) { // This will happen when the joints are discontinuous, for example: // // Foo/Bar // Foo/Bar/Baz/Qux // // Baz is implicitly defined, which is allowed by UsdSkel. CreateAncestors(path, map, unityRoot, usdRoot, options, out parentGo); if (!parentGo) { Debug.LogException(new Exception("Failed to create ancestors for " + path + " for prim: " + prim.GetPath())); continue; } } Transform child = parentGo.transform.Find(path.GetName()); if (!child) { child = new GameObject(path.GetName()).transform; child.SetParent(parentGo.transform, worldPositionStays: false); } map[path] = child.gameObject; } }
// -------------------------------------------------------------------------------------------- // // Deserialize USD to -> Unity // -------------------------------------------------------------------------------------------- // static public void ImportObject(Scene scene, GameObject go, pxr.UsdPrim usdPrim, SceneImportOptions options) { if (!options.importMonoBehaviours) { return; } var comps = usdPrim.GetAuthoredPropertiesInNamespace("unity:component"); foreach (var compProp in comps) { var compAttr = usdPrim.GetAttribute(compProp.GetName()); string assemblyQualifiedName = (string)compAttr.Get(0); var compType = System.Type.GetType(assemblyQualifiedName); // TODO: Handle multiple components of the same type. Component comp = go.GetComponent(compType); if (comp == null) { comp = go.AddComponent(compType); } var so = new SerializedObject(comp); var prop = so.GetIterator(); prop.Next(true); var sb = new System.Text.StringBuilder(); // TODO: Handle multiple components of the same type. PropertyFromUsd(usdPrim, prop, sb, comp.GetType().Name); so.ApplyModifiedProperties(); Debug.Log(sb.ToString()); } }
static void ProcessPaths(HierInfo[] infos, Scene scene, GameObject unityRoot, SdfPath usdRoot, PrimMap map, SceneImportOptions options) { Profiler.BeginSample("Process all paths"); foreach (var info in infos) { var prim = info.prim; var path = info.prim.GetPath(); if (info.skelBindings != null) { // Collect all discovered skelBindings back into the PrimMap. map.SkelBindings.Add(info.prim.GetPath(), info.skelBindings); } GameObject go; if (path == usdRoot) { go = unityRoot; } else { GameObject parentGo = null; CreateAncestors(path, map, unityRoot, usdRoot, options, out parentGo); if (!parentGo) { Debug.LogWarning("Parent path not found for child: " + path.ToString()); continue; } var parent = parentGo ? parentGo.transform : null; if (!map.TryGetValue(path, out go)) { go = FindOrCreateGameObject(parent, path, unityRoot.transform, map, options); } } if (options.importSceneInstances) { Profiler.BeginSample("Add Scene Instance Root"); if (prim.IsInstance()) { map.AddInstanceRoot(prim.GetPath(), go, prim.GetMaster().GetPath()); } Profiler.EndSample(); } if (!options.importHierarchy) { continue; } ApplySelfVisibility(go, prim); try { Profiler.BeginSample("Add Model Root"); AddModelRoot(go, info); Profiler.EndSample(); Profiler.BeginSample("Add Variant Set"); AddVariantSet(go, prim); Profiler.EndSample(); Profiler.BeginSample("Add Payload"); AddPayload(go, info, options); Profiler.EndSample(); } catch (Exception ex) { Debug.LogException(new Exception("Error processing " + prim.GetPath(), ex)); } } Profiler.EndSample(); }
/// <summary> /// Map all UsdPrims and build Unity GameObjects, reconstructing the parent relationship. /// </summary> /// <remarks> /// When forceRebuild is true, game objects will be destroyed and recreated. If buildHierarchy /// is false, the primMap will be populated, but missing game objects will not be created. /// </remarks> static public PrimMap BuildGameObjects(Scene scene, GameObject unityRoot, SdfPath usdRoot, IEnumerable <SdfPath> paths, PrimMap map, SceneImportOptions options) { map[usdRoot] = unityRoot; // Like all GameObjects imported from USD, ensure the root has a UsdPrimSource. if (unityRoot.GetComponent <UsdPrimSource>() == null) { var ua = unityRoot.AddComponent <UsdPrimSource>(); ua.m_usdPrimPath = usdRoot.ToString(); } Profiler.BeginSample("Build Object Lists"); var hierInfo = BuildObjectLists(scene, unityRoot, usdRoot, map, options); Profiler.EndSample(); // TODO: Should recurse to discover deeply nested instancing. // TODO: Generates garbage for every prim, but we expect few masters. if (options.importPointInstances || options.importSceneInstances) { Profiler.BeginSample("Build Masters"); foreach (var masterRootPrim in scene.Stage.GetMasters()) { var goMaster = FindOrCreateGameObject(unityRoot.transform, masterRootPrim.GetPath(), unityRoot.transform, map, options); goMaster.hideFlags = HideFlags.HideInHierarchy; goMaster.SetActive(false); map.AddMasterRoot(masterRootPrim.GetPath(), goMaster); try { var info = new HierInfo(); info.prim = masterRootPrim; ReadModelInfo(ref info); AddModelRoot(goMaster, info); AddVariantSet(goMaster, masterRootPrim); } catch (Exception ex) { Debug.LogException(new Exception("Error processing " + masterRootPrim.GetPath(), ex)); } foreach (var usdPrim in masterRootPrim.GetDescendants()) { var parentPath = usdPrim.GetPath().GetParentPath(); Transform parentXf = null; if (parentPath == masterRootPrim.GetPath()) { parentXf = goMaster.transform; } else { parentXf = map[parentPath].transform; } var goPrim = FindOrCreateGameObject(parentXf, usdPrim.GetPath(), unityRoot.transform, map, options); ApplySelfVisibility(goPrim, usdPrim); if (usdPrim.IsInstance()) { map.AddInstanceRoot(usdPrim.GetPath(), goPrim, usdPrim.GetMaster().GetPath()); } try { var info = new HierInfo(); info.prim = usdPrim; ReadModelInfo(ref info); AddModelRoot(goPrim, info); AddVariantSet(goPrim, usdPrim); } catch (Exception ex) { Debug.LogException(new Exception("Error processing " + usdPrim.GetPath(), ex)); continue; } } } Profiler.EndSample(); } if (options.importSkinning) { Profiler.BeginSample("Expand Skeletons"); foreach (var info in hierInfo) { if (info.skelJoints == null || info.skelJoints.Length == 0) { continue; } try { ExpandSkeleton(info, unityRoot, usdRoot, info.prim, map, options); } catch (Exception ex) { Debug.LogException(new Exception("Error expanding skeleton at " + info.prim.GetPath(), ex)); } } Profiler.EndSample(); } return(map); }
BeginReading(Scene scene, SdfPath usdRoot, PrimMap map, SceneImportOptions options) { FindPathsJob.usdRoot = usdRoot; FindPathsJob.scene = scene; FindPathsJob.results = new SdfPath[8][]; FindPathsJob.queries = new FindPathsJob.IQuery[8]; if (options.ShouldBindMaterials) { FindPathsJob.queries[0] = (FindPathsJob.IQuery) new FindPathsJob.Query <MaterialSample>(); } if (options.importCameras) { FindPathsJob.queries[1] = (FindPathsJob.IQuery) new FindPathsJob.Query <CameraSample>(); } if (options.importMeshes) { FindPathsJob.queries[2] = (FindPathsJob.IQuery) new FindPathsJob.Query <MeshSample>(); FindPathsJob.queries[3] = (FindPathsJob.IQuery) new FindPathsJob.Query <CubeSample>(); FindPathsJob.queries[4] = (FindPathsJob.IQuery) new FindPathsJob.Query <SphereSample>(); } FindPathsJob.queries[5] = (FindPathsJob.IQuery) new FindPathsJob.Query <SkelRootSample>(); if (options.importSkinning) { FindPathsJob.queries[6] = (FindPathsJob.IQuery) new FindPathsJob.Query <SkeletonSample>(); } if (options.importTransforms) { FindPathsJob.queries[7] = (FindPathsJob.IQuery) new FindPathsJob.Query <XformSample>(); } var findPathsJob = new FindPathsJob(); #if !UNITY_2017 var findHandle = findPathsJob.Schedule(FindPathsJob.queries.Length, 1); findHandle.Complete(); #else findPathsJob.Run(); #endif map.Materials = FindPathsJob.results[0]; map.Cameras = FindPathsJob.results[1]; map.Meshes = FindPathsJob.results[2]; map.Cubes = FindPathsJob.results[3]; map.Spheres = FindPathsJob.results[4]; map.SkelRoots = FindPathsJob.results[5]; map.Skeletons = FindPathsJob.results[6]; map.Xforms = FindPathsJob.results[7]; ReadHierJob.paths = FindPathsJob.results.Where(i => i != null).SelectMany(i => i).ToArray(); ReadHierJob.result = new HierInfo[ReadHierJob.paths.Length]; ReadHierJob.scene = scene; ReadHierJob.skelCache = map.SkelCache; var readHierInfo = new ReadHierJob(); #if !UNITY_2017 return(readHierInfo.Schedule(ReadHierJob.paths.Length, 8, dependsOn: findHandle)); #else readHierInfo.Run(); return; #endif }
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()); }
/// <summary> /// Custom importer. This works almost exactly as the ScriptedImporter, but does not require /// the new API. /// </summary> public static void SavePrefab(GameObject rootObject, string prefabPath, string playableClipName, SceneImportOptions importOptions) { Directory.CreateDirectory(Path.GetDirectoryName(prefabPath)); GameObject oldPrefab = AssetDatabase.LoadAssetAtPath <GameObject>(prefabPath); GameObject prefab = null; if (oldPrefab == null) { // Create the prefab. At this point, the meshes do not yet exist and will be // dangling references #if UNITY_2018_3_OR_NEWER prefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); #else prefab = PrefabUtility.CreatePrefab(prefabPath, rootObject); #endif HashSet <Mesh> meshes; HashSet <Material> materials; AddObjectsToAsset(rootObject, prefab, importOptions, out meshes, out materials); foreach (var mesh in meshes) { AssetDatabase.AddObjectToAsset(mesh, prefab); } foreach (var mat in materials) { AssetDatabase.AddObjectToAsset(mat, prefab); } // Fix the dangling references. #if UNITY_2018_3_OR_NEWER prefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); #else prefab = PrefabUtility.ReplacePrefab(rootObject, prefab); #endif var playable = ScriptableObject.CreateInstance <UsdPlayableAsset>(); playable.SourceUsdAsset.defaultValue = prefab.GetComponent <UsdAsset>(); playable.name = playableClipName; AssetDatabase.AddObjectToAsset(playable, prefab); #if UNITY_2018_3_OR_NEWER prefab = PrefabUtility.SavePrefabAsset(prefab); #endif } else { HashSet <Mesh> meshes; HashSet <Material> materials; #if UNITY_2018_3_OR_NEWER oldPrefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); #endif AddObjectsToAsset(rootObject, oldPrefab, importOptions, out meshes, out materials); // ReplacePrefab only removes the GameObjects from the asset. // Clear out all non-prefab junk (ie, meshes), because otherwise it piles up. // The main difference between LoadAllAssetRepresentations and LoadAllAssets // is that the former returns MonoBehaviours and the latter does not. foreach (var obj in AssetDatabase.LoadAllAssetRepresentationsAtPath(prefabPath)) { if (obj is GameObject) { continue; } if (obj is Mesh && meshes.Contains((Mesh)obj)) { meshes.Remove((Mesh)obj); continue; } if (obj is Material && materials.Contains((Material)obj)) { materials.Remove((Material)obj); continue; } Object.DestroyImmediate(obj, allowDestroyingAssets: true); } foreach (var mesh in meshes) { AssetDatabase.AddObjectToAsset(mesh, oldPrefab); } foreach (var mat in materials) { AssetDatabase.AddObjectToAsset(mat, oldPrefab); } #if UNITY_2018_3_OR_NEWER prefab = PrefabUtility.SaveAsPrefabAsset(rootObject, prefabPath); #else if (oldPrefab != rootObject) { prefab = PrefabUtility.ReplacePrefab( rootObject, oldPrefab, ReplacePrefabOptions.ReplaceNameBased); } else { prefab = oldPrefab; } #endif var playable = ScriptableObject.CreateInstance <UsdPlayableAsset>(); playable.SourceUsdAsset.defaultValue = prefab.GetComponent <UsdAsset>(); playable.name = playableClipName; AssetDatabase.AddObjectToAsset(playable, prefab); #if UNITY_2018_3_OR_NEWER PrefabUtility.SavePrefabAsset(prefab); #endif } AssetDatabase.ImportAsset(prefabPath, ImportAssetOptions.ForceUpdate); AssetDatabase.SaveAssets(); }
public static void BuildPointInstances(Scene scene, PrimMap primMap, string pointInstancerPath, PointInstancerSample sample, GameObject root, SceneImportOptions options) { Matrix4x4[] transforms = sample.ComputeInstanceMatrices(scene, pointInstancerPath); int i = 0; foreach (var protoRoot in sample.prototypes.targetPaths) { GameObject go; if (!primMap.TryGetValue(new pxr.SdfPath(protoRoot), out go)) { Debug.LogWarning("Proto not found in PrimMap: " + protoRoot); continue; } go.SetActive(false); if (options.enableGpuInstancing) { EnableGpuInstancing(go); } } var inactiveIds = new System.Collections.Generic.HashSet <long>(); /* * Disabled until this bug is resolved: * https://github.com/PixarAnimationStudios/USD/issues/639 * * if (sample.inactiveIds != null) { * foreach (long id in sample.inactiveIds.GetExplicitItems()) { * inactiveIds.Add(id); * } * } */ foreach (var index in sample.protoIndices) { if (inactiveIds.Contains(index)) { continue; } if (index >= sample.prototypes.targetPaths.Length) { Debug.LogWarning("ProtoIndex out of bounds: [" + index + "] " + "for instancer: " + pointInstancerPath); continue; } var targetPath = sample.prototypes.targetPaths[index]; GameObject goMaster; if (!primMap.TryGetValue(new pxr.SdfPath(targetPath), out goMaster)) { Debug.LogWarning("Proto not found in PrimMap: " + targetPath); continue; } if (i >= transforms.Length) { Debug.LogWarning("No transform for instance index [" + i + "] " + "for instancer: " + pointInstancerPath); break; } var xf = transforms[i]; var goInstance = GameObject.Instantiate(goMaster, root.transform); goInstance.SetActive(true); goInstance.name = goMaster.name + "_" + i; XformImporter.BuildXform(xf, goInstance, options); i++; } }
/// <summary> /// Applies the contents of this USD file to a foreign root object. /// </summary> /// <remarks> /// The idea here is that one may have many animation clips, but only a single GameObject in /// the Unity scenegraph. /// </remarks> public void SetTime(double time, UsdAsset foreignRoot, bool saveMeshUpdates) { var scene = GetScene(); if (scene == null) { Debug.LogWarning("Null scene from GetScene() at " + UnityTypeConverter.GetPath(transform)); return; } // Careful not to update any local members here, if this data is driven from a prefab, we // dont want those changes to be baked back into the asset. time += foreignRoot.m_usdTimeOffset; float usdTime = (float)(scene.StartTime + time * scene.Stage.GetTimeCodesPerSecond()); if (usdTime > scene.EndTime) { return; } if (usdTime < scene.StartTime) { return; } scene.Time = usdTime; var options = new SceneImportOptions(); foreignRoot.StateToOptions(ref options); PrepOptionsForTimeChange(ref options); if (foreignRoot.m_lastPrimMap == null) { foreignRoot.m_lastPrimMap = new PrimMap(); options.importHierarchy = true; } if (m_usdVariabilityCache) { if (m_lastAccessMask == null) { m_lastAccessMask = new AccessMask(); scene.IsPopulatingAccessMask = true; } } else { m_lastAccessMask = null; } if (m_debugPrintVariabilityCache && m_lastAccessMask != null && !scene.IsPopulatingAccessMask) { var sb = new System.Text.StringBuilder(); foreach (var kvp in m_lastAccessMask.Included) { sb.AppendLine(kvp.Key); foreach (var member in kvp.Value) { sb.AppendLine(" ." + member.Name); } sb.AppendLine(); } Debug.Log(sb.ToString()); } scene.AccessMask = m_lastAccessMask; SceneImporter.ImportUsd(foreignRoot.gameObject, scene, foreignRoot.m_lastPrimMap, options); scene.AccessMask = null; if (m_lastAccessMask != null) { scene.IsPopulatingAccessMask = false; } }
/// <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(); }
/// <summary> /// Build the root of a scene under which more USD data will be imported. If the handedness /// is changed here, no subsequent changes are required below, however the root will contain /// a negative scale. /// </summary> public static void BuildSceneRoot(Scene scene, Transform root, SceneImportOptions options) { var stageRoot = root.GetComponent <UsdAsset>(); bool newStageRoot = false; if (stageRoot == null) { stageRoot = root.gameObject.AddComponent <UsdAsset>(); stageRoot.usdFullPath = scene.FilePath; newStageRoot = true; ImporterBase.MoveComponentFirst(stageRoot); stageRoot.OptionsToState(options); } if (newStageRoot || options.changeHandedness != stageRoot.LastHandedness || options.scale != stageRoot.LastScale || options.forceRebuild) { var localScale = root.transform.localScale; var localRotation = root.transform.localRotation; if (options.forceRebuild) { localScale = Vector3.one; localRotation = Quaternion.identity; } else if (!newStageRoot) { // Undo the previous transforms. UndoRootTransform(scene, stageRoot, ref localScale, ref localRotation); } stageRoot.LastScale = options.scale; stageRoot.LastHandedness = options.changeHandedness; // Handle configurable up-axis (Y or Z). float invert = options.changeHandedness == BasisTransformation.FastWithNegativeScale ? -1 : 1; if (scene.UpAxis == Scene.UpAxes.Z) { localRotation *= Quaternion.AngleAxis(invert * 90, Vector3.right); } if (options.changeHandedness == BasisTransformation.FastWithNegativeScale) { // Convert from right-handed (USD) to left-handed (Unity). if (scene.UpAxis == Scene.UpAxes.Z) { localScale.y *= -1; } else { localScale.z *= -1; } } if (Mathf.Abs(options.scale - 1.0f) > 0.0001) { // Unilaterally setting the scale here is a little wrong, since it will stomp the root // object scale if set in Unity. localScale *= options.scale; } root.transform.localScale = localScale; root.transform.localRotation = localRotation; } }
static void AddObjectsToAsset(GameObject rootObject, Object asset, SceneImportOptions importOptions, out HashSet <Mesh> usedMeshes, out HashSet <Material> usedMaterials) { var meshes = new HashSet <Mesh>(); var materials = new HashSet <Material>(); materials.Add(importOptions.materialMap.DisplayColorMaterial); materials.Add(importOptions.materialMap.MetallicWorkflowMaterial); materials.Add(importOptions.materialMap.SpecularWorkflowMaterial); var tempMat = importOptions.materialMap.DisplayColorMaterial; if (tempMat != null && AssetDatabase.GetAssetPath(tempMat) == "") { materials.Add(tempMat); } tempMat = importOptions.materialMap.MetallicWorkflowMaterial; if (tempMat != null && AssetDatabase.GetAssetPath(tempMat) == "") { materials.Add(tempMat); } tempMat = importOptions.materialMap.SpecularWorkflowMaterial; if (tempMat != null && AssetDatabase.GetAssetPath(tempMat) == "") { materials.Add(tempMat); } foreach (var mf in rootObject.GetComponentsInChildren <MeshFilter>()) { if (!mf) { continue; } if (mf.sharedMesh != null && meshes.Add(mf.sharedMesh)) { mf.sharedMesh.name = mf.name; } } foreach (var mf in rootObject.GetComponentsInChildren <MeshRenderer>()) { if (!mf) { continue; } foreach (var mat in mf.sharedMaterials) { if (mat != null && !materials.Add(mat)) { mat.name = mf.name; continue; } } } foreach (var mf in rootObject.GetComponentsInChildren <SkinnedMeshRenderer>()) { if (!mf) { continue; } if (mf.sharedMesh != null && meshes.Add(mf.sharedMesh)) { mf.sharedMesh.name = mf.name; } foreach (var mat in mf.sharedMaterials) { if (mat != null && !materials.Add(mat)) { mat.name = mf.name; continue; } } } usedMeshes = meshes; usedMaterials = materials; }
/// <summary> /// Builds a Unity Material from the given USD material sample. /// </summary> public static Material BuildMaterial(Scene scene, string materialPath, MaterialSample sample, SceneImportOptions options) { if (string.IsNullOrEmpty(sample.surface.connectedPath)) { return(null); } var previewSurf = new UnityPreviewSurfaceSample(); scene.Read(new pxr.SdfPath(sample.surface.connectedPath).GetPrimPath(), previewSurf); // Currently, only UsdPreviewSurface is supported. if (previewSurf.id == null || previewSurf.id != "UsdPreviewSurface") { Debug.LogWarning("Unknown surface type: <" + sample.surface.connectedPath + ">" + "Surface ID: " + previewSurf.id); return(null); } Material mat = null; if (options.materialMap.useOriginalShaderIfAvailable && !string.IsNullOrEmpty(previewSurf.unity.shaderName)) { // We may or may not have the original shader. var shader = Shader.Find(previewSurf.unity.shaderName); if (shader) { mat = new Material(shader); mat.shaderKeywords = previewSurf.unity.shaderKeywords; } else { Debug.LogWarning("Original shader not found: " + previewSurf.unity.shaderName); } } if (mat == null) { if (previewSurf.useSpecularWorkflow.defaultValue == 1) { // Metallic workflow. mat = Material.Instantiate(options.materialMap.SpecularWorkflowMaterial); } else { // Metallic workflow. mat = Material.Instantiate(options.materialMap.MetallicWorkflowMaterial); } } foreach (var kvp in previewSurf.unity.colorArgs) { mat.SetColor(kvp.Key, kvp.Value); } foreach (var kvp in previewSurf.unity.floatArgs) { mat.SetFloat(kvp.Key, kvp.Value); } foreach (var kvp in previewSurf.unity.vectorArgs) { mat.SetVector(kvp.Key, kvp.Value); } var pipeline = UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset; if (!pipeline) { var matAdapter = new StandardShaderImporter(mat); matAdapter.ImportParametersFromUsd(scene, materialPath, sample, previewSurf, options); matAdapter.ImportFromUsd(); } else if (pipeline.GetType().Name == "HDRenderPipelineAsset") { // Robustness: Comparing a strng ^ here is not great, but there is no other option. var matAdapter = new HdrpShaderImporter(mat); matAdapter.ImportParametersFromUsd(scene, materialPath, sample, previewSurf, options); matAdapter.ImportFromUsd(); } else { // Fallback to the Standard importer, which may pickup some attributes by luck. var matAdapter = new StandardShaderImporter(mat); matAdapter.ImportParametersFromUsd(scene, materialPath, sample, previewSurf, options); matAdapter.ImportFromUsd(); } // Get the material name from the path if (mat != null && !string.IsNullOrEmpty(materialPath)) { mat.name = new pxr.SdfPath(materialPath).GetName(); } return(mat); }
public static Texture2D ImportConnectedTexture <T>(Scene scene, Connectable <T> connection, bool isNormalMap, SceneImportOptions options, out string uvPrimvar) { uvPrimvar = null; // TODO: look for the expected texture/primvar reader pair. var textureSample = new TextureReaderSample(); var connectedPrimPath = scene.GetSdfPath(connection.connectedPath).GetPrimPath(); Texture2D result = null; scene.Read(connectedPrimPath, textureSample); if (textureSample.file.defaultValue != null && !string.IsNullOrEmpty(textureSample.file.defaultValue.GetResolvedPath())) { if (OnResolveTexture != null) { result = OnResolveTexture(textureSample.file.defaultValue, isNormalMap, options); } else { result = DefaultTextureResolver(textureSample.file.defaultValue, isNormalMap, options); } } Connectable <Vector2> st = textureSample.st; if (st != null && st.IsConnected() && !string.IsNullOrEmpty(st.connectedPath)) { var pvSrc = new PrimvarReaderSample <Vector2>(); scene.Read(new pxr.SdfPath(textureSample.st.connectedPath).GetPrimPath(), pvSrc); if (pvSrc.varname != null) { if (pvSrc.varname.IsConnected()) { var connPath = new pxr.SdfPath(pvSrc.varname.GetConnectedPath()); var attr = scene.GetAttributeAtPath(connPath); if (attr != null) { var value = attr.Get(scene.Time); uvPrimvar = pxr.UsdCs.VtValueToTfToken(value).ToString(); } else { Debug.LogWarning("No primvar name was provided at the connected path: " + connPath); uvPrimvar = ""; } } else if (pvSrc.varname.defaultValue != null) { // Ask the mesh importer to load the specified texcoord. // This must be a callback, since materials-to-meshes are one-to-many. uvPrimvar = pvSrc.varname.defaultValue; } } } return(result); }