/// <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); }
/// <summary> /// Given a PrimMap, finds all instanced objects and their respective master objects and /// instantiates Unity clones using GameObject.Instantiate. Note that this does not result /// in GPU instancing. /// </summary> public static void BuildSceneInstances(PrimMap primMap, SceneImportOptions options) { if (options.enableGpuInstancing) { foreach (var masterPath in primMap.GetMasterRootPaths()) { EnableGpuInstancing(primMap[masterPath]); } } foreach (var instance in primMap.GetInstanceRoots()) { GameObject goInstance = instance.gameObject; GameObject goMaster = primMap[instance.masterPath]; foreach (Transform child in goMaster.transform) { Transform newChild = goInstance.transform.Find(child.name); if (newChild == null) { newChild = GameObject.Instantiate(child.gameObject).transform; newChild.name = child.name; newChild.transform.SetParent(goInstance.transform, worldPositionStays: false); } primMap.AddInstance(newChild.gameObject); } } }
/// <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); }
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>(); } #if !UNITY_2017 BeginReading(scene, usdRoot, map, options).Complete(); #else BeginReading(scene, usdRoot, map, options); #endif ProcessPaths(ReadHierJob.result, scene, unityRoot, usdRoot, map, options); return(ReadHierJob.result); }
public static void ImportUsd(GameObject goRoot, Scene scene, PrimMap primMap, SceneImportOptions importOptions) { ImportUsd(goRoot, scene, primMap, false, importOptions); }
/// <summary> /// A private event that fires whenever the USD scene was reimported. /// </summary> private void OnReload() { m_lastPrimMap = null; m_lastAccessMask = null; if (m_lastScene != null) { m_lastScene.Close(); m_lastScene = null; } }
/// <summary> /// Reimports the USD scene, either fully rebuilding every object or updating them in-place. /// </summary> /// <param name="forceRebuild">Destroys each GameObject before reimporting.</param> public void Reload(bool forceRebuild) { var options = new SceneImportOptions(); StateToOptions(ref options); options.forceRebuild = forceRebuild; if (string.IsNullOrEmpty(options.projectAssetPath)) { options.projectAssetPath = "Assets/"; OptionsToState(options); } var root = gameObject; string assetPath = GetPrefabAssetPath(root); // The prefab asset path will be null for prefab instances. // When the assetPath is not null, the object is the prefab itself. if (!string.IsNullOrEmpty(assetPath)) { if (options.forceRebuild) { DestroyAllImportedObjects(); } SceneImporter.ImportUsd(root, GetScene(), new PrimMap(), options); #if UNITY_EDITOR string clipName = Path.GetFileNameWithoutExtension(usdFullPath); // As an optimization, we could detect if any meshes or materials were created and only // rebuild the prefab in those cases. SceneImporter.SavePrefab(root, assetPath, clipName, options); #endif } else { // An instance of a prefab or a vanilla game object. // Just reload the scene into memory and let the user decide if they want to send those // changes back to the prefab or not. if (forceRebuild) { // First, destroy all existing USD game objects. DestroyAllImportedObjects(); } m_lastScene = null; m_lastPrimMap = null; m_lastAccessMask = null; SceneImporter.ImportUsd(root, GetScene(), new PrimMap(), options); } }
// Creates ancestors, but note that this method does not apply visibility, since it was // designed to create bones, which cannot have visibility opinions in USD. static void CreateAncestors(SdfPath path, PrimMap map, GameObject unityRoot, SdfPath usdRoot, SceneImportOptions options, out GameObject parentGo) { var parentPath = path.GetParentPath(); if (path == parentPath) { Debug.LogException(new Exception("Parent path was identical to current path: " + path.ToString())); parentGo = null; return; } if (map.TryGetValue(parentPath, out parentGo) && parentGo) { return; } // Base case. if (parentPath == usdRoot) { map[parentPath] = unityRoot; return; } if (parentPath == kAbsoluteRootPath) { // Something went wrong. Debug.LogException(new Exception( "Error: unexpected path </> creating ancestors for <" + usdRoot.ToString() + ">")); } // Recursive case. // First, get the grandparent (parent's parent). GameObject grandparentGo; CreateAncestors(parentPath, map, unityRoot, usdRoot, options, out grandparentGo); if (!grandparentGo) { Debug.LogError("Failed to find ancestor for " + parentPath); return; } // Then find/create the current parent. parentGo = FindOrCreateGameObject(grandparentGo.transform, parentPath, unityRoot.transform, map, options); }
public static void WriteSparseOverrides(Scene scene, PrimMap primMap, BasisTransformation changeHandedness, float tolerance = 0.0001f) { var oldMode = scene.WriteMode; scene.WriteMode = Scene.WriteModes.Over; try { foreach (var path in scene.Find <XformableSample>()) { GameObject go; if (!primMap.TryGetValue(path, out go)) { continue; } var tx = go.transform; var xfNew = XformSample.FromTransform(tx); var xfOld = new XformSample(); scene.Read(path, xfOld); bool areClose = true; for (int i = 0; i < 16; i++) { if (Mathf.Abs(xfNew.transform[i] - xfOld.transform[i]) > tolerance) { areClose = false; break; } } if (areClose) { continue; } if (changeHandedness == BasisTransformation.SlowAndSafe) { xfNew.ConvertTransform(); } scene.Write(path, xfNew); } } finally { scene.WriteMode = oldMode; } }
static HierInfo[] BuildObjectLists(Scene scene, GameObject unityRoot, SdfPath usdRoot, PrimMap map, SceneImportOptions options) { #if !UNITY_2017 BeginReading(scene, usdRoot, map, options).Complete(); #else BeginReading(scene, usdRoot, map, options); #endif ProcessPaths(ReadHierJob.result, scene, unityRoot, usdRoot, map, options); return(ReadHierJob.result); }
/// <summary> /// Checks for a child named "name" under the given parent, if it exists it is returned, /// else a new child is created with this name. /// </summary> static GameObject FindOrCreateGameObject(Transform parent, SdfPath path, Transform unityRoot, PrimMap primMap, SceneImportOptions options) { Transform root = null; GameObject go = null; string name = path.GetName(); if (parent == null) { go = GameObject.Find(name); root = go ? go.transform : null; } else { root = parent.Find(name); go = root ? root.gameObject : null; } if (!go) { // TODO: this should really not construct a game object if ImportHierarchy is false, // but it requires all downstream code be driven by the primMap instead of finding prims // via the usd scene. In addition, this requies the prim map to store lists of prims by // type, e.g. cameras, meshes, cubes, etc. go = new GameObject(name); } if (!go.GetComponent <UsdPrimSource>()) { var ua = go.AddComponent <UsdPrimSource>(); ua.m_usdPrimPath = path.ToString(); } if (parent != null) { go.transform.SetParent(parent, worldPositionStays: false); } Profiler.BeginSample("Add to PrimMap"); primMap[path] = go; Profiler.EndSample(); return(go); }
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); }
/// <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(); } }
/// <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; } }
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); }
static JobHandle BeginReading(Scene scene, SdfPath usdRoot, PrimMap map, SceneImportOptions options) { FindPathsJob.usdRoot = usdRoot; FindPathsJob.scene = scene; FindPathsJob.results = new SdfPath[9][]; FindPathsJob.queries = new FindPathsJob.IQuery[9]; 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>(); } FindPathsJob.queries[8] = (FindPathsJob.IQuery) new FindPathsJob.Query <ScopeSample>(); var findPathsJob = new FindPathsJob(); var findHandle = findPathsJob.Schedule(FindPathsJob.queries.Length, 1); findHandle.Complete(); // Note that Scope prims are taken into account when building the hierarchy but not added to the PrimMap // This is because Scopes don't need specific import/export logic for now: // * they don't hold any data ton convert on the way in // * being represented as Xforms in Unity they get automatically exported (as Xform) as part of the parent hierarchy of any // valid prim // This will need to change if/when we want proper round tripping. 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(); return(readHierInfo.Schedule(ReadHierJob.paths.Length, 8, dependsOn: findHandle)); }
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 }
public static void BuildSkinnedMesh(string meshPath, string skelPath, SkeletonSample skeleton, UsdSkelSkinningQuery skinningQuery, GameObject go, PrimMap primMap, SceneImportOptions options) { // The mesh renderer must already exist, since hte mesh also must already exist. var smr = go.GetComponent <SkinnedMeshRenderer>(); if (!smr) { throw new Exception( "Error importing " + meshPath + " SkinnnedMeshRenderer not present on GameObject" ); } // Get and validate the joint weights and indices informations. UsdGeomPrimvar jointWeights = skinningQuery.GetJointWeightsPrimvar(); UsdGeomPrimvar jointIndices = skinningQuery.GetJointIndicesPrimvar(); if (!jointWeights.IsDefined() || !jointIndices.IsDefined()) { throw new Exception("Joints information (indices and/or weights) are missing for: " + meshPath); } // TODO: Both indices and weights attributes can be animated. It's not handled yet. // TODO: Having something that convert a UsdGeomPrimvar into a PrimvarSample could help simplify this code. int[] indices = IntrinsicTypeConverter.FromVtArray((VtIntArray)jointIndices.GetAttr().Get()); int indicesElementSize = jointIndices.GetElementSize(); pxr.TfToken indicesInterpolation = jointIndices.GetInterpolation(); if (indices.Length == 0 || indicesElementSize == 0 || indices.Length % indicesElementSize != 0 || !pxr.UsdGeomPrimvar.IsValidInterpolation(indicesInterpolation)) { throw new Exception("Joint indices information are invalid or empty for: " + meshPath); } float[] weights = IntrinsicTypeConverter.FromVtArray((VtFloatArray)jointWeights.GetAttr().Get()); int weightsElementSize = jointWeights.GetElementSize(); pxr.TfToken weightsInterpolation = jointWeights.GetInterpolation(); if (weights.Length == 0 || weightsElementSize == 0 || weights.Length % weightsElementSize != 0 || !pxr.UsdGeomPrimvar.IsValidInterpolation(weightsInterpolation)) { throw new Exception("Joints weights information are invalid or empty for: " + meshPath); } // Get and validate the local list of joints. VtTokenArray jointsAttr = new VtTokenArray(); skinningQuery.GetJointOrder(jointsAttr); // If jointsAttr wasn't define, GetJointOrder return an empty array and FromVtArray as well. string[] joints = IntrinsicTypeConverter.FromVtArray(jointsAttr); // WARNING: Do not mutate skeleton values. string[] skelJoints = skeleton.joints; if (joints == null || joints.Length == 0) { if (skelJoints == null || skelJoints.Length == 0) { throw new Exception("Joints array empty: " + meshPath); } else { joints = skelJoints; } } var mesh = smr.sharedMesh; // TODO: bind transform attribute can be animated. It's not handled yet. Matrix4x4 geomXf = UnityTypeConverter.FromMatrix(skinningQuery.GetGeomBindTransform()); // If the joints list is a different length than the bind transforms, then this is likely // a mesh using a subset of the total bones in the skeleton and the bindTransforms must be // reconstructed. var bindPoses = skeleton.bindTransforms; if (!JointsMatch(skeleton.joints, joints)) { var boneToPose = new Dictionary <string, Matrix4x4>(); bindPoses = new Matrix4x4[joints.Length]; for (int i = 0; i < skelJoints.Length; i++) { boneToPose[skelJoints[i]] = skeleton.bindTransforms[i]; } for (int i = 0; i < joints.Length; i++) { bindPoses[i] = boneToPose[joints[i]]; } } // When geomXf is identity, we can take a shortcut and just use the exact skeleton bindPoses. if (!ImporterBase.ApproximatelyEqual(geomXf, Matrix4x4.identity)) { // Note that the bind poses were transformed when the skeleton was imported, but the // geomBindTransform is per-mesh, so it must be transformed here so it is in the same space // as the bind pose. XformImporter.ImportXform(ref geomXf, options); // Make a copy only if we haven't already copied the bind poses earlier. if (bindPoses == skeleton.bindTransforms) { var newBindPoses = new Matrix4x4[skeleton.bindTransforms.Length]; Array.Copy(bindPoses, newBindPoses, bindPoses.Length); bindPoses = newBindPoses; } // Concatenate the geometry bind transform with the skeleton bind poses. for (int i = 0; i < bindPoses.Length; i++) { // The geometry transform should be applied to the points before any other transform, // hence the right hand multiply here. bindPoses[i] = bindPoses[i] * geomXf; } } mesh.bindposes = bindPoses; var bones = new Transform[joints.Length]; var sdfSkelPath = new SdfPath(skelPath); for (int i = 0; i < joints.Length; i++) { var jointPath = new SdfPath(joints[i]); if (joints[i] == "/") { jointPath = sdfSkelPath; } else if (jointPath.IsAbsolutePath()) { Debug.LogException(new Exception("Unexpected absolute joint path: " + jointPath)); jointPath = new SdfPath(joints[i].TrimStart('/')); jointPath = sdfSkelPath.AppendPath(jointPath); } else { jointPath = sdfSkelPath.AppendPath(jointPath); } var jointGo = primMap[jointPath]; if (!jointGo) { Debug.LogError("Error importing " + meshPath + " " + "Joint not found: " + joints[i]); continue; } bones[i] = jointGo.transform; } smr.bones = bones; bool isConstant = weightsInterpolation.GetString() == pxr.UsdGeomTokens.constant; // Unity 2019 supports many-bone rigs, older versions of Unity only support four bones. #if UNITY_2019 var bonesPerVertex = new NativeArray <byte>(mesh.vertexCount, Allocator.Persistent); var boneWeights1 = new NativeArray <BoneWeight1>(mesh.vertexCount * weightsElementSize, Allocator.Persistent); for (int i = 0; i < mesh.vertexCount; i++) { int unityIndex = i * weightsElementSize; int usdIndex = isConstant ? 0 : unityIndex; bonesPerVertex[i] = (byte)weightsElementSize; for (int wi = 0; wi < weightsElementSize; wi++) { var bw = boneWeights1[unityIndex + wi]; bw.boneIndex = indices[usdIndex + wi]; bw.weight = weights[usdIndex + wi]; boneWeights1[unityIndex + wi] = bw; } } // TODO: Investigate if bone weights should be normalized before this line. mesh.SetBoneWeights(bonesPerVertex, boneWeights1); bonesPerVertex.Dispose(); boneWeights1.Dispose(); #else var boneWeights = new BoneWeight[mesh.vertexCount]; for (int i = 0; i < boneWeights.Length; i++) { // When interpolation is constant, the base usdIndex should always be zero. // When non-constant, the offset is the index times the number of weights per vertex. int usdIndex = isConstant ? 0 : i * weightsElementSize; var boneWeight = boneWeights[i]; if (usdIndex >= indices.Length) { Debug.Log("UsdIndex out of bounds: " + usdIndex + " indices.Length: " + indices.Length + " boneWeights.Length: " + boneWeights.Length + " mesh: " + meshPath); } boneWeight.boneIndex0 = indices[usdIndex]; boneWeight.weight0 = weights[usdIndex]; if (indicesElementSize >= 2) { boneWeight.boneIndex1 = indices[usdIndex + 1]; boneWeight.weight1 = weights[usdIndex + 1]; } if (indicesElementSize >= 3) { boneWeight.boneIndex2 = indices[usdIndex + 2]; boneWeight.weight2 = weights[usdIndex + 2]; } if (indicesElementSize >= 4) { boneWeight.boneIndex3 = indices[usdIndex + 3]; boneWeight.weight3 = weights[usdIndex + 3]; } // If weights are less than 1, Unity will not automatically renormalize. // If weights are greater than 1, Unity will renormalize. // Only normalize when less than one to make it easier to diff bone weights which were // round-tripped and were being normalized by Unity. float sum = boneWeight.weight0 + boneWeight.weight1 + boneWeight.weight2 + boneWeight.weight3; if (sum < 1) { boneWeight.weight0 /= sum; boneWeight.weight1 /= sum; boneWeight.weight2 /= sum; boneWeight.weight3 /= sum; } boneWeights[i] = boneWeight; } mesh.boneWeights = boneWeights; #endif }
public static void BuildSkinnedMesh(string meshPath, string skelPath, SkeletonSample skeleton, SkelBindingSample meshBinding, GameObject go, PrimMap primMap, SceneImportOptions options) { string[] joints = meshBinding.joints; // WARNING: Do not mutate skeleton values. string[] skelJoints = skeleton.joints; bool isConstant = meshBinding.jointWeights.interpolation == PrimvarInterpolation.Constant; if (joints == null || joints.Length == 0) { if (skelJoints == null || skelJoints.Length == 0) { throw new Exception("Joints array empty: " + meshPath); } else { joints = skelJoints; } } // The mesh renderer must already exist, since hte mesh also must already exist. var smr = go.GetComponent <SkinnedMeshRenderer>(); if (!smr) { throw new Exception("Error importing " + meshPath + " SkinnnedMeshRenderer not present on GameObject"); } var mesh = smr.sharedMesh; var geomXf = meshBinding.geomBindTransform.value; // If the joints list is a different length than the bind transforms, then this is likely // a mesh using a subset of the total bones in the skeleton and the bindTransforms must be // reconstructed. var bindPoses = skeleton.bindTransforms; if (!JointsMatch(skeleton.joints, joints)) { var boneToPose = new Dictionary <string, Matrix4x4>(); bindPoses = new Matrix4x4[joints.Length]; for (int i = 0; i < skelJoints.Length; i++) { boneToPose[skelJoints[i]] = skeleton.bindTransforms[i]; } for (int i = 0; i < joints.Length; i++) { bindPoses[i] = boneToPose[joints[i]]; } } // When geomXf is identity, we can take a shortcut and just use the exact skeleton bindPoses. if (!ImporterBase.ApproximatelyEqual(geomXf, Matrix4x4.identity)) { // Note that the bind poses were transformed when the skeleton was imported, but the // geomBindTransform is per-mesh, so it must be transformed here so it is in the same space // as the bind pose. XformImporter.ImportXform(ref geomXf, options); // Make a copy only if we haven't already copied the bind poses earlier. if (bindPoses == skeleton.bindTransforms) { var newBindPoses = new Matrix4x4[skeleton.bindTransforms.Length]; Array.Copy(bindPoses, newBindPoses, bindPoses.Length); bindPoses = newBindPoses; } // Concatenate the geometry bind transform with the skeleton bind poses. for (int i = 0; i < bindPoses.Length; i++) { // The geometry transform should be applied to the points before any other transform, // hence the right hand multiply here. bindPoses[i] = bindPoses[i] * geomXf; } } mesh.bindposes = bindPoses; var bones = new Transform[joints.Length]; var sdfSkelPath = new SdfPath(skelPath); for (int i = 0; i < joints.Length; i++) { var jointPath = new SdfPath(joints[i]); if (joints[i] == "/") { jointPath = sdfSkelPath; } else if (jointPath.IsAbsolutePath()) { Debug.LogException(new Exception("Unexpected absolute joint path: " + jointPath)); jointPath = new SdfPath(joints[i].TrimStart('/')); jointPath = sdfSkelPath.AppendPath(jointPath); } else { jointPath = sdfSkelPath.AppendPath(jointPath); } var jointGo = primMap[jointPath]; if (!jointGo) { Debug.LogError("Error importing " + meshPath + " " + "Joint not found: " + joints[i]); continue; } bones[i] = jointGo.transform; } smr.bones = bones; int[] indices = meshBinding.jointIndices.value; float[] weights = meshBinding.jointWeights.value; // Unity 2019 supports many-bone rigs, older versions of Unity only support four bones. #if UNITY_2019 var bonesPerVertex = new NativeArray <byte>(mesh.vertexCount, Allocator.Persistent); var boneWeights1 = new NativeArray <BoneWeight1>(mesh.vertexCount * meshBinding.jointWeights.elementSize, Allocator.Persistent); for (int i = 0; i < mesh.vertexCount; i++) { int unityIndex = i * meshBinding.jointWeights.elementSize; int usdIndex = isConstant ? 0 : unityIndex; bonesPerVertex[i] = (byte)meshBinding.jointWeights.elementSize; for (int wi = 0; wi < meshBinding.jointWeights.elementSize; wi++) { var bw = boneWeights1[unityIndex + wi]; bw.boneIndex = indices[usdIndex + wi]; bw.weight = weights[usdIndex + wi]; boneWeights1[unityIndex + wi] = bw; } } mesh.SetBoneWeights(bonesPerVertex, boneWeights1); bonesPerVertex.Dispose(); boneWeights1.Dispose(); #else var boneWeights = new BoneWeight[mesh.vertexCount]; for (int i = 0; i < boneWeights.Length; i++) { // When interpolation is constant, the base usdIndex should always be zero. // When non-constant, the offset is the index times the number of weights per vertex. int usdIndex = isConstant ? 0 : i * meshBinding.jointWeights.elementSize; var boneWeight = boneWeights[i]; if (usdIndex >= indices.Length) { Debug.Log("UsdIndex out of bounds: " + usdIndex + " indices.Length: " + indices.Length + " boneWeights.Length: " + boneWeights.Length + " mesh: " + meshPath); } boneWeight.boneIndex0 = indices[usdIndex]; boneWeight.weight0 = weights[usdIndex]; if (meshBinding.jointIndices.elementSize >= 2) { boneWeight.boneIndex1 = indices[usdIndex + 1]; boneWeight.weight1 = weights[usdIndex + 1]; } if (meshBinding.jointIndices.elementSize >= 3) { boneWeight.boneIndex2 = indices[usdIndex + 2]; boneWeight.weight2 = weights[usdIndex + 2]; } if (meshBinding.jointIndices.elementSize >= 4) { boneWeight.boneIndex3 = indices[usdIndex + 3]; boneWeight.weight3 = weights[usdIndex + 3]; } // If weights are less than 1, Unity will not automatically renormalize. // If weights are greater than 1, Unity will renormalize. // Only normalize when less than one to make it easier to diff bone weights which were // round-tripped and were being normalized by Unity. float sum = boneWeight.weight0 + boneWeight.weight1 + boneWeight.weight2 + boneWeight.weight3; if (sum < 1) { boneWeight.weight0 /= sum; boneWeight.weight1 /= sum; boneWeight.weight2 /= sum; boneWeight.weight3 /= sum; } boneWeights[i] = boneWeight; } mesh.boneWeights = boneWeights; #endif }
/// <summary> /// Clear internal data. /// Call to <see cref="GetScene">GetScene()</see> to update them with the latest USD data. /// </summary> private void ClearLastData() { m_lastScene = null; m_lastPrimMap = null; m_lastAccessMask = null; }
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++; } }
public void BeginReading(Scene scene, PrimMap primMap) { m_readMeshesJob = new ReadAllJob <MeshSample>(scene, primMap.Meshes); m_readMeshesJob.Schedule(primMap.Meshes.Length, 2); }
/// <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(); }
public void BeginReading(Scene scene, PrimMap primMap) { m_readMeshesJob = new ReadAllJob <MeshSample>(scene, primMap.Meshes); m_readMeshesJob.Run(); }
public System.Collections.IEnumerator Import(Scene scene, PrimMap primMap, SceneImportOptions importOptions) { if (importOptions.importSkinning) { Profiler.BeginSample("USD: Populate SkelCache"); foreach (var path in primMap.SkelRoots) { var prim = scene.GetPrimAtPath(path); if (!prim) { continue; } var skelRoot = new UsdSkelRoot(prim); if (!skelRoot) { continue; } } Profiler.EndSample(); } System.Reflection.MemberInfo faceVertexCounts = null; System.Reflection.MemberInfo faceVertexIndices = null; System.Reflection.MemberInfo orientation = null; System.Reflection.MemberInfo purpose = null; System.Reflection.MemberInfo visibility = null; if (scene.AccessMask != null && scene.IsPopulatingAccessMask) { var meshType = typeof(MeshSample); faceVertexCounts = meshType.GetMember("faceVertexCounts")[0]; faceVertexIndices = meshType.GetMember("faceVertexIndices")[0]; orientation = meshType.GetMember("orientation")[0]; purpose = meshType.GetMember("purpose")[0]; visibility = meshType.GetMember("visibility")[0]; } foreach (var pathAndSample in m_readMeshesJob) { if (scene.AccessMask != null && scene.IsPopulatingAccessMask) { HashSet <System.Reflection.MemberInfo> members; if (scene.AccessMask.Included.TryGetValue(pathAndSample.path, out members)) { if (members.Contains(faceVertexCounts) || members.Contains(orientation) || members.Contains(faceVertexIndices)) { members.Add(faceVertexCounts); members.Add(faceVertexIndices); members.Add(orientation); } if (pathAndSample.sample.purpose != Purpose.Default && !members.Contains(purpose)) { members.Add(purpose); } if (pathAndSample.sample.visibility != Visibility.Inherited && !members.Contains(visibility)) { members.Add(visibility); } } } Profiler.BeginSample("USD: Build Meshes"); try { GameObject go = primMap[pathAndSample.path]; NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions); if (importOptions.importTransforms) { Profiler.BeginSample("Build Mesh Xform"); XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); Profiler.EndSample(); } Profiler.BeginSample("Read Mesh Subsets"); MeshImporter.GeometrySubsets subsets = null; if (primMap == null || !primMap.MeshSubsets.TryGetValue(pathAndSample.path, out subsets)) { subsets = MeshImporter.ReadGeomSubsets(scene, pathAndSample.path); } Profiler.EndSample(); UsdSkelSkinningQuery skinningQuery; if (importOptions.importHierarchy) { if (importOptions.importSkinning && primMap.SkelCache != null) { // This is pre-cached as part of calling skelCache.Populate and IsValid indicates if we // have the data required to setup a skinned mesh. Profiler.BeginSample("Get Skinning Query"); skinningQuery = new UsdSkelSkinningQuery(); primMap.SkinningQueries[pathAndSample.path] = primMap.SkelCache.GetSkinningQuery(scene.GetPrimAtPath(pathAndSample.path)); Profiler.EndSample(); } if (importOptions.importMeshes) { primMap.MeshSubsets[pathAndSample.path] = MeshImporter.ReadGeomSubsets(scene, pathAndSample.path); } } if (importOptions.importSkinning) { primMap.SkinningQueries.TryGetValue(pathAndSample.path, out skinningQuery); /* * Profiler.BeginSample("Get Skinning Query"); * skinningQuery = primMap.SkelCache.GetSkinningQuery(scene.GetPrimAtPath(pathAndSample.path)); * Profiler.EndSample(); */ } if (importOptions.importSkinning && primMap.SkelCache != null && primMap.SkinningQueries.TryGetValue(pathAndSample.path, out skinningQuery) && skinningQuery.IsValid()) { Profiler.BeginSample("USD: Build Skinned Mesh"); m_skinnedMeshImporter(pathAndSample.path, pathAndSample.sample, subsets, go, importOptions); Profiler.EndSample(); } else { Profiler.BeginSample("USD: Build Mesh"); m_meshImporter(pathAndSample.path, pathAndSample.sample, subsets, go, importOptions); Profiler.EndSample(); } } catch (Exception ex) { Debug.LogException( new SceneImporter.ImportException( "Error processing mesh <" + pathAndSample.path + ">", ex)); } Profiler.EndSample(); yield return(null); } // foreach mesh }