/// <summary>
        /// Creates and returns a new mesh with generated lightmap uvs (Editor Only)
        /// </summary>
        public Mesh GetMesh_GenerateSecondaryUVSet(UnityEditor.UnwrapParam param)
        {
            Mesh shape = GetMesh();

            // for light mapping
            UnityEditor.Unwrapping.GenerateSecondaryUVSet(shape, param);

            return(shape);
        }
Example #2
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
        }
 protected void SecondaryUV(Mesh m)
 {
     UnityEditor.UnwrapParam param = new UnityEditor.UnwrapParam();
     UnityEditor.UnwrapParam.SetDefaults(out param);
     UnityEditor.Unwrapping.GenerateSecondaryUVSet(m, param);
 }
        private static void GenerateLightmapUVsForInstance(ChiselModel model, ChiselRenderComponents renderComponents, Mesh generatedMesh)
        {
            // Avoid light mapping multiple times, when the same mesh is used on multiple MeshRenderers
            if (ChiselSharedUnityMeshManager.HasLightmapUVs(generatedMesh))
            {
                return;
            }

            if (renderComponents == null ||
                !renderComponents.meshFilter ||
                !renderComponents.meshRenderer)
            {
                return;
            }

            var uvSettings = model.UVGenerationSettings;
            var param      = new UnityEditor.UnwrapParam();

            UnityEditor.UnwrapParam.SetDefaults(out param);
            param.angleError = Mathf.Clamp(uvSettings.angleError, SerializableUnwrapParam.minAngleError, SerializableUnwrapParam.maxAngleError);
            param.areaError  = Mathf.Clamp(uvSettings.areaError, SerializableUnwrapParam.minAreaError, SerializableUnwrapParam.maxAreaError);
            param.hardAngle  = Mathf.Clamp(uvSettings.hardAngle, SerializableUnwrapParam.minHardAngle, SerializableUnwrapParam.maxHardAngle);
            param.packMargin = Mathf.Clamp(uvSettings.packMarginPixels, SerializableUnwrapParam.minPackMargin, SerializableUnwrapParam.maxPackMargin) / 256.0f;

            var oldVertices = generatedMesh.vertices;

            if (oldVertices.Length == 0)
            {
                return;
            }

            // TODO: can we avoid creating a temporary Mesh? if not; make sure CSGSharedUnityMeshManager is handled correctly

            var oldUV        = generatedMesh.uv;
            var oldNormals   = generatedMesh.normals;
            var oldTangents  = generatedMesh.tangents;
            var oldTriangles = generatedMesh.triangles;

            var tempMesh = new Mesh
            {
                vertices  = oldVertices,
                normals   = oldNormals,
                uv        = oldUV,
                tangents  = oldTangents,
                triangles = oldTriangles
            };

            var lightmapGenerationTime = UnityEditor.EditorApplication.timeSinceStartup;

            UnityEditor.Unwrapping.GenerateSecondaryUVSet(tempMesh, param);
            lightmapGenerationTime = UnityEditor.EditorApplication.timeSinceStartup - lightmapGenerationTime;

            // TODO: make a nicer text here
            Debug.Log("Generating lightmap UVs (by Unity) for the mesh '" + generatedMesh.name + "' of the Model named \"" + model.name + "\"\n" +
                      "\tUV generation in " + (lightmapGenerationTime * 1000) + " ms\n", model);

            // Modify the original mesh, since it is shared
            generatedMesh.uv2 = tempMesh.uv2;   // static lightmaps
            generatedMesh.uv3 = tempMesh.uv3;   // real-time lightmaps
            ChiselSharedUnityMeshManager.SetHasLightmapUVs(generatedMesh, true);

            renderComponents.meshFilter.sharedMesh = null;
            renderComponents.meshFilter.sharedMesh = generatedMesh;
            UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(model.gameObject.scene);
        }