This commit is contained in:
Gerard Gascón 2025-02-02 23:44:44 +01:00
commit f5c1616018
679 changed files with 188502 additions and 0 deletions

View file

@ -0,0 +1,192 @@
using UnityEditor;
using UnityEngine;
using static System.Runtime.InteropServices.Marshal;
[RequireComponent(typeof(MeshSurfaceSampler))]
public class LeafInstancer : MonoBehaviour
{
[SerializeField] LayerMask _grassLayer;
[SerializeField] Mesh _leafMesh;
[SerializeField] Material _material;
MeshSurfaceSampler _meshSurfaceSampler;
RenderParams _renderParams;
GraphicsBuffer commandBuffer;
GraphicsBuffer.IndirectDrawIndexedArgs[] commandData;
LeafData[] leafData;
ComputeBuffer leafBuffer;
const int commandCount = 1;
struct LeafData
{
public Vector3 position;
public Vector3 normal;
}
void OnEnable()
{
Setup();
}
void OnDisable()
{
commandBuffer?.Release();
commandBuffer = null;
leafBuffer?.Release();
leafBuffer = null;
}
void Setup()
{
_meshSurfaceSampler = GetComponent<MeshSurfaceSampler>();
if (_meshSurfaceSampler == null)
Debug.LogError("MeshGenerator is null");
var meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer == null)
Debug.LogError("MeshRenderer is null");
if (_material == null)
Debug.LogError("Material is null");
if (_leafMesh == null)
Debug.LogError("Grass mesh is null");
}
public void GeneratePoints()
{
_meshSurfaceSampler.GeneratePoints();
}
public Bounds GetBoundingBox()
{
Bounds boundingBox = new Bounds();
if (_meshSurfaceSampler.points != null && _meshSurfaceSampler.points.Length > 0)
{
Vector3 minPoint = _meshSurfaceSampler.points[0];
Vector3 maxPoint = _meshSurfaceSampler.points[0];
for (int i = 1; i < _meshSurfaceSampler.points.Length; i++)
{
minPoint = Vector3.Min(minPoint, _meshSurfaceSampler.points[i]);
maxPoint = Vector3.Max(maxPoint, _meshSurfaceSampler.points[i]);
}
boundingBox.SetMinMax(minPoint, maxPoint);
}
var extrude = _material.GetFloat("_Extrude");
boundingBox.Expand(boundingBox.size * extrude);
return boundingBox;
}
public void Generate()
{
var count = _meshSurfaceSampler.points.Length;
// var count = 1;
leafData = new LeafData[count];
leafBuffer?.Release();
leafBuffer = new ComputeBuffer(count, SizeOf<LeafData>());
commandBuffer?.Release();
commandBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, commandCount, GraphicsBuffer.IndirectDrawIndexedArgs.size);
commandData = new GraphicsBuffer.IndirectDrawIndexedArgs[commandCount];
for (int i = 0; i < count; i++)
{
leafData[i] = new LeafData
{
position = _meshSurfaceSampler.points[i],
normal = _meshSurfaceSampler.normals[i]
};
}
// Set the buffers
var indirectDrawIndexedArgs = new GraphicsBuffer.IndirectDrawIndexedArgs
{
indexCountPerInstance = _leafMesh.GetIndexCount(0),
instanceCount = (uint)count,
startIndex = _leafMesh.GetIndexStart(0),
baseVertexIndex = _leafMesh.GetBaseVertex(0),
startInstance = 0,
};
for (int i = 0; i < commandCount; i++)
commandData[i] = indirectDrawIndexedArgs;
commandBuffer.SetData(commandData);
leafBuffer.SetData(leafData);
// Set the render params
var block = new MaterialPropertyBlock();
block.SetBuffer("_LeafData", leafBuffer);
_renderParams = new RenderParams(_material)
{
layer = (int)Mathf.Log(_grassLayer.value, 2),
worldBounds = new Bounds(Vector3.zero, 10000 * Vector3.one),
matProps = block,
receiveShadows = true,
};
UpdateRotation();
}
// Update the rotation of the grass
void UpdateRotation()
{
var target = Camera.main.transform.position;
var rotation = Quaternion.LookRotation(transform.position - target, Vector3.up);
var quaternion = new Vector4(rotation.x, rotation.y, rotation.z, rotation.w);
_renderParams.matProps.SetVector("_Rotation", quaternion);
}
void Update()
{
// Validation checks
if (_meshSurfaceSampler == null)
Setup();
if (!_meshSurfaceSampler.ArePointsGenerated())
_meshSurfaceSampler.GeneratePoints();
// if (transform.lossyScale != Vector3.one)
// Debug.LogWarning("GrassInstancer does not support scaling");
if (leafData == null || leafData.Length == 0)
Generate();
UpdateRotation();
// Frustum culling check for the chunk
var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
if (!GeometryUtility.TestPlanesAABB(frustumPlanes, GetBoundingBox()))
return;
// Render the grass
Graphics.RenderMeshIndirect(_renderParams, _leafMesh, commandBuffer, commandCount);
}
}
#if UNITY_EDITOR
// Custom editor which adds a button to generate the terrain
[CustomEditor(typeof(LeafInstancer))]
public class LeafInstancerEditor : Editor
{
public override void OnInspectorGUI()
{
var script = (LeafInstancer)target;
DrawDefaultInspector();
GUILayout.Space(10);
if (GUILayout.Button("Generate Leaves"))
{
script.GeneratePoints();
script.Generate();
}
}
}
#endif