Files
Fishing2/Assets/Procedural Worlds/GeNa/Scripts/Runtime/Utilities/GeNaTerrainTreeConverter.cs
2026-02-28 12:43:44 +08:00

360 lines
13 KiB
C#

using System.Collections.Generic;
using UnityEngine;
namespace GeNa.Core
{
public enum ConversionType { GameObject, TerrainTree, Flora }
public enum ConversionDisableMode { Disable, DestroyAndInstantiate }
[System.Serializable]
public class TreeInstasiateData
{
public GameObject m_prefab;
public Vector3 m_position;
public Vector3 m_rotation;
public Vector3 m_scale;
}
public class GeNaTerrainTreeConverter : MonoBehaviour
{
#region Variables
public List<GameObject> m_storedGameObjects = new List<GameObject>();
public List<TreeInstance> m_storedTreeInstanceData = new List<TreeInstance>();
public List<TreeInstasiateData> m_instasiateData = new List<TreeInstasiateData>();
public ConversionType m_conversionType = ConversionType.GameObject;
public bool m_ingestIntoFlora = false;
public bool m_syncTreeInstancesToGameObjects = true;
public ConversionDisableMode m_conversionDisableMode = ConversionDisableMode.Disable;
#endregion
#region Unity Functions
private void Start()
{
if (m_ingestIntoFlora)
{
foreach (GameObject storedGameObject in m_storedGameObjects)
{
switch (m_conversionDisableMode)
{
case ConversionDisableMode.Disable:
{
storedGameObject.SetActive(false);
break;
}
case ConversionDisableMode.DestroyAndInstantiate:
{
DestroyImmediate(storedGameObject);
break;
}
}
}
//Add ingest flora code base
}
}
#endregion
#region Utils
/// <summary>
/// Cleans up the cached data
/// </summary>
/// <param name="removeTreeInstances"></param>
public void CleanCachedData(bool removeTreeInstances = true)
{
if (removeTreeInstances)
{
m_storedTreeInstanceData.Clear();
}
if (m_storedGameObjects == null)
{
m_storedGameObjects = new List<GameObject>();
}
if (m_storedGameObjects.Count > 0)
{
for (int i = m_storedGameObjects.Count; i-- > 0;)
{
if (m_storedGameObjects[i] == null)
{
m_storedGameObjects.RemoveAt(i);
}
}
}
}
/// <summary>
/// Removes the tree instances from the list of instances
/// </summary>
/// <param name="instances"></param>
/// <param name="terrain"></param>
public void RemoveTreeInstances(List<TreeInstance> instances, Terrain terrain)
{
List<TreeInstance> treeInstances = new List<TreeInstance>();
treeInstances.AddRange(terrain.terrainData.treeInstances);
if (treeInstances.Count > 0)
{
foreach (TreeInstance instance in instances)
{
treeInstances.RemoveAll(inst => GeNaUtility.ApproximatelyEqual(inst.position.x, instance.position.x) && GeNaUtility.ApproximatelyEqual(inst.position.z, instance.position.z));
}
RefreshTerrainTreeInstances(ref treeInstances, terrain);
}
}
/// <summary>
/// Refreshes the terrain tree instances sets from current list then returns the new tree instance list from the terrain
/// </summary>
/// <param name="currenTreeInstances"></param>
/// <param name="terrain"></param>
public void RefreshTerrainTreeInstances(ref List<TreeInstance> currenTreeInstances, Terrain terrain)
{
terrain.terrainData.treeInstances = currenTreeInstances.ToArray();
currenTreeInstances.Clear();
currenTreeInstances.AddRange(terrain.terrainData.treeInstances);
terrain.Flush();
}
/// <summary>
/// Creates a new tree instance
/// </summary>
/// <param name="source"></param>
/// <param name="terrain"></param>
/// <param name="prototypeIndex"></param>
/// <param name="storeInstance"></param>
/// <returns></returns>
public TreeInstance CreateNewTreeInstance(GameObject source, Terrain terrain, int prototypeIndex, bool storeInstance = true)
{
if (source != null)
{
// Create a new Tree Instance
Transform instanceTransform = source.transform;
Vector3 position = instanceTransform.position;
//Fix y position
float y = terrain.SampleHeight(position);
position.y = y;
Vector3 eulerAngles = instanceTransform.eulerAngles;
Vector3 globalScale = instanceTransform.lossyScale;
Vector3 normalizedLocalPos = GeNaMath.WorldToTerrainPosition(position, terrain);
TreeInstance treeInstance = new TreeInstance
{
prototypeIndex = prototypeIndex,
position = normalizedLocalPos,
widthScale = Mathf.Max(globalScale.x, globalScale.z),
heightScale = globalScale.y,
rotation = eulerAngles.y * Mathf.Deg2Rad,
};
if (storeInstance)
{
//Add stored instance
m_storedTreeInstanceData.Add(new TreeInstance
{
color = treeInstance.color,
heightScale = treeInstance.heightScale,
lightmapColor = treeInstance.lightmapColor,
position = treeInstance.position,
prototypeIndex = treeInstance.prototypeIndex,
rotation = treeInstance.rotation,
widthScale = treeInstance.widthScale
});
}
return treeInstance;
}
return new TreeInstance();
}
/// <summary>
/// Adds tree instance to terrain
/// </summary>
/// <param name="source"></param>
/// <param name="terrain"></param>
/// <param name="prototypeIndex"></param>
public void AddTreeInstanceToTerrain(GameObject source, Terrain terrain, int prototypeIndex)
{
terrain.AddTreeInstance(CreateNewTreeInstance(source, terrain, prototypeIndex));
}
/// <summary>
/// Is part of the terrain
/// </summary>
/// <param name="terrainData"></param>
/// <param name="prefab"></param>
/// <returns></returns>
public bool IsPartOfTerrain(TerrainData terrainData, GameObject prefab)
{
TreePrototype[] prototypes = terrainData.treePrototypes;
foreach (var prototype in prototypes)
{
if (prototype.prefab == prefab)
return true;
}
return false;
}
/// <summary>
/// Add prefab to the terrain
/// </summary>
/// <param name="terrainData"></param>
/// <param name="prefab"></param>
public void AddPrefabToTerrain(TerrainData terrainData, GameObject prefab)
{
var prototypes = new List<TreePrototype>(terrainData.treePrototypes)
{
new TreePrototype
{
prefab = prefab
}
};
terrainData.treePrototypes = prototypes.ToArray();
}
/// <summary>
/// Sets the conversion type
/// </summary>
/// <param name="type"></param>
public void SetConversionType(ConversionType type)
{
m_conversionType = type;
}
/// <summary>
/// Converts back to gameobjects
/// </summary>
/// <returns></returns>
public Terrain ConvertBackToGameObjects()
{
CleanCachedData(false);
// Check if the Parent is part of a Terrain
Terrain terrain = gameObject.GetComponentInParent<Terrain>();
if (terrain == null)
{
GeNaDebug.LogWarning("You can only convert GameObjects to Trees if they are children of a Terrain!");
return null;
}
//Check sync settings
SyncTreesWithTerrainTreeInstances(terrain);
//Removes the instances on the terrain
RemoveTreeInstances(m_storedTreeInstanceData, terrain);
switch (m_conversionDisableMode)
{
case ConversionDisableMode.Disable:
{
for (int i = 0; i < m_storedGameObjects.Count; i++)
{
m_storedGameObjects[i].SetActive(true);
}
break;
}
case ConversionDisableMode.DestroyAndInstantiate:
{
InstantiatePrefabs(m_instasiateData, gameObject);
break;
}
}
SetConversionType(ConversionType.GameObject);
m_storedTreeInstanceData.Clear();
m_instasiateData.Clear();
return terrain;
}
/// <summary>
/// Checks trees synced with tree instances on the terrain to see if the instance still exists
/// </summary>
/// <param name="terrain"></param>
public void SyncTreesWithTerrainTreeInstances(Terrain terrain)
{
if (m_syncTreeInstancesToGameObjects && terrain != null)
{
List<GameObject> sortGameObjects = new List<GameObject>();
List<TreeInstasiateData> sortTreeInstasiateDatas = new List<TreeInstasiateData>();
TreeInstance[] instances = terrain.terrainData.treeInstances;
if (instances.Length > 0)
{
for (int j = m_storedTreeInstanceData.Count; j-- > 0;)
{
TreeInstance storedInstance = m_storedTreeInstanceData[j];
for (int i = 0; i < instances.Length; i++)
{
if (!GeNaUtility.ApproximatelyEqual(instances[i].position.x, storedInstance.position.x) &&
!GeNaUtility.ApproximatelyEqual(instances[i].position.z, storedInstance.position.z))
{
sortGameObjects.Add(m_storedGameObjects[j]);
sortTreeInstasiateDatas.Add(m_instasiateData[j]);
}
}
}
foreach (GameObject storedGameObject in sortGameObjects)
{
m_storedGameObjects.Remove(storedGameObject);
}
foreach (TreeInstasiateData data in sortTreeInstasiateDatas)
{
m_instasiateData.Remove(data);
}
}
}
}
/// <summary>
/// Instantiates all the prefabs
/// </summary>
/// <param name="treeData"></param>
/// <param name="parent"></param>
private void InstantiatePrefabs(List<TreeInstasiateData> treeData, GameObject parent)
{
for (int i = 0; i < treeData.Count; i++)
{
TreeInstasiateData data = treeData[i];
if (ValidateTreeInstantiateData(data))
{
SetObjectTransform(data, (GameObject) GeNaEvents.Instantiate(data.m_prefab), parent);
}
}
}
/// <summary>
/// Sets up the object transform and parent
/// </summary>
/// <param name="data"></param>
/// <param name="prefab"></param>
/// <param name="parent"></param>
private void SetObjectTransform(TreeInstasiateData data, GameObject prefab, GameObject parent)
{
if (data != null)
{
prefab.transform.position = data.m_position;
prefab.transform.eulerAngles = data.m_rotation;
prefab.transform.localScale = data.m_scale;
if (parent != null)
{
prefab.transform.SetParent(parent.transform);
}
}
}
/// <summary>
/// Validates the tree data see if there is a prefab to Instantiate from
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private bool ValidateTreeInstantiateData(TreeInstasiateData data)
{
if (data != null)
{
if (data.m_prefab != null)
{
return true;
}
}
return false;
}
#endregion
}
}