public static void TraversalTest() { var scene = USD.NET.Scene.Open(@"D:\usd\Kitchen_set.usd"); foreach (pxr.UsdPrim prim in scene.Stage.Traverse()) { var mesh = new pxr.UsdGeomMesh(prim); if (mesh) { Console.WriteLine("Non-instanced mesh: " + prim.GetPath()); } // // See if this is instanced. // if (prim.IsInstance()) { Console.WriteLine("Instanced prim: " + prim.GetPath() + "\n\tMaster: " + prim.GetMaster().GetPath()); // // Now traverse the master. // var master = prim.GetMaster(); foreach (pxr.UsdPrim masterPrim in master.GetAllDescendants()) { var masterMesh = new pxr.UsdGeomMesh(masterPrim); if (masterMesh) { Console.WriteLine("\tMaster mesh: " + masterPrim.GetPath()); } } } } }
public static void CreatePointInstancerManualTest() { var scene = USD.NET.Scene.Create(); var stageWeakRef = new pxr.UsdStageWeakPtr(scene.Stage); var pi = pxr.UsdGeomPointInstancer.Define(stageWeakRef, new pxr.SdfPath("/Instancer")); var cube = pxr.UsdGeomCube.Define(stageWeakRef, new pxr.SdfPath("/Instancer/Cube")); // The cube is the only prototype. pi.CreatePrototypesRel().AddTarget(cube.GetPath()); // Create three instances of the cube (proto index=0). var intArray = new pxr.VtIntArray(3, 0); pi.CreateProtoIndicesAttr().Set(intArray); var mesh = new pxr.UsdGeomMesh(); var meshSamples = new USD.NET.Unity.MeshSample[0]; // Create three positions. var vec3fArray = new pxr.VtVec3fArray(3); vec3fArray[0] = new pxr.GfVec3f(-2.5f, 0, 0); vec3fArray[1] = new pxr.GfVec3f(0, 0, 0); vec3fArray[2] = new pxr.GfVec3f(2.5f, 0, 0); pi.CreatePositionsAttr().Set(vec3fArray); scene.Stage.Export("D:\\instancer.usda"); // // Compute the root transform for each instance. // var xforms = new pxr.VtMatrix4dArray(3); pi.ComputeInstanceTransformsAtTime(xforms, time: 1.0, baseTime: 0.0); for (int i = 0; i < xforms.size(); i++) { Console.WriteLine(xforms[i]); } }
static void ExportMesh(ObjectContext objContext, ExportContext exportContext, Mesh mesh, Material sharedMaterial, Material[] sharedMaterials, bool exportMeshPose = true) { string path = objContext.path; if (mesh == null) { Debug.LogWarning("Null mesh for: " + path, objContext.gameObject); return; } #if UNITY_EDITOR if (!CanReadMesh(mesh)) { #else if (!mesh.isReadable) { #endif Debug.LogError( "Mesh is not readable: " + objContext.path + ". To fix this, enable read/write in the inspector for the source asset that you are attempting to export.", objContext.gameObject); return; } var scene = exportContext.scene; bool unvarying = scene.Time == null; bool slowAndSafeConversion = exportContext.basisTransform == BasisTransformation.SlowAndSafe; var sample = (MeshSample)objContext.sample; var go = objContext.gameObject; if (mesh.bounds.center == Vector3.zero && mesh.bounds.extents == Vector3.zero) { mesh.RecalculateBounds(); } sample.extent = mesh.bounds; if (slowAndSafeConversion) { // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change of // basis is required. There are shortcuts, but this is fully general. sample.ConvertTransform(); sample.extent.center = UnityTypeConverter.ChangeBasis(sample.extent.center); } // Only export the mesh topology on the first frame. if (unvarying) { // TODO: Technically a mesh could be the root transform, which is not handled correctly here. // It should have the same logic for root prims as in ExportXform. sample.transform = XformExporter.GetLocalTransformMatrix( go.transform, scene.UpAxis == Scene.UpAxes.Z, new pxr.SdfPath(path).IsRootPrimPath(), exportContext.basisTransform); sample.normals = mesh.normals; sample.points = mesh.vertices; sample.tangents = mesh.tangents; sample.colors = mesh.colors; if (sample.colors != null && sample.colors.Length == 0) { sample.colors = null; } if ((sample.colors == null || sample.colors.Length == 0) && (sharedMaterial != null && sharedMaterial.HasProperty("_Color"))) { sample.colors = new Color[1]; sample.colors[0] = sharedMaterial.color.linear; } // Gah. There is no way to inspect a meshes UVs. sample.st = mesh.uv; // Set face vertex counts and indices. var tris = mesh.triangles; if (slowAndSafeConversion) { // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change // of basis is required. There are shortcuts, but this is fully general. for (int i = 0; i < sample.points.Length; i++) { sample.points[i] = UnityTypeConverter.ChangeBasis(sample.points[i]); if (sample.normals != null && sample.normals.Length == sample.points.Length) { sample.normals[i] = UnityTypeConverter.ChangeBasis(sample.normals[i]); } if (sample.tangents != null && sample.tangents.Length == sample.points.Length) { var w = sample.tangents[i].w; var t = UnityTypeConverter.ChangeBasis(sample.tangents[i]); sample.tangents[i] = new Vector4(t.x, t.y, t.z, w); } } for (int i = 0; i < tris.Length; i += 3) { var t = tris[i]; tris[i] = tris[i + 1]; tris[i + 1] = t; } } sample.SetTriangles(tris); UnityEngine.Profiling.Profiler.BeginSample("USD: Mesh Write"); scene.Write(path, sample); UnityEngine.Profiling.Profiler.EndSample(); // TODO: this is a bit of a half-measure, we need real support for primvar interpolation. // Set interpolation based on color count. if (sample.colors != null && sample.colors.Length == 1) { pxr.UsdPrim usdPrim = scene.GetPrimAtPath(path); var colorPrimvar = new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayColor)); colorPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant); var opacityPrimvar = new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayOpacity)); opacityPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant); } string usdMaterialPath; if (exportContext.exportMaterials && sharedMaterial != null) { if (!exportContext.matMap.TryGetValue(sharedMaterial, out usdMaterialPath)) { Debug.LogError("Invalid material bound for: " + path); } else { MaterialSample.Bind(scene, path, usdMaterialPath); } } // In USD subMeshes are represented as UsdGeomSubsets. // When there are multiple subMeshes, convert them into UsdGeomSubsets. if (mesh.subMeshCount > 1) { // Build a table of face indices, used to convert the subMesh triangles to face indices. var faceTable = new Dictionary <Vector3, int>(); for (int i = 0; i < tris.Length; i += 3) { if (!slowAndSafeConversion) { faceTable.Add(new Vector3(tris[i], tris[i + 1], tris[i + 2]), i / 3); } else { // Under slow and safe export, index 0 and 1 are swapped. // This swap will not be present in the subMesh indices, so must be undone here. faceTable.Add(new Vector3(tris[i + 1], tris[i], tris[i + 2]), i / 3); } } var usdPrim = scene.GetPrimAtPath(path); var usdGeomMesh = new pxr.UsdGeomMesh(usdPrim); // Process each subMesh and create a UsdGeomSubset of faces this subMesh targets. for (int si = 0; si < mesh.subMeshCount; si++) { int[] indices = mesh.GetTriangles(si); int[] faceIndices = new int[indices.Length / 3]; for (int i = 0; i < indices.Length; i += 3) { faceIndices[i / 3] = faceTable[new Vector3(indices[i], indices[i + 1], indices[i + 2])]; } var vtIndices = UnityTypeConverter.ToVtArray(faceIndices); var subset = pxr.UsdGeomSubset.CreateUniqueGeomSubset( usdGeomMesh, // The object of which this subset belongs. m_subMeshesToken, // An arbitrary name for the subset. pxr.UsdGeomTokens.face, // Indicator that these represent face indices vtIndices, // The actual face indices. m_materialBindToken // familyName = "materialBind" ); if (exportContext.exportMaterials) { if (si >= sharedMaterials.Length || !sharedMaterials[si] || !exportContext.matMap.TryGetValue(sharedMaterials[si], out usdMaterialPath)) { Debug.LogWarning("Invalid material bound for: " + path + "\n" + (si >= sharedMaterials.Length ? "More submeshes than materials assigned." : (!sharedMaterials[si] ? "Submesh " + si + " has null material" : "ExportMap can't map material"))); } else { MaterialSample.Bind(scene, subset.GetPath(), usdMaterialPath); } } } } } else { // Only write the transform when animating. var meshSample = new MeshSampleBase(); meshSample.extent = sample.extent; meshSample.transform = XformExporter.GetLocalTransformMatrix( go.transform, scene.UpAxis == Scene.UpAxes.Z, new pxr.SdfPath(path).IsRootPrimPath(), exportContext.basisTransform); if (exportMeshPose) { meshSample.points = mesh.vertices; // Set face vertex counts and indices. var tris = mesh.triangles; if (slowAndSafeConversion) { // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change // of basis is required. There are shortcuts, but this is fully general. for (int i = 0; i < meshSample.points.Length; i++) { meshSample.points[i] = UnityTypeConverter.ChangeBasis(meshSample.points[i]); } for (int i = 0; i < tris.Length; i += 3) { var t = tris[i]; tris[i] = tris[i + 1]; tris[i + 1] = t; } } sample.SetTriangles(tris); } UnityEngine.Profiling.Profiler.BeginSample("USD: Mesh Write"); scene.Write(path, meshSample); UnityEngine.Profiling.Profiler.EndSample(); } } }