using System.Collections.Generic; using UnityEngine; namespace ProceduralWorlds.Flora { [System.Serializable] public class TreeColliderData { public string m_treeName; public string m_vegetationType; public int m_id; public GameObject m_prefab; public bool m_buildCollider = true; } [System.Serializable] public class BakerColliderData { public bool m_drawMeshPreview = false; public Material m_defaultMaterial; public Mesh m_box; public Mesh m_capsule; public Mesh m_sphere; } public class TreeBakerProfile : ScriptableObject { public Terrain m_terrain; public GameObject m_parentObject; public BakerColliderData m_colliderData = new BakerColliderData(); public List Data = new List(); private List m_tempGeneratedPrefabs = new List(); public bool CheckLastTreePrototypes(TreePrototype[] lastTreePrototypes) { if (lastTreePrototypes == null) { return true; } if (m_terrain != null) { TreePrototype[] treePrototypes = m_terrain.terrainData.treePrototypes; if (treePrototypes.Length > 0) { if (lastTreePrototypes.Length != treePrototypes.Length) { return true; } for (int i = 0; i < treePrototypes.Length; i++) { TreePrototype prototype = treePrototypes[i]; TreePrototype lastPrototype = lastTreePrototypes[i]; if (prototype.prefab != lastPrototype.prefab) { return true; } } } } return false; } public void BakeTerrainTreeColliders(Terrain terrain) { if (terrain == null) { return; } m_terrain = terrain; if (m_terrain != null && Data.Count > 0) { ClearBakedColliders(); GetOrCreateRootParentObject(m_terrain); if (m_terrain.terrainData.treeInstances.Length < 1) { Debug.Log("No tree instances we're found to spawn from"); return; } //Build instance list List allInstances = new List(); foreach (TreeInstance instance in m_terrain.terrainData.treeInstances) { allInstances.Add(instance); } for (int i = 0; i < Data.Count; i++) { Collider dataCollider = GetCollider(Data[i].m_prefab); if (Data[i].m_buildCollider && dataCollider != null) { List currentTreeInstances = allInstances.FindAll(instance => instance.prototypeIndex == Data[i].m_id); GameObject treeInstanceParent = CreateTreeInstanceParent(Data[i].m_treeName); treeInstanceParent.transform.SetParent(m_parentObject.transform); for (int j = 0; j < currentTreeInstances.Count; j++) { TreeInstance instance = currentTreeInstances[j]; GameObject treeColliderObject = new GameObject(Data[i].m_treeName + " " + currentTreeInstances[j].position); MeshRenderer meshRenderer = null; MeshFilter meshFilter = null; if (m_colliderData.m_drawMeshPreview) { meshRenderer = treeColliderObject.AddComponent(); meshFilter = treeColliderObject.AddComponent(); } treeColliderObject.isStatic = true; treeColliderObject.transform.SetParent(treeInstanceParent.transform); if (dataCollider.GetType() == typeof(BoxCollider)) { BoxCollider dataBoxCollider = (BoxCollider) dataCollider; BoxCollider newBoxCollider = treeColliderObject.AddComponent(); //SetData treeColliderObject.transform.position = TranslatePosition(instance.position, m_terrain); treeColliderObject.transform.eulerAngles = new Vector3(0f, instance.rotation, 0f); newBoxCollider.center = dataBoxCollider.center; newBoxCollider.size = dataBoxCollider.size; //Setup Mesh treeColliderObject.transform.localScale = Vector3.one; if (m_colliderData.m_drawMeshPreview) { ConvertDefaultPreviewMaterial(m_colliderData.m_defaultMaterial); meshRenderer.sharedMaterial = m_colliderData.m_defaultMaterial; meshFilter.sharedMesh = m_colliderData.m_box; } } else if (dataCollider.GetType() == typeof(SphereCollider)) { SphereCollider dataSphereCollider = (SphereCollider)dataCollider; SphereCollider newSphereCollider = treeColliderObject.AddComponent(); //SetData treeColliderObject.transform.position = TranslatePosition(instance.position, m_terrain); treeColliderObject.transform.eulerAngles = new Vector3(0f, instance.rotation, 0f); newSphereCollider.center = dataSphereCollider.center; newSphereCollider.radius = dataSphereCollider.radius; //Setup Mesh treeColliderObject.transform.localScale = Vector3.one; if (m_colliderData.m_drawMeshPreview) { ConvertDefaultPreviewMaterial(m_colliderData.m_defaultMaterial); meshRenderer.sharedMaterial = m_colliderData.m_defaultMaterial; meshFilter.sharedMesh = m_colliderData.m_sphere; } } else if (dataCollider.GetType() == typeof(CapsuleCollider)) { CapsuleCollider dataCapsuleCollider = (CapsuleCollider)dataCollider; CapsuleCollider newCapsuleCollider = treeColliderObject.AddComponent(); //SetData treeColliderObject.transform.position = TranslatePosition(instance.position, m_terrain); treeColliderObject.transform.eulerAngles = new Vector3(0f, instance.rotation, 0f); newCapsuleCollider.center = dataCapsuleCollider.center; newCapsuleCollider.radius = dataCapsuleCollider.radius; newCapsuleCollider.direction = dataCapsuleCollider.direction; newCapsuleCollider.height = dataCapsuleCollider.height; //Setup Mesh treeColliderObject.transform.localScale = Vector3.one; if (m_colliderData.m_drawMeshPreview) { ConvertDefaultPreviewMaterial(m_colliderData.m_defaultMaterial); meshRenderer.sharedMaterial = m_colliderData.m_defaultMaterial; meshFilter.sharedMesh = m_colliderData.m_capsule; } } else if (dataCollider.GetType() == typeof(MeshCollider)) { MeshCollider dataMeshCollider = (MeshCollider)dataCollider; MeshCollider newMeshCollider = treeColliderObject.AddComponent(); //SetData treeColliderObject.transform.localScale = Data[i].m_prefab.transform.localScale; treeColliderObject.transform.position = TranslatePosition(instance.position, m_terrain); treeColliderObject.transform.eulerAngles = new Vector3(0f, instance.rotation, 0f); newMeshCollider.convex = dataMeshCollider.convex; newMeshCollider.cookingOptions = dataMeshCollider.cookingOptions; newMeshCollider.sharedMesh = dataMeshCollider.sharedMesh; //Setup Mesh if (m_colliderData.m_drawMeshPreview) { ConvertDefaultPreviewMaterial(m_colliderData.m_defaultMaterial); meshRenderer.sharedMaterial = m_colliderData.m_defaultMaterial; meshFilter.sharedMesh = dataMeshCollider.sharedMesh; } } } treeInstanceParent.transform.localPosition = Vector3.zero; } else { Debug.Log("Collision Will Be Skipped: " + Data[i].m_treeName + " does not have a collider present (Box, Capsule, Sphere or Mesh Collider)"); } } m_parentObject.transform.localPosition = Vector3.zero; CleanUpTemps(); } } public void ClearBakedColliders() { if (m_parentObject != null) { DestroyImmediate(m_parentObject); } GameObject[] colliderObjects = GameObject.FindGameObjectsWithTag("Flora Tree Collision"); if (colliderObjects.Length > 0) { foreach (GameObject colliderObject in colliderObjects) { DestroyImmediate(colliderObject); } } } public void SetBuildState(bool build) { if (Data.Count > 0) { foreach (TreeColliderData data in Data) { data.m_buildCollider = build; } } } /// /// Insures that the default render material works in the current render pipeline /// /// private static void ConvertDefaultPreviewMaterial(Material material) { if (material != null) { switch (FloraTerrainTile.GetRenderPipline()) { case FloraTerrainTile.FLORARP.Builtin: { material.shader = Shader.Find("Standard"); break; } case FloraTerrainTile.FLORARP.URP: { material.shader = Shader.Find("Universal Render Pipeline/Lit"); break; } case FloraTerrainTile.FLORARP.HDRP: { material.shader = Shader.Find("HDRP/Lit"); break; } } } } private void GetOrCreateRootParentObject(Terrain terrain) { if (m_parentObject == null && terrain != null) { m_parentObject = new GameObject("Generated Tree Colliders " + terrain.name); m_parentObject.transform.SetParent(terrain.transform); m_parentObject.tag = "Flora Tree Collision"; } } private GameObject CreateTreeInstanceParent(string name) { return new GameObject(name); } private Collider GetCollider(GameObject treeObject) { GameObject tempObject = Instantiate(treeObject); m_tempGeneratedPrefabs.Add(tempObject); Collider collider = null; if (tempObject != null) { collider = tempObject.GetComponent(); if (collider == null) { collider = tempObject.GetComponentInChildren(); } } return collider; } private void CleanUpTemps() { if (m_tempGeneratedPrefabs.Count > 0) { foreach (GameObject tempGeneratedPrefab in m_tempGeneratedPrefabs) { DestroyImmediate(tempGeneratedPrefab); } m_tempGeneratedPrefabs.Clear(); } } private Vector3 TranslatePosition(Vector3 treePosition, Terrain terrain) { TerrainData data = terrain.terrainData; float width = data.size.x; float height = data.size.z; float y = data.size.y; return new Vector3(treePosition.x * width, treePosition.y * y - 0.05f, treePosition.z * height); } } }