Beispiel #1
0
        private static Mesh SimplifyMesh(Mesh mesh, float quality, SimplificationOptions options)
        {
            var meshSimplifier = new MeshSimplifier();

            meshSimplifier.SimplificationOptions = options;
            meshSimplifier.Initialize(mesh);
            meshSimplifier.SimplifyMesh(quality);

            var simplifiedMesh = meshSimplifier.ToMesh();

            simplifiedMesh.bindposes = mesh.bindposes;
            return(simplifiedMesh);
        }
Beispiel #2
0
        private void Reset()
        {
            fadeMode              = LODFadeMode.None;
            animateCrossFading    = false;
            autoCollectRenderers  = true;
            simplificationOptions = SimplificationOptions.Default;

            levels = new LODLevel[]
            {
                new LODLevel(0.5f, 1f)
                {
                    CombineMeshes        = false,
                    CombineSubMeshes     = false,
                    SkinQuality          = SkinQuality.Auto,
                    ShadowCastingMode    = UnityEngine.Rendering.ShadowCastingMode.On,
                    ReceiveShadows       = true,
                    SkinnedMotionVectors = true,
                    LightProbeUsage      = UnityEngine.Rendering.LightProbeUsage.BlendProbes,
                    ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.BlendProbes,
                },
                new LODLevel(0.17f, 0.65f)
                {
                    CombineMeshes        = true,
                    CombineSubMeshes     = false,
                    SkinQuality          = SkinQuality.Auto,
                    ShadowCastingMode    = UnityEngine.Rendering.ShadowCastingMode.On,
                    ReceiveShadows       = true,
                    SkinnedMotionVectors = true,
                    LightProbeUsage      = UnityEngine.Rendering.LightProbeUsage.BlendProbes,
                    ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Simple
                },
                new LODLevel(0.02f, 0.4225f)
                {
                    CombineMeshes        = true,
                    CombineSubMeshes     = true,
                    SkinQuality          = SkinQuality.Bone2,
                    ShadowCastingMode    = UnityEngine.Rendering.ShadowCastingMode.Off,
                    ReceiveShadows       = false,
                    SkinnedMotionVectors = false,
                    LightProbeUsage      = UnityEngine.Rendering.LightProbeUsage.Off,
                    ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off
                }
            };
        }
        private static Mesh SimplifyMesh(Mesh mesh, float quality, SimplificationOptions options)
        {
            var meshSimplifier = new MeshSimplifier();

            meshSimplifier.PreserveBorderEdges     = options.PreserveBorderEdges;
            meshSimplifier.PreserveUVSeamEdges     = options.PreserveUVSeamEdges;
            meshSimplifier.PreserveUVFoldoverEdges = options.PreserveUVFoldoverEdges;
            meshSimplifier.EnableSmartLink         = options.EnableSmartLink;
            meshSimplifier.VertexLinkDistance      = options.VertexLinkDistance;
            meshSimplifier.MaxIterationCount       = options.MaxIterationCount;
            meshSimplifier.Agressiveness           = options.Agressiveness;

            meshSimplifier.Initialize(mesh);
            meshSimplifier.SimplifyMesh(quality);

            var simplifiedMesh = meshSimplifier.ToMesh();

            simplifiedMesh.bindposes = mesh.bindposes;
            return(simplifiedMesh);
        }
        /// <summary>
        /// Generates the LODs and sets up a LOD Group for the specified game object.
        /// </summary>
        /// <param name="gameObject">The game object to set up.</param>
        /// <param name="levels">The LOD levels to set up.</param>
        /// <param name="autoCollectRenderers">If the renderers under the game object and any children should be automatically collected.
        /// Enabling this will ignore any renderers defined under each LOD level.</param>
        /// <param name="simplificationOptions">The mesh simplification options.</param>
        /// <param name="saveAssetsPath">The path to where the generated assets should be saved. Can be null or empty to use the default path.</param>
        /// <returns>The generated LOD Group.</returns>
        public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bool autoCollectRenderers, SimplificationOptions simplificationOptions, string saveAssetsPath)
        {
            if (gameObject == null)
            {
                throw new System.ArgumentNullException(nameof(gameObject));
            }
            else if (levels == null)
            {
                throw new System.ArgumentNullException(nameof(levels));
            }

            var transform         = gameObject.transform;
            var existingLodParent = transform.Find(LODParentGameObjectName);

            if (existingLodParent != null)
            {
                throw new System.InvalidOperationException("The game object already appears to have LODs. Please remove them first.");
            }

            var existingLodGroup = gameObject.GetComponent <LODGroup>();

            if (existingLodGroup != null)
            {
                throw new System.InvalidOperationException("The game object already appears to have a LOD Group. Please remove it first.");
            }

            saveAssetsPath = ValidateSaveAssetsPath(saveAssetsPath);

            var lodParentGameObject = new GameObject(LODParentGameObjectName);
            var lodParent           = lodParentGameObject.transform;

            ParentAndResetTransform(lodParent, transform);

            var lodGroup = gameObject.AddComponent <LODGroup>();

            Renderer[] allRenderers = null;
            if (autoCollectRenderers)
            {
                // Collect all enabled renderers under the game object
                allRenderers = GetChildRenderersForLOD(gameObject);
            }

            var renderersToDisable = new List <Renderer>((allRenderers != null ? allRenderers.Length : 10));
            var lods = new LOD[levels.Length];

            for (int levelIndex = 0; levelIndex < levels.Length; levelIndex++)
            {
                var level           = levels[levelIndex];
                var levelGameObject = new GameObject(string.Format("Level{0:00}", levelIndex));
                var levelTransform  = levelGameObject.transform;
                ParentAndResetTransform(levelTransform, lodParent);

                Renderer[] originalLevelRenderers = allRenderers ?? level.Renderers;
                var        levelRenderers         = new List <Renderer>((originalLevelRenderers != null ? originalLevelRenderers.Length : 0));

                if (originalLevelRenderers != null && originalLevelRenderers.Length > 0)
                {
                    var meshRenderers = (from renderer in originalLevelRenderers
                                         where renderer.enabled && renderer as MeshRenderer != null
                                         select renderer as MeshRenderer).ToArray();
                    var skinnedMeshRenderers = (from renderer in originalLevelRenderers
                                                where renderer.enabled && renderer as SkinnedMeshRenderer != null
                                                select renderer as SkinnedMeshRenderer).ToArray();

                    StaticRenderer[]  staticRenderers;
                    SkinnedRenderer[] skinnedRenderers;
                    if (level.CombineMeshes)
                    {
                        staticRenderers  = CombineStaticMeshes(transform, levelIndex, meshRenderers);
                        skinnedRenderers = CombineSkinnedMeshes(transform, levelIndex, skinnedMeshRenderers);
                    }
                    else
                    {
                        staticRenderers  = GetStaticRenderers(meshRenderers);
                        skinnedRenderers = GetSkinnedRenderers(skinnedMeshRenderers);
                    }

                    if (staticRenderers != null)
                    {
                        for (int rendererIndex = 0; rendererIndex < staticRenderers.Length; rendererIndex++)
                        {
                            var renderer = staticRenderers[rendererIndex];
                            var mesh     = renderer.mesh;

                            // Simplify the mesh if necessary
                            if (level.Quality < 1f)
                            {
                                mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);
                                SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, renderer.mesh.name, saveAssetsPath);

                                if (renderer.isNewMesh)
                                {
                                    DestroyObject(renderer.mesh);
                                    renderer.mesh = null;
                                }
                            }

                            string rendererName  = string.Format("{0:000}_static_{1}", rendererIndex, renderer.name);
                            var    levelRenderer = CreateLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, ref level);
                            levelRenderers.Add(levelRenderer);
                        }
                    }

                    if (skinnedRenderers != null)
                    {
                        for (int rendererIndex = 0; rendererIndex < skinnedRenderers.Length; rendererIndex++)
                        {
                            var renderer = skinnedRenderers[rendererIndex];
                            var mesh     = renderer.mesh;

                            // Simplify the mesh if necessary
                            if (level.Quality < 1f)
                            {
                                mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);
                                SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, renderer.mesh.name, saveAssetsPath);

                                if (renderer.isNewMesh)
                                {
                                    DestroyObject(renderer.mesh);
                                    renderer.mesh = null;
                                }
                            }

                            string rendererName  = string.Format("{0:000}_skinned_{1}", rendererIndex, renderer.name);
                            var    levelRenderer = CreateSkinnedLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, renderer.rootBone, renderer.bones, ref level);
                            levelRenderers.Add(levelRenderer);
                        }
                    }
                }

                foreach (var renderer in originalLevelRenderers)
                {
                    if (!renderersToDisable.Contains(renderer))
                    {
                        renderersToDisable.Add(renderer);
                    }
                }

                lods[levelIndex] = new LOD(level.ScreenRelativeTransitionHeight, levelRenderers.ToArray());
            }

            CreateBackup(gameObject, renderersToDisable.ToArray());
            foreach (var renderer in renderersToDisable)
            {
                renderer.enabled = false;
            }

            lodGroup.animateCrossFading = false;
            lodGroup.SetLODs(lods);
            return(lodGroup);
        }
 /// <summary>
 /// Generates the LODs and sets up a LOD Group for the specified game object.
 /// </summary>
 /// <param name="gameObject">The game object to set up.</param>
 /// <param name="levels">The LOD levels to set up.</param>
 /// <param name="autoCollectRenderers">If the renderers under the game object and any children should be automatically collected.
 /// Enabling this will ignore any renderers defined under each LOD level.</param>
 /// <param name="simplificationOptions">The mesh simplification options.</param>
 /// <returns>The generated LOD Group.</returns>
 public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bool autoCollectRenderers, SimplificationOptions simplificationOptions)
 {
     return(GenerateLODs(gameObject, levels, autoCollectRenderers, simplificationOptions, null));
 }
