ThomagicRenderer/Runtime/GrassRenderer.cs

197 lines
7.1 KiB
C#

using System.Collections.Generic;
using UnityEngine;
namespace Assets.ThoMagic.Renderer
{
public class GrassRenderer : GrassStreamer
{
private readonly Terrain terrain;
private readonly TerrainData terrainData;
private bool isDirty;
public GrassRenderer(Terrain terrain)
: base(terrain)
{
this.terrain = terrain;
terrainData = terrain.terrainData;
}
public void OnTerrainChanged(TerrainChangedFlags flags)
{
if (flags.HasFlag(TerrainChangedFlags.DelayedHeightmapUpdate))
isDirty = true;
else if (flags.HasFlag(TerrainChangedFlags.Heightmap))
isDirty = true;
else if (flags.HasFlag(TerrainChangedFlags.HeightmapResolution))
isDirty = true;
else if (flags.HasFlag(TerrainChangedFlags.Holes))
isDirty = true;
else if (flags.HasFlag(TerrainChangedFlags.DelayedHolesUpdate))
isDirty = true;
else if (flags.HasFlag(TerrainChangedFlags.FlushEverythingImmediately))
isDirty = true;
}
public void Destroy()
{
Recycle();
}
public void LateUpdate()
{
if (isDirty)
{
isDirty = false;
Load();
}
if (Application.isEditor && !Application.isPlaying)
RebuildChangedPrototypes();
Update();
}
public void Load()
{
DetailPrototype[] detailPrototypes = terrainData.detailPrototypes;
Recycle();
int ownerHash = GetHashCode();
if (grassPrefabs == null)
{
grassPrefabs = new List<int>();
grassPrefabSet = new List<bool>();
}
float maxDetailDistance = float.MinValue;
for (int index = 0; index < detailPrototypes.Length; ++index)
{
if (!detailPrototypes[index].usePrototypeMesh || detailPrototypes[index].prototype == null)
{
AddDummy(index);
}
else
{
GameObject prototypeToRender = RendererUtility.GetPrototypeToRender(detailPrototypes[index]);
if (!RendererUtility.SupportsProceduralInstancing(prototypeToRender))
{
AddDummy(index);
}
else
{
var settings = GetSettingsOrDefault(prototypeToRender);
maxDetailDistance = Mathf.Max(maxDetailDistance, settings.Settings.RenderDistance);
grassPrefabs.Add(RendererPool.RegisterObject(prototypeToRender, settings, this, ownerHash));
grassPrefabSet.Add(true);
}
}
}
Build(maxDetailDistance, detailPrototypes.Length);
}
private void AddDummy(int i)
{
grassPrefabs.Add(-1);
grassPrefabSet.Add(false);
}
public override void Recycle()
{
if (grassPrefabs == null)
return;
base.Recycle();
int ownerHash = GetHashCode();
for (int index = 0; index < grassPrefabs.Count; ++index)
{
if (grassPrefabSet[index])
{
RendererPool.RemoveObject(grassPrefabs[index], ownerHash);
}
}
grassPrefabs.Clear();
grassPrefabSet.Clear();
}
private void RebuildChangedPrototypes()
{
DetailPrototype[] detailPrototypes = terrainData.detailPrototypes;
if (detailPrototypes.Length != grassPrefabs.Count)
{
Load();
}
else
{
int ownerHash = GetHashCode();
for (int index = 0; index < grassPrefabs.Count; ++index)
{
if (grassPrefabSet[index])
{
GameObject prototypeToRender = RendererUtility.GetPrototypeToRender(detailPrototypes[index]);
GameObject gameObject = RendererPool.GetObject(grassPrefabs[index]);
if (prototypeToRender != gameObject)
{
RendererPool.RemoveObject(grassPrefabs[index], ownerHash);
if (prototypeToRender != null)
{
grassPrefabs[index] = RendererPool.RegisterObject(prototypeToRender, GetSettingsOrDefault(prototypeToRender), this, ownerHash);
grassPrefabSet[index] = true;
}
else
{
grassPrefabs[index] = -1;
grassPrefabSet[index] = false;
}
}
else if (RendererPool.ContentHashChanged(grassPrefabs[index]))
{
RendererPool.RemoveObject(grassPrefabs[index], GetHashCode());
if (prototypeToRender != null)
{
grassPrefabs[index] = RendererPool.RegisterObject(prototypeToRender, GetSettingsOrDefault(prototypeToRender), this, ownerHash);
grassPrefabSet[index] = true;
}
else
{
grassPrefabs[index] = -1;
grassPrefabSet[index] = false;
}
}
else if (prototypeToRender != null)
RendererPool.SetObjectSettings(prototypeToRender, GetSettingsOrDefault(prototypeToRender));
}
}
}
}
private IInstanceRenderSettings GetSettingsOrDefault(GameObject gameObject)
{
IInstanceRenderSettings instanceRenderSettings;
return gameObject.TryGetComponent(out instanceRenderSettings) ? instanceRenderSettings : new DefaultRenderSettings();
}
private class DefaultRenderSettings : IInstanceRenderSettings
{
public InstanceRenderSettings Settings => new InstanceRenderSettings()
{
Render = true,
DensityInDistance = 0.125f,
DensityInDistanceFalloff = new Vector2(0.08f, 0.0075f),
RenderDistance = 150f,
ShadowDistance = 50f,
Shadows = true,
Supported = true
};
}
}
}