using System; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Serialization; [RequireComponent(typeof(MeshRenderer)), RequireComponent(typeof(MeshFilter))] public class GrassChunk : MonoBehaviour { [SerializeField] private Material materialLOD0; public Material MaterialLOD0 { get { return materialLOD0; } set { materialLOD0 = value; UpdateLODAssets(); } } [SerializeField] private Mesh meshLOD0; public Mesh MeshLOD0 { get { return meshLOD0; } set { meshLOD0 = value; UpdateLODAssets(); UpdateLOD(); // a new mesh may change the AABB which may affect the LOD } } [SerializeField] private bool enableShadowsLOD0; public bool EnableShadowsLOD0 { get { return enableShadowsLOD0; } set { enableShadowsLOD0 = value; UpdateLODAssets(); } } [SerializeField] private Material materialLOD1; public Material MaterialLOD1 { get { return materialLOD1; } set { materialLOD1 = value; UpdateLODAssets(); } } [SerializeField] private Mesh meshLOD1; public Mesh MeshLOD1 { get { return meshLOD1; } set { meshLOD1 = value; UpdateLODAssets(); UpdateLOD(); // a new mesh may change the AABB which may affect the LOD } } [SerializeField] private bool enableShadowsLOD1; public bool EnableShadowsLOD1 { get { return enableShadowsLOD1; } set { enableShadowsLOD1 = value; UpdateLODAssets(); } } [SerializeField] private Material materialLOD2; public Material MaterialLOD2 { get { return materialLOD2; } set { materialLOD2 = value; UpdateLODAssets(); } } [SerializeField] private Mesh meshLOD2; public Mesh MeshLOD2 { get { return meshLOD2; } set { meshLOD2 = value; UpdateLODAssets(); UpdateLOD(); // a new mesh may change the AABB which may affect the LOD } } [SerializeField] private bool enableShadowsLOD2; public bool EnableShadowsLOD2 { get { return enableShadowsLOD2; } set { enableShadowsLOD2 = value; UpdateLODAssets(); } } private int lod = -1; // set to -1 so LOD.set does not exit early when initially called with 0 public int LOD { get { return lod; } set { if (lod == value) return; lod = value; UpdateLODAssets(); } } private void Update() { UpdateLOD(); } public void UpdateLOD() { var meshRenderer = GetComponent(); if (Camera.main) { // using meshRenderer.bounds may cause a loop where LOD switches constantly var sqrCamDist = meshRenderer.bounds.SqrDistance(Camera.main.transform.position); if (sqrCamDist > 20f * 20f) { LOD = 2; } else if (sqrCamDist > 5f * 5f) { LOD = 1; } else { LOD = 0; } } } private void UpdateLODAssets() { var meshRenderer = GetComponent(); var meshFilter = GetComponent(); switch (lod) { case 0: meshRenderer.sharedMaterial = materialLOD0; meshRenderer.shadowCastingMode = EnableShadowsLOD0 ? ShadowCastingMode.On : ShadowCastingMode.Off; meshFilter.sharedMesh = meshLOD0; break; case 1: meshRenderer.sharedMaterial = materialLOD1; meshRenderer.shadowCastingMode = EnableShadowsLOD1 ? ShadowCastingMode.On : ShadowCastingMode.Off; meshFilter.sharedMesh = meshLOD1; break; case 2: meshRenderer.sharedMaterial = materialLOD2; meshRenderer.shadowCastingMode = EnableShadowsLOD2 ? ShadowCastingMode.On : ShadowCastingMode.Off; meshFilter.sharedMesh = meshLOD2; break; } } private void OnDrawGizmos() { Gizmos.color = new Color(1f, 0.7f, 0f, 0.5f); var meshRenderer = GetComponent(); var bounds = meshRenderer.bounds; Gizmos.DrawWireCube(bounds.center, bounds.size); } }