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();
            }
        }
    }
Exemplo n.º 2
0
        public static void ExportSkelAnimation(ObjectContext objContext, ExportContext exportContext)
        {
            var scene     = exportContext.scene;
            var sample    = (SkelAnimationSample)objContext.sample;
            var go        = objContext.gameObject;
            var boneNames = exportContext.skelSortedMap[go.transform];
            var skelRoot  = go.transform;

            sample.joints = new string[boneNames.Count];

            var worldXf    = new Matrix4x4[boneNames.Count];
            var worldXfInv = new Matrix4x4[boneNames.Count];

            string rootPath = UnityTypeConverter.GetPath(go.transform);

            var basisChange = Matrix4x4.identity;

            basisChange[2, 2] = -1;

            for (int i = 0; i < boneNames.Count; i++)
            {
                var bonePath = boneNames[i];
                if (!exportContext.pathToBone.ContainsKey(bonePath))
                {
                    sample.joints[i] = "";
                    continue;
                }

                var bone = exportContext.pathToBone[bonePath];
                sample.joints[i] = bonePath.Replace(rootPath + "/", "");

                worldXf[i] = bone.localToWorldMatrix;
                if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
                {
                    worldXf[i] = UnityTypeConverter.ChangeBasis(worldXf[i]);
                }

                worldXfInv[i] = worldXf[i].inverse;
            }

            var rootXf = skelRoot.localToWorldMatrix.inverse;

            if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
            {
                rootXf = UnityTypeConverter.ChangeBasis(rootXf);
            }

            var skelWorldTransform = UnityTypeConverter.ToGfMatrix(rootXf);

            pxr.VtMatrix4dArray vtJointsLS    = new pxr.VtMatrix4dArray((uint)boneNames.Count);
            pxr.VtMatrix4dArray vtJointsWS    = UnityTypeConverter.ToVtArray(worldXf);
            pxr.VtMatrix4dArray vtJointsWSInv = UnityTypeConverter.ToVtArray(worldXfInv);

            var translations = new pxr.VtVec3fArray();
            var rotations    = new pxr.VtQuatfArray();

            sample.scales = new pxr.VtVec3hArray();

            var topo = new pxr.UsdSkelTopology(UnityTypeConverter.ToVtArray(sample.joints));

            pxr.UsdCs.UsdSkelComputeJointLocalTransforms(topo,
                                                         vtJointsWS,
                                                         vtJointsWSInv,
                                                         vtJointsLS,
                                                         skelWorldTransform);

            pxr.UsdCs.UsdSkelDecomposeTransforms(
                vtJointsLS,
                translations,
                rotations,
                sample.scales);
            sample.translations = UnityTypeConverter.FromVtArray(translations);
            sample.rotations    = UnityTypeConverter.FromVtArray(rotations);

            scene.Write(objContext.path, sample);
        }
Exemplo n.º 3
0
        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
        }