/// <summary>
 /// Generates the LODs for this object.
 /// </summary>
 /// <param name="statusCallback">The status report callback.</param>
 public void GenerateLODs(LODStatusReportCallback statusCallback = null)
 {
     if (levels != null)
     {
         LODGenerator.GenerateLODs(gameObject, levels, statusCallback);
     }
     generated = true;
 }
        /// <summary>
        /// Generates the LODs and sets up a LOD Group for the specified game object.
        /// </summary>
        /// <param name="gameObj">The game object to set up.</param>
        /// <param name="levels">The LOD levels.</param>
        /// <param name="statusCallback">The optional status report callback.</param>
        public static void GenerateLODs(GameObject gameObj, LODSettings[] levels, LODStatusReportCallback statusCallback = null)
        {
            DestroyLODs(gameObj);

            var transform        = gameObj.transform;
            var meshRenderers    = gameObj.GetComponentsInChildren <MeshRenderer>();
            var skinnedRenderers = gameObj.GetComponentsInChildren <SkinnedMeshRenderer>();

            // Check if there's anything to do
            if (meshRenderers.Length == 0 && skinnedRenderers.Length == 0)
            {
                return;
            }

            var lodsParentObj = new GameObject(ParentGameObjectName);
            var lodsParent    = lodsParentObj.transform;

            lodsParent.parent        = transform;
            lodsParent.localPosition = Vector3.zero;
            lodsParent.localRotation = Quaternion.identity;
            lodsParent.localScale    = Vector3.one;

            var lodGroup = gameObj.GetComponent <LODGroup>();

            if (lodGroup == null)
            {
                lodGroup = gameObj.AddComponent <LODGroup>();
            }

            float screenRelativeTransitionHeight = 0.5f;

            LOD[] lodLevels           = new LOD[levels.Length + 1];
            var   firstLevelRenderers = CombineRenderers(meshRenderers, skinnedRenderers);

            lodLevels[0] = new LOD(screenRelativeTransitionHeight, firstLevelRenderers);
            screenRelativeTransitionHeight *= 0.5f;

            float minimumQuality = 1f;

            for (int levelIndex = 0; levelIndex < levels.Length; levelIndex++)
            {
                var   level   = levels[levelIndex];
                float quality = Mathf.Clamp(level.quality, 0.01f, minimumQuality);
                screenRelativeTransitionHeight *= quality * quality;
                GameObject lodObj    = new GameObject(string.Format("Level{0}", levelIndex));
                var        lodParent = lodObj.transform;
                lodParent.parent        = lodsParent;
                lodParent.localPosition = Vector3.zero;
                lodParent.localRotation = Quaternion.identity;
                lodParent.localScale    = Vector3.one;

                DecimationAlgorithm.StatusReportCallback levelStatusCallback = null;
                if (statusCallback != null)
                {
                    levelStatusCallback = (iteration, originalTris, currentTris, targetTris) =>
                    {
                        statusCallback.Invoke(levelIndex, iteration, originalTris, currentTris, targetTris);
                    };
                }

                GameObject staticLodObj     = lodObj;
                GameObject skinnedLodObj    = lodObj;
                Transform  staticLodParent  = lodParent;
                Transform  skinnedLodParent = lodParent;
                if (meshRenderers.Length > 0 && skinnedRenderers.Length > 0)
                {
                    staticLodObj                  = new GameObject("Static", typeof(MeshFilter), typeof(MeshRenderer));
                    staticLodParent               = staticLodObj.transform;
                    staticLodParent.parent        = lodParent;
                    staticLodParent.localPosition = Vector3.zero;
                    staticLodParent.localRotation = Quaternion.identity;
                    staticLodParent.localScale    = Vector3.one;

                    skinnedLodObj                  = new GameObject("Skinned", typeof(SkinnedMeshRenderer));
                    skinnedLodParent               = skinnedLodObj.transform;
                    skinnedLodParent.parent        = lodParent;
                    skinnedLodParent.localPosition = Vector3.zero;
                    skinnedLodParent.localRotation = Quaternion.identity;
                    skinnedLodParent.localScale    = Vector3.one;
                }

                Renderer[] staticLodRenderers  = null;
                Renderer[] skinnedLodRenderers = null;
                if (meshRenderers.Length > 0)
                {
                    if (level.combineMeshes)
                    {
                        Material[] materials;
                        Mesh       lodMesh = GenerateStaticLOD(transform, meshRenderers, quality, out materials, levelStatusCallback);
                        lodMesh.name = string.Format("{0}_static{1}", gameObj.name, levelIndex);
                        var meshFilter = staticLodObj.AddComponent <MeshFilter>();
                        meshFilter.sharedMesh = lodMesh;
                        var lodRenderer = staticLodObj.AddComponent <MeshRenderer>();
                        lodRenderer.sharedMaterials = materials;
                        SetupLODRenderer(lodRenderer, level);
                        staticLodRenderers = new Renderer[] { lodRenderer };
                    }
                    else
                    {
                        staticLodRenderers = new Renderer[meshRenderers.Length];

                        Material[] materials;
                        for (int rendererIndex = 0; rendererIndex < meshRenderers.Length; rendererIndex++)
                        {
                            var  renderer = meshRenderers[rendererIndex];
                            Mesh lodMesh  = GenerateStaticLOD(transform, renderer, quality, out materials, levelStatusCallback);
                            lodMesh.name = string.Format("{0}_static{1}_{2}", gameObj.name, levelIndex, rendererIndex);

                            var rendererLodObj = new GameObject(renderer.name, typeof(MeshFilter), typeof(MeshRenderer));
                            rendererLodObj.transform.parent        = staticLodParent;
                            rendererLodObj.transform.localPosition = Vector3.zero;
                            rendererLodObj.transform.localRotation = Quaternion.identity;
                            rendererLodObj.transform.localScale    = Vector3.one;
                            var meshFilter = rendererLodObj.GetComponent <MeshFilter>();
                            meshFilter.sharedMesh = lodMesh;
                            var lodRenderer = rendererLodObj.GetComponent <MeshRenderer>();
                            lodRenderer.sharedMaterials = materials;
                            SetupLODRenderer(lodRenderer, level);
                            staticLodRenderers[rendererIndex] = lodRenderer;
                        }
                    }
                }

                if (skinnedRenderers.Length > 0)
                {
                    if (level.combineMeshes)
                    {
                        Transform[] bones;
                        Material[]  materials;
                        Mesh        lodMesh = GenerateSkinnedLOD(transform, skinnedRenderers, quality, out materials, out bones, levelStatusCallback);
                        lodMesh.name = string.Format("{0}_skinned{1}", gameObj.name, levelIndex);
                        Transform rootBone = FindRootBone(transform, bones);

                        var lodRenderer = skinnedLodObj.AddComponent <SkinnedMeshRenderer>();
                        lodRenderer.sharedMesh      = lodMesh;
                        lodRenderer.sharedMaterials = materials;
                        lodRenderer.rootBone        = rootBone;
                        lodRenderer.bones           = bones;
                        SetupLODRenderer(lodRenderer, level);
                        skinnedLodRenderers = new Renderer[] { lodRenderer };
                    }
                    else
                    {
                        skinnedLodRenderers = new Renderer[skinnedRenderers.Length];

                        Transform[] bones;
                        Material[]  materials;
                        for (int rendererIndex = 0; rendererIndex < skinnedRenderers.Length; rendererIndex++)
                        {
                            var  renderer = skinnedRenderers[rendererIndex];
                            Mesh lodMesh  = GenerateSkinnedLOD(transform, renderer, quality, out materials, out bones, levelStatusCallback);
                            lodMesh.name = string.Format("{0}_skinned{1}_{2}", gameObj.name, levelIndex, rendererIndex);
                            Transform rootBone = FindRootBone(transform, bones);

                            var rendererLodObj = new GameObject(renderer.name, typeof(SkinnedMeshRenderer));
                            rendererLodObj.transform.parent        = skinnedLodParent;
                            rendererLodObj.transform.localPosition = Vector3.zero;
                            rendererLodObj.transform.localRotation = Quaternion.identity;
                            rendererLodObj.transform.localScale    = Vector3.one;
                            var lodRenderer = rendererLodObj.GetComponent <SkinnedMeshRenderer>();
                            lodRenderer.sharedMesh      = lodMesh;
                            lodRenderer.sharedMaterials = materials;
                            lodRenderer.rootBone        = rootBone;
                            lodRenderer.bones           = bones;
                            SetupLODRenderer(lodRenderer, level);
                            skinnedLodRenderers[rendererIndex] = lodRenderer;
                        }
                    }
                }

                Renderer[] lodRenderers;
                if (staticLodRenderers != null && skinnedLodRenderers != null)
                {
                    lodRenderers = staticLodRenderers.Concat <Renderer>(skinnedLodRenderers).ToArray();
                }
                else if (staticLodRenderers != null)
                {
                    lodRenderers = staticLodRenderers;
                }
                else if (skinnedLodRenderers != null)
                {
                    lodRenderers = skinnedLodRenderers;
                }
                else
                {
                    lodRenderers = new Renderer[0];
                }

                minimumQuality            = quality;
                lodLevels[levelIndex + 1] = new LOD(screenRelativeTransitionHeight, lodRenderers);
            }

            lodGroup.SetLODs(lodLevels);
        }