public static UsdGeomMesh Define(UsdStageWeakPtr stage, SdfPath path) { UsdGeomMesh ret = new UsdGeomMesh(UsdCsPINVOKE.UsdGeomMesh_Define(UsdStageWeakPtr.getCPtr(stage), SdfPath.getCPtr(path)), true); if (UsdCsPINVOKE.SWIGPendingException.Pending) { throw UsdCsPINVOKE.SWIGPendingException.Retrieve(); } return(ret); }
public static void TestTriangulation() { VtIntArray indices = new VtIntArray(); VtIntArray faceCounts = new VtIntArray(); faceCounts.push_back(5); indices.push_back(0); indices.push_back(1); indices.push_back(2); indices.push_back(3); indices.push_back(4); faceCounts.push_back(4); indices.push_back(5); indices.push_back(6); indices.push_back(7); indices.push_back(8); faceCounts.push_back(3); indices.push_back(9); indices.push_back(10); indices.push_back(11); // Degenerate face. faceCounts.push_back(2); indices.push_back(12); indices.push_back(13); UsdGeomMesh.Triangulate(indices, faceCounts); AssertEqual((int)faceCounts.size(), 6); for (int i = 0; i < faceCounts.size(); i++) { AssertEqual((int)faceCounts[i], 3); } AssertEqual((int)indices.size(), 18); AssertEqual(indices[0], 0); AssertEqual(indices[1], 1); AssertEqual(indices[2], 2); AssertEqual(indices[3], 0); AssertEqual(indices[4], 2); AssertEqual(indices[5], 3); AssertEqual(indices[6], 0); AssertEqual(indices[7], 3); AssertEqual(indices[8], 4); AssertEqual(indices[9], 5); AssertEqual(indices[10], 6); AssertEqual(indices[11], 7); AssertEqual(indices[12], 5); AssertEqual(indices[13], 7); AssertEqual(indices[14], 8); AssertEqual(indices[15], 9); AssertEqual(indices[16], 10); AssertEqual(indices[17], 11); }
private static void BuildMesh_(string path, MeshSample usdMesh, Mesh unityMesh, GeometrySubsets geomSubsets, GameObject go, Renderer renderer, SceneImportOptions options) { // TODO: Because this method operates on a GameObject, it must be single threaded. For this // reason, it should be extremely light weight. All computation should be completed prior to // this step, allowing heavy computations to happen in parallel. This is not currently the // case, triangulation and change of basis are non-trivial operations. Computing the mesh // bounds, normals and tangents should similarly be moved out of this function and should not // rely on the UnityEngine.Mesh API. Material mat = renderer.sharedMaterial; bool changeHandedness = options.changeHandedness == BasisTransformation.SlowAndSafe; // // Points. // if (options.meshOptions.points == ImportMode.Import && usdMesh.points != null) { if (changeHandedness) { for (int i = 0; i < usdMesh.points.Length; i++) { usdMesh.points[i] = UnityTypeConverter.ChangeBasis(usdMesh.points[i]); } } if (usdMesh.faceVertexIndices != null) { // Annoyingly, there is a circular dependency between vertices and triangles, which makes // it impossible to have a fixed update order in this function. As a result, we must clear // the triangles before setting the points, to break that dependency. unityMesh.SetTriangles(new int[0] { }, 0); } unityMesh.vertices = usdMesh.points; } // // Purpose. // // Deactivate non-geometry prims (e.g. guides, render, etc). if (usdMesh.purpose != Purpose.Default) { go.SetActive(false); } // // Mesh Topology. // // TODO: indices should not be accessed if topology is not requested, however it may be // needed for facevarying primvars; that special case should throw a warning, rather than // reading the value. int[] originalIndices = new int[usdMesh.faceVertexIndices == null ? 0 : usdMesh.faceVertexIndices.Length]; // Optimization: only do this when there are face varying primvars. if (usdMesh.faceVertexIndices != null) { Array.Copy(usdMesh.faceVertexIndices, originalIndices, originalIndices.Length); } if (options.meshOptions.topology == ImportMode.Import && usdMesh.faceVertexIndices != null) { Profiler.BeginSample("Triangulate Mesh"); if (options.meshOptions.triangulateMesh) { // Triangulate n-gons. // For best performance, triangulate off-line and skip conversion. if (usdMesh.faceVertexIndices == null) { Debug.LogWarning("Mesh had no face indices: " + UnityTypeConverter.GetPath(go.transform)); return; } if (usdMesh.faceVertexCounts == null) { Debug.LogWarning("Mesh had no face counts: " + UnityTypeConverter.GetPath(go.transform)); return; } var indices = UnityTypeConverter.ToVtArray(usdMesh.faceVertexIndices); var counts = UnityTypeConverter.ToVtArray(usdMesh.faceVertexCounts); UsdGeomMesh.Triangulate(indices, counts); UnityTypeConverter.FromVtArray(indices, ref usdMesh.faceVertexIndices); } Profiler.EndSample(); Profiler.BeginSample("Convert LeftHanded"); bool isLeftHanded = usdMesh.orientation == Orientation.LeftHanded; if (changeHandedness && !isLeftHanded || !changeHandedness && isLeftHanded) { // USD is right-handed, so the mesh needs to be flipped. // Unity is left-handed, but that doesn't matter here. for (int i = 0; i < usdMesh.faceVertexIndices.Length; i += 3) { int tmp = usdMesh.faceVertexIndices[i]; usdMesh.faceVertexIndices[i] = usdMesh.faceVertexIndices[i + 1]; usdMesh.faceVertexIndices[i + 1] = tmp; } } Profiler.EndSample(); if (usdMesh.faceVertexIndices.Length > 65535) { unityMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; } Profiler.BeginSample("Breakdown triangles for Mesh Subsets"); if (geomSubsets.Subsets.Count == 0) { unityMesh.triangles = usdMesh.faceVertexIndices; } else { unityMesh.subMeshCount = geomSubsets.Subsets.Count; int subsetIndex = 0; foreach (var kvp in geomSubsets.Subsets) { int[] faceIndices = kvp.Value; int[] triangleIndices = new int[faceIndices.Length * 3]; for (int i = 0; i < faceIndices.Length; i++) { triangleIndices[i * 3 + 0] = usdMesh.faceVertexIndices[faceIndices[i] * 3 + 0]; triangleIndices[i * 3 + 1] = usdMesh.faceVertexIndices[faceIndices[i] * 3 + 1]; triangleIndices[i * 3 + 2] = usdMesh.faceVertexIndices[faceIndices[i] * 3 + 2]; } unityMesh.SetTriangles(triangleIndices, subsetIndex); subsetIndex++; } } Profiler.EndSample(); } // // Extent / Bounds. // bool hasBounds = usdMesh.extent.size.x > 0 || usdMesh.extent.size.y > 0 || usdMesh.extent.size.z > 0; if (ShouldImport(options.meshOptions.boundingBox) && hasBounds) { Profiler.BeginSample("Import Bounds"); if (changeHandedness) { usdMesh.extent.center = UnityTypeConverter.ChangeBasis(usdMesh.extent.center); usdMesh.extent.extents = UnityTypeConverter.ChangeBasis(usdMesh.extent.extents); } unityMesh.bounds = usdMesh.extent; Profiler.EndSample(); } else if (ShouldCompute(options.meshOptions.boundingBox)) { Profiler.BeginSample("Calculate Bounds"); unityMesh.RecalculateBounds(); Profiler.EndSample(); } // // Normals. // if (usdMesh.normals != null && ShouldImport(options.meshOptions.normals)) { Profiler.BeginSample("Import Normals"); if (changeHandedness) { for (int i = 0; i < usdMesh.points.Length; i++) { usdMesh.normals[i] = UnityTypeConverter.ChangeBasis(usdMesh.normals[i]); } } // If more normals than verts, assume face-varying. if (usdMesh.normals.Length > usdMesh.points.Length) { usdMesh.normals = UnrollFaceVarying(usdMesh.points.Length, usdMesh.normals, usdMesh.faceVertexCounts, originalIndices); } unityMesh.normals = usdMesh.normals; Profiler.EndSample(); } else if (ShouldCompute(options.meshOptions.normals)) { Profiler.BeginSample("Calculate Normals"); unityMesh.RecalculateNormals(); Profiler.EndSample(); } // // Tangents. // if (usdMesh.tangents != null && ShouldImport(options.meshOptions.tangents)) { Profiler.BeginSample("Import Tangents"); if (changeHandedness) { for (int i = 0; i < usdMesh.points.Length; i++) { var w = usdMesh.tangents[i].w; var t = UnityTypeConverter.ChangeBasis(usdMesh.tangents[i]); usdMesh.tangents[i] = new Vector4(t.x, t.y, t.z, w); } } unityMesh.tangents = usdMesh.tangents; Profiler.EndSample(); } else if (ShouldCompute(options.meshOptions.tangents)) { Profiler.BeginSample("Calculate Tangents"); unityMesh.RecalculateTangents(); Profiler.EndSample(); } // // Display Color. // if (ShouldImport(options.meshOptions.color) && usdMesh.colors != null && usdMesh.colors.Length > 0) { Profiler.BeginSample("Import Display Color"); // NOTE: The following color conversion assumes PlayerSettings.ColorSpace == Linear. // For best performance, convert color space to linear off-line and skip conversion. if (usdMesh.colors.Length == 1) { // Constant color can just be set on the material. if (options.useDisplayColorAsFallbackMaterial && options.materialImportMode != MaterialImportMode.None) { mat = options.materialMap.InstantiateSolidColor(usdMesh.colors[0].gamma); } } else if (usdMesh.colors.Length == usdMesh.points.Length) { // Vertex colors map on to verts. // TODO: move the conversion to C++ and use the color management API. for (int i = 0; i < usdMesh.colors.Length; i++) { usdMesh.colors[i] = usdMesh.colors[i]; } unityMesh.colors = usdMesh.colors; } else if (usdMesh.colors.Length == usdMesh.faceVertexCounts.Length) { // Uniform colors, one per face. // Unroll face colors into vertex colors. This is not strictly correct, but it's much faster // than the fully correct solution. var colors = new Color[unityMesh.vertexCount]; int idx = 0; try { for (int faceIndex = 0; faceIndex < usdMesh.colors.Length; faceIndex++) { var faceColor = usdMesh.colors[faceIndex]; for (int f = 0; f < usdMesh.faceVertexCounts[faceIndex]; f++) { int vertexInFaceIdx = originalIndices[idx++]; colors[vertexInFaceIdx] = faceColor; } } unityMesh.colors = colors; } catch (Exception ex) { Debug.LogException(new Exception("Failed loading uniform/per-face colors at " + path, ex)); } } else if (usdMesh.colors.Length > usdMesh.points.Length) { try { usdMesh.colors = UnrollFaceVarying(unityMesh.vertexCount, usdMesh.colors, usdMesh.faceVertexCounts, originalIndices); for (int i = 0; i < usdMesh.colors.Length; i++) { usdMesh.colors[i] = usdMesh.colors[i]; } unityMesh.colors = usdMesh.colors; } catch (Exception ex) { Debug.LogException( new Exception("Error unrolling Face-Varying colors at <" + path + ">", ex)); } } else { Debug.LogWarning("Uniform (color per face) display color not supported"); } Profiler.EndSample(); } // should import color // // UVs / Texture Coordinates. // // TODO: these should also be driven by the UV privmars required by the bound shader. Profiler.BeginSample("Import UV Sets"); ImportUv(path, unityMesh, 0, usdMesh.st, usdMesh.indices, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord0, go); ImportUv(path, unityMesh, 0, usdMesh.uv, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord0, go); ImportUv(path, unityMesh, 1, usdMesh.uv2, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord1, go); ImportUv(path, unityMesh, 2, usdMesh.uv3, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord2, go); ImportUv(path, unityMesh, 3, usdMesh.uv4, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord3, go); Profiler.EndSample(); Profiler.BeginSample("Request Material Bindings"); // // Materials. // if (options.materialImportMode != MaterialImportMode.None) { if (mat == null) { mat = options.materialMap.InstantiateSolidColor(Color.white); } if (unityMesh.subMeshCount == 1) { renderer.sharedMaterial = mat; if (options.ShouldBindMaterials) { options.materialMap.RequestBinding(path, (scene, boundMat, primvars) => BindMat(scene, unityMesh, boundMat, renderer, path, primvars, usdMesh.faceVertexCounts, originalIndices)); } } else { var mats = new Material[unityMesh.subMeshCount]; for (int i = 0; i < mats.Length; i++) { mats[i] = mat; } renderer.sharedMaterials = mats; if (options.ShouldBindMaterials) { Debug.Assert(geomSubsets.Subsets.Count == unityMesh.subMeshCount); var subIndex = 0; foreach (var kvp in geomSubsets.Subsets) { int idx = subIndex++; options.materialMap.RequestBinding(kvp.Key, (scene, boundMat, primvars) => BindMat(scene, unityMesh, boundMat, renderer, idx, path, primvars, usdMesh.faceVertexCounts, originalIndices)); } } } } Profiler.EndSample(); // // Lightmap UV Unwrapping. // #if UNITY_EDITOR if (options.meshOptions.generateLightmapUVs) { #if !UNITY_2018_3_OR_NEWER if (unityMesh.indexFormat == UnityEngine.Rendering.IndexFormat.UInt32) { Debug.LogWarning("Skipping prim " + path + " due to large IndexFormat (UInt32) bug in older vesrsions of Unity"); return; } #endif Profiler.BeginSample("Unwrap Lightmap UVs"); var unwrapSettings = new UnityEditor.UnwrapParam(); unwrapSettings.angleError = options.meshOptions.unwrapAngleError; unwrapSettings.areaError = options.meshOptions.unwrapAngleError; unwrapSettings.hardAngle = options.meshOptions.unwrapHardAngle; // Convert pixels to unitless UV space, which is what unwrapSettings uses internally. unwrapSettings.packMargin = options.meshOptions.unwrapPackMargin / 1024.0f; UnityEditor.Unwrapping.GenerateSecondaryUVSet(unityMesh, unwrapSettings); Profiler.EndSample(); } #else if (options.meshOptions.generateLightmapUVs) { Debug.LogWarning("Lightmap UVs were requested to be generated, but cannot be generated outside of the editor"); } #endif }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(UsdGeomMesh obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }
public static GameObject Import( USD.NET.Scene scene, GameObject rootObj, Dictionary <SdfPath, GameObject> objectMap, UpdateMask mask, out List <string> warnings, List <string> pathsToUpdate = null) { // TODO: generalize this to avoid having to dig down into USD for sparse reads. TfToken brushToken = new pxr.TfToken("brush"); TfToken faceVertexIndicesToken = new pxr.TfToken("faceVertexIndices"); warnings = new List <string>(); // Would be nice to find a way to kick this off automatically. // Redundant calls are ignored. if (!InitUsd.Initialize()) { return(null); } // PLAN: Process any UsdStage either constructing or updating GameObjects as needed. // This should include analysis of the time samples to see what attributes are // actually varying so they are updated minimally. UsdPrimVector prims = null; if (pathsToUpdate == null) { prims = scene.Stage.GetAllPrims(); } else { prims = new UsdPrimVector(); foreach (var path in pathsToUpdate) { prims.Add(scene.Stage.GetPrimAtPath(new pxr.SdfPath(path))); } } for (int p = 0; p < prims.Count; p++) { // TODO: prims[p] generates garbage. UsdPrim usdPrim = prims[p]; UsdGeomMesh usdMesh = new UsdGeomMesh(usdPrim); if (!usdMesh) { continue; } ExportUsd.BrushSample sample = new ExportUsd.BrushSample(); if (mask == UpdateMask.All) { scene.Read(usdPrim.GetPath(), sample); } else { // TODO: Generalize this as a reusable mechanism for sparse reads. if (mask == UpdateMask.Topology) { sample.brush = new Guid((string)usdPrim.GetCustomDataByKey(brushToken)); var fv = usdPrim.GetAttribute(faceVertexIndicesToken).Get(scene.Time); sample.faceVertexIndices = USD.NET.IntrinsicTypeConverter.FromVtArray((VtIntArray)fv); } else { throw new NotImplementedException(); } } GameObject strokeObj; Mesh unityMesh; // // Construct the GameObject if needed. // if (!objectMap.TryGetValue(usdPrim.GetPath(), out strokeObj)) { // On first import, we need to pull in all the data, regardless of what was requested. mask = UpdateMask.All; BrushDescriptor brush = BrushCatalog.m_Instance.GetBrush(sample.brush); if (brush == null) { Debug.LogWarningFormat("Invalid brush GUID at path: <{0}> guid: {1}", usdPrim.GetPath(), sample.brush); continue; } strokeObj = UnityEngine.Object.Instantiate(brush.m_BrushPrefab); // Register the Prim/Object mapping. objectMap.Add(usdPrim.GetPath(), strokeObj); // Init the game object. strokeObj.transform.parent = rootObj.transform; strokeObj.GetComponent <MeshRenderer>().material = brush.Material; strokeObj.GetComponent <MeshFilter>().sharedMesh = new Mesh(); strokeObj.AddComponent <BoxCollider>(); unityMesh = strokeObj.GetComponent <MeshFilter>().sharedMesh; } else { unityMesh = strokeObj.GetComponent <MeshFilter>().sharedMesh; } // // Points // Note that points must come first, before all other mesh data. // if ((mask & UpdateMask.Points) == UpdateMask.Points) { unityMesh.vertices = sample.points; } // // Bounds // if ((mask & UpdateMask.Bounds) == UpdateMask.Bounds) { var bc = strokeObj.GetComponent <BoxCollider>(); bc.center = sample.extent.center; bc.size = sample.extent.size; unityMesh.bounds = bc.bounds; } // // Topology // if ((mask & UpdateMask.Topology) == UpdateMask.Topology) { unityMesh.triangles = sample.faceVertexIndices; } // // Normals // if ((mask & UpdateMask.Normals) == UpdateMask.Normals) { unityMesh.normals = sample.normals; } // // Color & Opacity // if ((mask & UpdateMask.Colors) == UpdateMask.Colors && sample.colors != null) { unityMesh.colors = sample.colors; } // // Tangents // if ((mask & UpdateMask.Tangents) == UpdateMask.Tangents && sample.tangents != null) { unityMesh.tangents = sample.tangents; } // // UVs // if ((mask & UpdateMask.UVs) == UpdateMask.UVs) { SetUv(unityMesh, 0, sample.uv); SetUv(unityMesh, 1, sample.uv2); SetUv(unityMesh, 2, sample.uv3); SetUv(unityMesh, 3, sample.uv4); } } // For each prim return(rootObj); }