Beispiel #6
0
        private static Renderer CreateLevelRenderer(GameObject gameObject, int levelIndex, ref LODLevel level, Transform levelTransform, int rendererIndex, RendererInfo renderer, ref SimplificationOptions simplificationOptions, string saveAssetsPath)
        {
            var mesh = renderer.mesh;

            // Simplify the mesh if necessary
            if (level.Quality < 1f)
            {
                mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);

#if UNITY_EDITOR
                SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, mesh.name, saveAssetsPath);
#endif

                if (renderer.isNewMesh)
                {
                    DestroyObject(renderer.mesh);
                }
            }

            if (renderer.isStatic)
            {
                string rendererName = string.Format("{0:000}_static_{1}", rendererIndex, renderer.name);
                return(CreateStaticLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, ref level));
            }
            else
            {
                string rendererName = string.Format("{0:000}_skinned_{1}", rendererIndex, renderer.name);
                return(CreateSkinnedLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, renderer.rootBone, renderer.bones, ref level));
            }
        }
        /// <summary>
        /// Generates the LODs and sets up a LOD Group for the specified game object.
        /// </summary>
        /// <param name="gameObject">The game object to set up.</param>
        /// <param name="levels">The LOD levels to set up.</param>
        /// <param name="autoCollectRenderers">If the renderers under the game object and any children should be automatically collected.
        /// Enabling this will ignore any renderers defined under each LOD level.</param>
        /// <param name="simplificationOptions">The mesh simplification options.</param>
        /// <param name="saveAssetsPath">The path to where the generated assets should be saved. Can be null or empty to use the default path.</param>
        /// <returns>The generated LOD Group.</returns>
        public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bool autoCollectRenderers, SimplificationOptions simplificationOptions, string saveAssetsPath)
        {
            if (gameObject == null)
            {
                throw new System.ArgumentNullException(nameof(gameObject));
            }
            else if (levels == null)
            {
                throw new System.ArgumentNullException(nameof(levels));
            }

            var transform         = gameObject.transform;
            var existingLodParent = transform.Find(LODParentGameObjectName);

            if (existingLodParent != null)
            {
                throw new System.InvalidOperationException("The game object already appears to have LODs. Please remove them first.");
            }

            var existingLodGroup = gameObject.GetComponent <LODGroup>();

            if (existingLodGroup != null)
            {
                throw new System.InvalidOperationException("The game object already appears to have a LOD Group. Please remove it first.");
            }

            saveAssetsPath = ValidateSaveAssetsPath(saveAssetsPath);

            var lodParentGameObject = new GameObject(LODParentGameObjectName);
            var lodParent           = lodParentGameObject.transform;

            ParentAndResetTransform(lodParent, transform);

            var lodGroup = gameObject.AddComponent <LODGroup>();

            Renderer[] allRenderers = null;
            if (autoCollectRenderers)
            {
                // Collect all enabled renderers under the game object
                allRenderers = GetChildRenderersForLOD(gameObject);
            }

            var renderersToDisable = new List <Renderer>((allRenderers != null ? allRenderers.Length : 10));
            var lods = new LOD[levels.Length];



            // No need to get the Renderers again and again we just cache it for once

            var meshRenderers = (from renderer in allRenderers
                                 let meshFilter = renderer.transform.GetComponent <MeshFilter>()
                                                  where renderer.enabled && renderer as MeshRenderer != null &&
                                                  meshFilter != null &&
                                                  meshFilter.sharedMesh != null
                                                  select renderer as MeshRenderer).ToArray();
            var skinnedMeshRenderers = (from renderer in allRenderers
                                        where renderer.enabled && renderer as SkinnedMeshRenderer != null &&
                                        (renderer as SkinnedMeshRenderer).sharedMesh != null
                                        select renderer as SkinnedMeshRenderer).ToArray();


            List <Mesh> meshesChangedToReadible = new List <Mesh>();
            bool        areAnyCombinedLevels    = false;

            foreach (var lodLevel in levels)
            {
                if (lodLevel.CombineMeshes)
                {
                    areAnyCombinedLevels = true; break;
                }
            }

            // If there is any Lod level with the Combine meshes option selected then
            // we enable Read/Write flag on the original meshes
            if (areAnyCombinedLevels)
            {
                foreach (var renderer in meshRenderers)
                {
                    var meshFilter = renderer.GetComponent <MeshFilter>();

                    if (meshFilter == null || meshFilter.sharedMesh == null)
                    {
                        continue;
                    }

                    if (!meshFilter.sharedMesh.isReadable)
                    {
                        MeshUtils.ChangeMeshReadibility(meshFilter.sharedMesh, true, false);

                        if (meshFilter.sharedMesh.isReadable)
                        {
                            meshesChangedToReadible.Add(meshFilter.sharedMesh);
                        }
                    }
                }

                foreach (var renderer in skinnedMeshRenderers)
                {
                    if (renderer == null || renderer.sharedMesh == null)
                    {
                        continue;
                    }

                    if (!renderer.sharedMesh.isReadable)
                    {
                        MeshUtils.ChangeMeshReadibility(renderer.sharedMesh, true, false);

                        if (renderer.sharedMesh.isReadable)
                        {
                            meshesChangedToReadible.Add(renderer.sharedMesh);
                        }
                    }
                }
            }


            System.Exception exception = null;

            try
            {
                for (int levelIndex = 0; levelIndex < levels.Length; levelIndex++)
                {
                    var level           = levels[levelIndex];
                    var levelGameObject = new GameObject(string.Format("Level{0:00}", levelIndex));
                    var levelTransform  = levelGameObject.transform;
                    ParentAndResetTransform(levelTransform, lodParent);

                    Renderer[] originalLevelRenderers = allRenderers ?? level.Renderers;
                    var        levelRenderers         = new List <Renderer>((originalLevelRenderers != null ? originalLevelRenderers.Length : 0));

                    if (originalLevelRenderers != null && originalLevelRenderers.Length > 0)
                    {
                        StaticRenderer[]  staticRenderers;
                        SkinnedRenderer[] skinnedRenderers;
                        if (level.CombineMeshes)
                        {
                            staticRenderers  = CombineStaticMeshes(transform, levelIndex, meshRenderers);
                            skinnedRenderers = CombineSkinnedMeshes(transform, levelIndex, skinnedMeshRenderers);
                        }
                        else
                        {
                            staticRenderers  = GetStaticRenderers(meshRenderers);
                            skinnedRenderers = GetSkinnedRenderers(skinnedMeshRenderers);
                        }

                        if (staticRenderers != null)
                        {
                            for (int rendererIndex = 0; rendererIndex < staticRenderers.Length; rendererIndex++)
                            {
                                var renderer = staticRenderers[rendererIndex];
                                var mesh     = renderer.mesh;

                                // Simplify the mesh if necessary
                                if (level.Quality < 1f)
                                {
                                    mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);
                                    SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, renderer.mesh.name, saveAssetsPath);

                                    if (renderer.isNewMesh)
                                    {
                                        DestroyObject(renderer.mesh);
                                        renderer.mesh = null;
                                    }
                                }

                                string rendererName  = string.Format("{0:000}_static_{1}", rendererIndex, renderer.name);
                                var    levelRenderer = CreateLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, ref level);
                                levelRenderers.Add(levelRenderer);
                            }
                        }

                        if (skinnedRenderers != null)
                        {
                            for (int rendererIndex = 0; rendererIndex < skinnedRenderers.Length; rendererIndex++)
                            {
                                var renderer = skinnedRenderers[rendererIndex];
                                var mesh     = renderer.mesh;

                                // Simplify the mesh if necessary
                                if (level.Quality < 1f)
                                {
                                    mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);
                                    SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, renderer.mesh.name, saveAssetsPath);

                                    if (renderer.isNewMesh)
                                    {
                                        DestroyObject(renderer.mesh);
                                        renderer.mesh = null;
                                    }
                                }

                                string rendererName  = string.Format("{0:000}_skinned_{1}", rendererIndex, renderer.name);
                                var    levelRenderer = CreateSkinnedLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, renderer.rootBone, renderer.bones, ref level);
                                levelRenderers.Add(levelRenderer);
                            }
                        }
                    }

                    foreach (var renderer in originalLevelRenderers)
                    {
                        if (!renderersToDisable.Contains(renderer))
                        {
                            renderersToDisable.Add(renderer);
                        }
                    }

                    lods[levelIndex] = new LOD(level.ScreenRelativeTransitionHeight, levelRenderers.ToArray());
                }
            }

            catch (System.Exception ex)
            {
                exception = ex;
            }


            //Restore meshes Read/Write flag
            foreach (var mesh in meshesChangedToReadible)
            {
                Debug.LogWarning($"Mesh \"{mesh.name}\" was not readible so we marked it readible for the mesh combining process to complete and changed it back to non-readible after completion. This process can slow down LOD generation. You may want to mark this mesh Read/Write enabled in the model import settings, so that next time LOD generation on this model can be faster.");
                MeshUtils.ChangeMeshReadibility(mesh, false, false);
            }


            if (exception != null)
            {
                throw exception;
            }



            CreateBackup(gameObject, renderersToDisable.ToArray());

            foreach (var renderer in renderersToDisable)
            {
                renderer.enabled = false;
            }

            lodGroup.animateCrossFading = false;
            lodGroup.SetLODs(lods);
            return(lodGroup);
        }