Files
ZHGD_Web/Assets/Art/Art Plugins/GPUInstancer/Scripts/GPUInstancerTreeManager.cs
2025-07-13 23:16:20 +08:00

289 lines
12 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace GPUInstancer
{
/// <summary>
/// Add this to a Unity terrain for GPU Instancing terrain trees at runtime.
/// </summary>
[ExecuteInEditMode]
public class GPUInstancerTreeManager : GPUInstancerTerrainManager
{
private static ComputeShader _treeInstantiationComputeShader;
public bool initializeWithCoroutine = true;
private bool _isCoroutineActive;
#region MonoBehaviour Methods
public override void Awake()
{
base.Awake();
if (_treeInstantiationComputeShader == null)
_treeInstantiationComputeShader = Resources.Load<ComputeShader>(GPUInstancerConstants.TREE_INSTANTIATION_RESOURCE_PATH);
}
public override void Update()
{
base.Update();
if (Application.isPlaying && _requiresTerrainUpdate && !_isCoroutineActive)
{
StartCoroutine(ReplaceUnityTrees());
_requiresTerrainUpdate = false;
}
}
#endregion MonoBehaviour Methods
#region Override Methods
public override void ClearInstancingData()
{
base.ClearInstancingData();
if (_terrains != null)
{
foreach (Terrain terrain in _terrains)
{
if (terrain != null && terrain.treeDistance == 0)
terrain.treeDistance = terrainSettings.maxTreeDistance;
}
}
}
public override void GeneratePrototypes(bool forceNew = false)
{
base.GeneratePrototypes(forceNew);
if (terrainSettings != null && terrain != null && terrain.terrainData != null)
{
GPUInstancerUtility.SetTreeInstancePrototypes(gameObject, prototypeList, terrain.terrainData.treePrototypes, terrainSettings, forceNew);
}
}
#if UNITY_EDITOR
public override void CheckPrototypeChanges()
{
base.CheckPrototypeChanges();
if (!Application.isPlaying && terrainSettings != null && terrain != null && terrain.terrainData != null)
{
if (prototypeList.Count != terrain.terrainData.treePrototypes.Length)
{
GeneratePrototypes();
}
int index = 0;
foreach (GPUInstancerTreePrototype prototype in prototypeList)
{
prototype.prototypeIndex = index;
index++;
}
}
}
#endif
public override void InitializeRuntimeDataAndBuffers(bool forceNew = true)
{
base.InitializeRuntimeDataAndBuffers(forceNew);
if (!forceNew && isInitialized)
return;
if (terrainSettings == null)
return;
if (prototypeList != null && prototypeList.Count > 0)
{
GPUInstancerUtility.AddTreeInstanceRuntimeDataToList(runtimeDataList, prototypeList, terrainSettings);
}
StartCoroutine(ReplaceUnityTrees());
isInitialized = true;
}
public override void DeletePrototype(GPUInstancerPrototype prototype, bool removeSO = true)
{
if (terrainSettings != null && terrain != null && terrain.terrainData != null)
{
int treePrototypeIndex = prototypeList.IndexOf(prototype);
TreePrototype[] treePrototypes = terrain.terrainData.treePrototypes;
List<TreePrototype> newTreePrototypes = new List<TreePrototype>(treePrototypes);
List<TreeInstance> newTreeInstanceList = new List<TreeInstance>();
TreeInstance treeInstance;
for (int i = 0; i < terrain.terrainData.treeInstances.Length; i++)
{
treeInstance = terrain.terrainData.treeInstances[i];
if (treeInstance.prototypeIndex < treePrototypeIndex)
{
newTreeInstanceList.Add(treeInstance);
}
else if (treeInstance.prototypeIndex > treePrototypeIndex)
{
treeInstance.prototypeIndex = treeInstance.prototypeIndex - 1;
newTreeInstanceList.Add(treeInstance);
}
}
if (newTreePrototypes.Count > treePrototypeIndex)
newTreePrototypes.RemoveAt(treePrototypeIndex);
terrain.terrainData.treeInstances = newTreeInstanceList.ToArray();
terrain.terrainData.treePrototypes = newTreePrototypes.ToArray();
terrain.terrainData.RefreshPrototypes();
if (removeSO)
base.DeletePrototype(prototype, removeSO);
GeneratePrototypes(false);
if (!removeSO)
base.DeletePrototype(prototype, removeSO);
}
else
base.DeletePrototype(prototype, removeSO);
}
#endregion Override Methods
public IEnumerator ReplaceUnityTrees()
{
_isCoroutineActive = true;
if (prototypeList.Count > 0)
{
Vector4[] treeScales = new Vector4[prototypeList.Count];
int count = 0;
foreach (GPUInstancerTreePrototype tp in prototypeList)
{
treeScales[count] = tp.isApplyPrefabScale ? tp.prefabObject.transform.localScale : Vector3.one;
count++;
}
int[] instanceCounts = new int[prototypeList.Count];
List<Vector4> treeDataList = new List<Vector4>(); // prototypeIndex - positionx3 - rotation - scalex2
int instanceTotal = 0;
foreach (Terrain terrain in _terrains)
{
if (terrain == null)
continue;
if (terrain.terrainData.treePrototypes.Length > prototypeList.Count)
{
Debug.LogError("Additional Terrain has more Tree prototypes than defined prototypes on the Tree Manager. Tree Manager requires every Terrain to have the same Tree prototypes defined.", terrain);
continue;
}
terrain.treeDistance = 0f; // will not persist if called at runtime.
Vector3 terrainSize = terrain.terrainData.size;
Vector3 terrainPosition = terrain.GetPosition();
TreeInstance[] treeInstances = terrain.terrainData.treeInstances;
instanceTotal += treeInstances.Length;
foreach (TreeInstance treeInstance in treeInstances)
{
treeDataList.Add(new Vector4(
treeInstance.prototypeIndex,
treeInstance.position.x * terrainSize.x + terrainPosition.x,
treeInstance.position.y * terrainSize.y + terrainPosition.y,
treeInstance.position.z * terrainSize.z + terrainPosition.z
));
treeDataList.Add(new Vector4(
treeInstance.rotation,
treeInstance.widthScale,
treeInstance.heightScale,
0
));
instanceCounts[treeInstance.prototypeIndex]++;
}
}
if (instanceTotal > 0)
{
if (initializeWithCoroutine && !isInitialized)
yield return null;
ComputeBuffer treeDataBuffer = new ComputeBuffer(treeDataList.Count, GPUInstancerConstants.STRIDE_SIZE_FLOAT4);
#if UNITY_2019_1_OR_NEWER
treeDataBuffer.SetData(treeDataList);
#else
treeDataBuffer.SetData(treeDataList.ToArray());
#endif
ComputeBuffer treeScalesBuffer = new ComputeBuffer(treeScales.Length, GPUInstancerConstants.STRIDE_SIZE_FLOAT4);
treeScalesBuffer.SetData(treeScales);
ComputeBuffer counterBuffer = new ComputeBuffer(1, GPUInstancerConstants.STRIDE_SIZE_INT);
uint[] emptyCounterData = new uint[1];
treeDataList = null;
treeScales = null;
GPUInstancerRuntimeData runtimeData;
for (int i = 0; i < runtimeDataList.Count; i++)
{
runtimeData = runtimeDataList[i];
int instanceCount = instanceCounts[i];
runtimeData.bufferSize = instanceCount;
runtimeData.instanceCount = instanceCount;
if (instanceCount == 0)
{
GPUInstancerUtility.ReleaseInstanceBuffers(runtimeData);
continue;
}
counterBuffer.SetData(emptyCounterData);
if (runtimeData.transformationMatrixVisibilityBuffer != null)
runtimeData.transformationMatrixVisibilityBuffer.Release();
runtimeData.transformationMatrixVisibilityBuffer = new ComputeBuffer(instanceCount, GPUInstancerConstants.STRIDE_SIZE_MATRIX4X4);
_treeInstantiationComputeShader.SetBuffer(0,
GPUInstancerConstants.VisibilityKernelPoperties.INSTANCE_DATA_BUFFER, runtimeData.transformationMatrixVisibilityBuffer);
_treeInstantiationComputeShader.SetBuffer(0,
GPUInstancerConstants.TreeKernelProperties.TREE_DATA, treeDataBuffer);
_treeInstantiationComputeShader.SetBuffer(0,
GPUInstancerConstants.TreeKernelProperties.TREE_SCALES, treeScalesBuffer);
_treeInstantiationComputeShader.SetBuffer(0,
GPUInstancerConstants.GrassKernelProperties.COUNTER_BUFFER, counterBuffer);
_treeInstantiationComputeShader.SetInt(
GPUInstancerConstants.VisibilityKernelPoperties.BUFFER_PARAMETER_BUFFER_SIZE, instanceTotal);
//_treeInstantiationComputeShader.SetVector(
// GPUInstancerConstants.GrassKernelProperties.TERRAIN_SIZE_DATA, terrain.terrainData.size);
//_treeInstantiationComputeShader.SetVector(
// GPUInstancerConstants.TreeKernelProperties.TERRAIN_POSITION, terrain.GetPosition());
_treeInstantiationComputeShader.SetBool(
GPUInstancerConstants.TreeKernelProperties.IS_APPLY_ROTATION, ((GPUInstancerTreePrototype)runtimeData.prototype).isApplyRotation);
_treeInstantiationComputeShader.SetBool(
GPUInstancerConstants.TreeKernelProperties.IS_APPLY_TERRAIN_HEIGHT, ((GPUInstancerTreePrototype)runtimeData.prototype).isApplyTerrainHeight);
_treeInstantiationComputeShader.SetInt(
GPUInstancerConstants.TreeKernelProperties.PROTOTYPE_INDEX, i);
_treeInstantiationComputeShader.Dispatch(0,
Mathf.CeilToInt(instanceTotal / GPUInstancerConstants.COMPUTE_SHADER_THREAD_COUNT), 1, 1);
GPUInstancerUtility.InitializeGPUBuffer(runtimeData);
if (initializeWithCoroutine && !isInitialized)
yield return null;
}
treeDataBuffer.Release();
treeScalesBuffer.Release();
counterBuffer.Release();
}
else
{
GPUInstancerUtility.ReleaseInstanceBuffers(runtimeDataList);
}
}
isInitial = true;
if (!isInitialized)
GPUInstancerUtility.TriggerEvent(GPUInstancerEventType.TreeInitializationFinished);
_isCoroutineActive = false;
}
}
}