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,284 @@
using UnityEditor;
using UnityEngine;
using static System.Runtime.InteropServices.Marshal;
[RequireComponent(typeof(MeshGenerator))]
public class GrassInstancer : MonoBehaviour
{
[SerializeField] LayerMask _grassLayer;
[SerializeField] Mesh _grassMesh;
[SerializeField] Material _material;
[SerializeField] float _density = 10;
[SerializeField] int _seed = 42;
MeshGenerator _meshGenerator;
RenderParams _renderParams;
GraphicsBuffer.IndirectDrawIndexedArgs[] commandData;
struct GrassData
{
public Vector3 position;
public Vector2 colorTexUV;
}
struct GrassChunk
{
public Vector2Int sampleCount;
public GraphicsBuffer commandBuffer;
public ComputeBuffer grassBuffer;
public Bounds bounds;
public Material material;
public Vector2 uvMin;
public Vector2 uvMax;
}
GrassChunk[] grassChunks;
[SerializeField] int chunkCount = 1;
void OnEnable()
{
Setup();
}
void OnDisable()
{
FreeChunks();
}
void Setup()
{
_meshGenerator = GetComponent<MeshGenerator>();
if (_meshGenerator == 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 (_grassMesh == null)
Debug.LogError("Grass mesh is null");
}
// Initialize the chunks
public void InitChunks()
{
// Ensure the chunks are freed
FreeChunks();
// Create the chunks
grassChunks = new GrassChunk[chunkCount * chunkCount];
for (int y = 0; y < chunkCount; y++)
{
for (int x = 0; x < chunkCount; x++)
{
var chunkIndex = y * chunkCount + x;
var bounds = GetChunkBounds(x, y);
var uvMin = new Vector2(x, y) / chunkCount;
var uvMax = uvMin + Vector2.one / chunkCount;
var size = new Vector2(bounds.size.x, bounds.size.z);
var sampleCount = new Vector2Int((int)(_density * size.x), (int)(_density * size.y));
grassChunks[chunkIndex] = new GrassChunk
{
commandBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, GraphicsBuffer.IndirectDrawIndexedArgs.size),
grassBuffer = new ComputeBuffer(sampleCount.x * sampleCount.y, SizeOf<GrassData>()),
bounds = bounds,
sampleCount = sampleCount,
material = _material,
uvMin = uvMin,
uvMax = uvMax,
};
}
}
}
// Setup a chunk
void SetupChunk(GrassChunk chunk)
{
if (chunk.sampleCount.x <= 0 || chunk.sampleCount.y <= 0)
return;
// Initialize the matrices
var grassData = new GrassData[chunk.sampleCount.x * chunk.sampleCount.y];
var meshSize = new Vector2(_meshGenerator.size.x, _meshGenerator.size.z);
// Loop through each grass
var index = 0;
for (int y = 0; y < chunk.sampleCount.y; y++)
{
for (int x = 0; x < chunk.sampleCount.x; x++)
{
// Calculate the position and uvs with random offset
var randomOffset = Random.insideUnitCircle / 2;
var chunkUV = (new Vector2(x, y) + Vector2.one / 2 + randomOffset) / chunk.sampleCount;
var worldUV = new Vector2(
Remap(chunkUV.x, 0, 1, chunk.uvMin.x, chunk.uvMax.x),
Remap(chunkUV.y, 0, 1, chunk.uvMin.y, chunk.uvMax.y)
);
var position2D = (worldUV - Vector2.one / 2) * meshSize;
var height = _meshGenerator.GetMeshHeight(worldUV);
var position = transform.TransformPoint(new Vector3(position2D.x, height, position2D.y));
// Set the grass data
grassData[index] = new GrassData
{
position = position,
colorTexUV = worldUV
};
// Increment the index
index++;
}
}
chunk.grassBuffer.SetData(grassData);
commandData = new GraphicsBuffer.IndirectDrawIndexedArgs[1];
commandData[0] = new GraphicsBuffer.IndirectDrawIndexedArgs
{
indexCountPerInstance = _grassMesh.GetIndexCount(0),
instanceCount = (uint)(chunk.sampleCount.x * chunk.sampleCount.y),
startIndex = _grassMesh.GetIndexStart(0),
baseVertexIndex = _grassMesh.GetBaseVertex(0),
startInstance = 0,
};
chunk.commandBuffer.SetData(commandData);
}
// Free the chunks
void FreeChunks()
{
if (grassChunks == null)
return;
foreach (var chunk in grassChunks)
{
chunk.commandBuffer?.Release();
chunk.grassBuffer?.Release();
}
grassChunks = null;
}
void OnDrawGizmos()
{
if (grassChunks == null)
return;
foreach (var chunk in grassChunks)
{
Gizmos.color = Color.red;
Gizmos.DrawWireCube(chunk.bounds.center, chunk.bounds.size);
}
}
// Get the bounds of a chunk
Bounds GetChunkBounds(int x, int y)
{
var size = new Vector2(_meshGenerator.size.x, _meshGenerator.size.z);
var chunkSize = size / chunkCount;
var chunkPosition = new Vector2(x, y) * chunkSize - size / 2;
var chunkBounds = new Bounds();
var min = new Vector3(chunkPosition.x, 0, chunkPosition.y);
var max = new Vector3(chunkPosition.x + chunkSize.x, _meshGenerator.size.y, chunkPosition.y + chunkSize.y);
chunkBounds.SetMinMax(
transform.TransformPoint(min),
transform.TransformPoint(max)
);
return chunkBounds;
}
// Remap a value from one range to another
float Remap(float value, float low1, float high1, float low2, float high2)
{
return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
}
public void Generate()
{
// Set the seed
Random.InitState(_seed);
// Set the constant render params
var block = new MaterialPropertyBlock();
block.SetTexture("_ColorTex", _meshGenerator.colorTexture);
block.SetFloat("_MeshHeight", _grassMesh.bounds.size.y);
_renderParams = new RenderParams(_material)
{
layer = (int)Mathf.Log(_grassLayer.value, 2),
worldBounds = new Bounds(Vector3.zero, 10000 * Vector3.one),
matProps = block,
receiveShadows = true,
};
// Generate the chunks
InitChunks();
foreach (var chunk in grassChunks)
SetupChunk(chunk);
}
// 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 RenderChunks()
{
// Update the rotation
UpdateRotation();
// Render the chunks
foreach (var chunk in grassChunks)
{
// Frustum culling check for the chunk
var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
if (!GeometryUtility.TestPlanesAABB(frustumPlanes, chunk.bounds))
continue;
// Render the chunk
_renderParams.matProps.SetBuffer("_GrassData", chunk.grassBuffer);
Graphics.RenderMeshIndirect(_renderParams, _grassMesh, chunk.commandBuffer);
}
}
void Update()
{
// Validation checks
if (_meshGenerator == null)
Setup();
if (!_meshGenerator.IsMeshGenerated())
_meshGenerator.GenerateTerrainMesh();
if (transform.lossyScale != Vector3.one)
Debug.LogWarning("GrassInstancer does not support scaling");
if (grassChunks == null || grassChunks.Length == 0)
Generate();
// Render the chunks
RenderChunks();
}
}
#if UNITY_EDITOR
// Custom editor which adds a button to generate the terrain
[CustomEditor(typeof(GrassInstancer))]
public class GrassInstancerEditor : Editor
{
public override void OnInspectorGUI()
{
var script = (GrassInstancer)target;
DrawDefaultInspector();
GUILayout.Space(10);
// GUILayout.Label("Sample Count: " + script.GetSampleCount());
if (GUILayout.Button("Generate Grass"))
script.Generate();
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f0b6253ae2084750ad5801aa8fd8c18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,51 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IsometricCameraPivot : MonoBehaviour
{
float _targetAngleY;
float _currentAngleY;
float _targetAngleX;
float _currentAngleX;
[SerializeField] float _mouseSensitivity = 2;
[SerializeField] float _rotationSpeed = 5;
void Awake()
{
_targetAngleY = transform.eulerAngles.y;
_targetAngleX = transform.eulerAngles.x;
}
void Update()
{
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
if (Input.GetMouseButton(0))
{
_targetAngleY += mouseX * _mouseSensitivity;
_targetAngleX -= mouseY * _mouseSensitivity / 3;
}
else
{
_targetAngleY = Mathf.Round(_targetAngleY / 45) * 45;
_targetAngleX = Mathf.Round(_targetAngleX / 15) * 15;
}
_targetAngleY = (_targetAngleY + 360) % 360;
_targetAngleX = Mathf.Clamp(_targetAngleX, 23, 67);
_currentAngleY = Mathf.LerpAngle(transform.eulerAngles.y, _targetAngleY, Time.deltaTime * _rotationSpeed);
_currentAngleX = Mathf.LerpAngle(transform.eulerAngles.x, _targetAngleX, Time.deltaTime * _rotationSpeed);
if (Mathf.Abs(_currentAngleY - _targetAngleY) < 0.1f) _currentAngleY = _targetAngleY;
if (Mathf.Abs(_currentAngleX - _targetAngleX) < 0.1f) _currentAngleX = _targetAngleX;
transform.rotation = Quaternion.Euler(_currentAngleX, _currentAngleY, 0);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0717b4fb631d34d44a07d1b42a6b73ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 271baf40419394e30b857a667e336d88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,185 @@
using UnityEngine;
using UnityEditor;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class MeshGenerator : MonoBehaviour
{
public Texture2D heightMap;
public Vector3 size = new Vector3(10, 3, 10);
public Vector2Int sampleCount = new Vector2Int(25, 25);
public LayerMask terrainLayer;
[HideInInspector] public MeshData meshData;
[HideInInspector] public Mesh mesh;
[HideInInspector] public RenderTexture colorTexture;
void OnEnable()
{
var colorCameraObject = new GameObject("Color Camera");
colorCameraObject.transform.parent = transform;
colorCameraObject.transform.localPosition = Vector3.zero;
var offset = new Vector3(0, size.y, 0);
var rotation = Quaternion.Euler(90, 0, 0);
colorCameraObject.transform.SetLocalPositionAndRotation(offset, rotation);
var colorCamera = colorCameraObject.AddComponent<Camera>();
colorCamera.orthographic = true;
colorCamera.orthographicSize = size.z / 2;
colorCamera.aspect = size.x / size.z;
colorCamera.nearClipPlane = 0;
colorCamera.clearFlags = CameraClearFlags.Nothing;
colorCamera.depthTextureMode = DepthTextureMode.None;
colorCamera.cullingMask = 1 << (int)Mathf.Log(terrainLayer.value, 2);
colorTexture = new RenderTexture(1024, 1024, 0, RenderTextureFormat.ARGB32);
colorCamera.targetTexture = colorTexture;
}
public void GenerateTerrainMesh()
{
// Check if the height map is null
if (heightMap == null)
{
Debug.LogError("Height map is null");
return;
}
// Initialize the mesh data
meshData = new MeshData(sampleCount.x, sampleCount.y);
// Loop through each vertex
var index = 0;
for (var y = 0; y < sampleCount.y; y++)
{
for (var x = 0; x < sampleCount.x; x++)
{
// Calculate the uv, vertex position
var uv = new Vector2(x, y) / (sampleCount - Vector2.one);
var vertex = (uv - Vector2.one / 2) * new Vector2(size.x, size.z);
var heightValue = GetTrueHeight(uv);
// Set the vertex position and uv
meshData.vertices[index] = new Vector3(vertex.x, heightValue, vertex.y);
meshData.uvs[index] = uv;
// Add triangles if not at the edge
if (x < sampleCount.x - 1 && y < sampleCount.y - 1)
{
meshData.AddTriangle(index, index + sampleCount.x + 1, index + sampleCount.x);
meshData.AddTriangle(index + sampleCount.x + 1, index, index + 1);
}
// Increment the index
index++;
}
}
// Create the mesh
mesh = meshData.CreateMesh();
var meshFilter = GetComponent<MeshFilter>();
if (meshFilter != null)
meshFilter.sharedMesh = mesh;
else
Debug.LogError("Mesh filter is null");
var meshCollider = GetComponent<MeshCollider>();
if (meshCollider != null)
meshCollider.sharedMesh = mesh;
}
// Get the height of the terrain at the given uv from the mesh
public float GetMeshHeight(Vector2 uv)
{
// Calculate the precise indices
float xIndexPrecise = uv.x * (sampleCount.x - 1);
float zIndexPrecise = uv.y * (sampleCount.y - 1);
// Calculate the indices of the vertices around the point
var xIndex = Mathf.FloorToInt(uv.x * (sampleCount.x - 1));
var zIndex = Mathf.FloorToInt(uv.y * (sampleCount.y - 1));
// Ensure indices are within the bounds of the mesh
xIndex = Mathf.Clamp(xIndex, 0, sampleCount.x - 2);
zIndex = Mathf.Clamp(zIndex, 0, sampleCount.y - 2);
// Get the four vertices of the square (assuming row major and clockwise order)
var v1 = meshData.vertices[zIndex * sampleCount.x + xIndex];
var v2 = meshData.vertices[zIndex * sampleCount.x + xIndex + 1];
var v3 = meshData.vertices[(zIndex + 1) * sampleCount.x + xIndex];
var v4 = meshData.vertices[(zIndex + 1) * sampleCount.x + xIndex + 1];
// Bilinear interpolation
var xRem = xIndexPrecise - xIndex;
var zRem = zIndexPrecise - zIndex;
var height1 = Mathf.Lerp(v1.y, v2.y, xRem);
var height2 = Mathf.Lerp(v3.y, v4.y, xRem);
return Mathf.Lerp(height1, height2, zRem);
}
// Get the true height of the terrain at the given uv from the height map
public float GetTrueHeight(Vector2 uv)
{
var x = Mathf.RoundToInt(uv.x * heightMap.width);
var y = Mathf.RoundToInt(uv.y * heightMap.height);
return heightMap.GetPixel(x, y).grayscale * size.y; ;
}
// Check if the mesh and mesh data are generated
public bool IsMeshGenerated()
{
return mesh != null && meshData != null;
}
}
// MeshData class which allows for easy mesh creation
public class MeshData
{
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
int triangleIndex;
public MeshData(int meshWidth, int meshHeight)
{
vertices = new Vector3[meshWidth * meshHeight];
uvs = new Vector2[meshWidth * meshHeight];
triangles = new int[(meshWidth - 1) * (meshHeight - 1) * 6];
}
public void AddTriangle(int a, int b, int c)
{
triangles[triangleIndex] = a;
triangles[triangleIndex + 1] = c;
triangles[triangleIndex + 2] = b;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
var mesh = new Mesh
{
vertices = vertices,
triangles = triangles,
uv = uvs
};
mesh.RecalculateNormals();
return mesh;
}
}
#if UNITY_EDITOR
// Custom editor which adds a button to generate the terrain
[CustomEditor(typeof(MeshGenerator))]
public class MeshGeneratorEditor : Editor
{
public override void OnInspectorGUI()
{
var script = (MeshGenerator)target;
DrawDefaultInspector();
GUILayout.Space(10);
if (GUILayout.Button("Generate Terrain"))
script.GenerateTerrainMesh();
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff993955589c54d1f980c40c04421832
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,80 @@
using System.Collections.Generic;
using UnityEngine;
public class MeshSurfaceSampler : MonoBehaviour
{
[SerializeField] float _density = 1;
[SerializeField] int seed = 42;
Mesh _mesh;
[HideInInspector] public Vector3[] points;
[HideInInspector] public Vector3[] normals;
void OnEnable()
{
_mesh = GetComponent<MeshFilter>().mesh;
GeneratePoints();
}
public void GeneratePoints()
{
Random.InitState(seed);
var tris = _mesh.triangles;
var triangleAreas = new float[tris.Length / 3];
var totalArea = 0.0f;
for (var i = 0; i < tris.Length; i += 3)
{
var v1 = _mesh.vertices[tris[i]];
var v2 = _mesh.vertices[tris[i + 1]];
var v3 = _mesh.vertices[tris[i + 2]];
var area = TriangleArea(v1, v2, v3);
totalArea += area;
triangleAreas[i / 3] = area;
}
var pointList = new List<Vector3>();
var normalList = new List<Vector3>();
var totalPoints = Mathf.CeilToInt(totalArea * _density);
for (var i = 0; i < triangleAreas.Length; i++)
{
var pointsInThisTriangle = Mathf.CeilToInt(triangleAreas[i] / totalArea * totalPoints);
for (var j = 0; j < pointsInThisTriangle; j++)
{
(Vector3 point, Vector3 normal) = RandomPointAndNormalInTriangle(
_mesh.vertices[tris[i * 3]], _mesh.vertices[tris[i * 3 + 1]], _mesh.vertices[tris[i * 3 + 2]],
_mesh.normals[tris[i * 3]], _mesh.normals[tris[i * 3 + 1]], _mesh.normals[tris[i * 3 + 2]]);
pointList.Add(transform.TransformPoint(point));
// normalList.Add(normal);
normalList.Add(transform.TransformDirection(normal));
// Debug.DrawRay(point, normal, Color.red, 10);
}
}
points = pointList.ToArray();
normals = normalList.ToArray();
}
float TriangleArea(Vector3 v1, Vector3 v2, Vector3 v3)
{
return Vector3.Cross(v1 - v2, v1 - v3).magnitude * 0.5f;
}
(Vector3, Vector3) RandomPointAndNormalInTriangle(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 n1, Vector3 n2, Vector3 n3)
{
var r1 = Mathf.Sqrt(Random.value);
var r2 = Random.value;
var point = (1 - r1) * v1 + r1 * (1 - r2) * v2 + r1 * r2 * v3;
var normal = ((1 - r1) * n1 + r1 * (1 - r2) * n2 + r1 * r2 * n3).normalized;
return (point, normal);
}
public bool ArePointsGenerated()
{
return points != null && points.Length > 0;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99677e4825d094c0eab5be922e28904d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,474 @@
using UnityEngine;
using UnityEngine.Rendering;
[RequireComponent(typeof(Camera)),
// RequireComponent(typeof(SceneColorSetup))
]
public class PostProcesser : MonoBehaviour
{
[Header("Grass Overlaying Shaders")]
public LayerMask grassLayer;
public ShaderState grassState = ShaderState.On;
public Shader grassReplacementShader;
public float alphaThreshold = 0.5f;
private GameObject _grassCameraObject;
[Header("Pixel Shader")]
public ShaderState pixelState = ShaderState.On;
public bool dynamicPixelSize = false;
public int screenHeight = 192;
public float pixelsPerUnit = 24f;
[Range(1f / 32f, 1)] public float zoom = 0.125f;
[Header("Outline Shader")]
public ShaderState outlineState = ShaderState.On;
public Color outlineColor = Color.black;
public Color edgeColor = Color.white;
public float depthThreshold = 0.02f;
public float normalThreshold = 0.05f;
public Vector3 normalEdgeBias = Vector3.one;
public float angleThreshold = 0.5f;
public int angleFactorScale = 7;
[Header("Cloud Shader")]
public ShaderState cloudState = ShaderState.On;
public Texture2D cloudsFineDetail;
public Texture2D cloudsMediumDetail;
public Texture2D cloudsLargeDetail;
public Light lightSource;
public float cloudSpeed = 0.1f;
[Range(0, 1)] public float cloudThickness = 0.5f;
[Range(0, 1)] public float cloudCoverage = 0.5f;
[Range(0, 360)] public float cloudDirection = 30f;
public float cloudZoom = 10f;
public enum ShaderState
{
On,
Off,
Debug,
}
void OnEnable()
{
// Camera.main.depthTextureMode = DepthTextureMode.MotionVectors | DepthTextureMode.DepthNormals | DepthTextureMode.Depth;
Camera.main.depthTextureMode = DepthTextureMode.DepthNormals | DepthTextureMode.Depth;
if (grassState != ShaderState.Debug)
Camera.main.cullingMask = ~(1 << (int)Mathf.Log(grassLayer.value, 2));
if (grassState == ShaderState.On && _grassCameraObject == null)
{
_grassCameraObject = new GameObject("GrassCamera");
_grassCameraObject.transform.SetParent(Camera.main.transform);
_grassCameraObject.AddComponent<Camera>();
}
}
void OnDisable()
{
if (_grassCameraObject != null)
{
Destroy(_grassCameraObject);
_grassCameraObject = null;
}
}
void UpdateZoom()
{
if (Camera.main == null)
return;
Camera.main.orthographicSize = 1 / zoom;
var farPlane = 20 / zoom;
var pos = Camera.main.transform.localPosition;
pos.z = -farPlane / 2;
Camera.main.transform.SetLocalPositionAndRotation(pos, Quaternion.identity);
Camera.main.farClipPlane = farPlane;
}
void OnValidate()
{
UpdateZoom();
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
var outlineMaterial = CoreUtils.CreateEngineMaterial("Custom/PixelPerfectOutline");
outlineMaterial.SetFloat("_DepthThreshold", depthThreshold);
outlineMaterial.SetFloat("_AngleThreshold", angleThreshold);
outlineMaterial.SetFloat("_AngleFactorScale", angleFactorScale);
outlineMaterial.SetFloat("_NormalThreshold", normalThreshold);
outlineMaterial.SetVector("_NormalEdgeBias", normalEdgeBias);
outlineMaterial.SetInteger("_DebugOutline", outlineState == ShaderState.Debug ? 1 : 0);
outlineMaterial.SetColor("_OutlineColor", outlineColor);
outlineMaterial.SetColor("_EdgeColor", edgeColor);
var grassBlendingMaterial = CoreUtils.CreateEngineMaterial("Custom/GrassBlending");
grassBlendingMaterial.SetFloat("_AlphaThreshold", alphaThreshold);
UpdateZoom();
var pixelScreenHeight = screenHeight;
if (dynamicPixelSize)
{
pixelScreenHeight = (int)(1 / zoom * pixelsPerUnit);
}
var pixelScreenWidth = (int)(pixelScreenHeight * Camera.main.aspect + 0.5f);
var tempTex = RenderTexture.GetTemporary(src.descriptor);
var grassTex = RenderTexture.GetTemporary(src.descriptor);
var cloudTex = RenderTexture.GetTemporary(2048, 2048);
var screenSize = new Vector2(Screen.width, Screen.height);
if (pixelState == ShaderState.On)
{
src.filterMode = FilterMode.Point;
tempTex.Release();
tempTex.height = pixelScreenHeight;
tempTex.width = pixelScreenWidth;
tempTex.filterMode = FilterMode.Point;
tempTex.Create();
grassTex.Release();
grassTex.height = pixelScreenHeight;
grassTex.width = pixelScreenWidth;
grassTex.filterMode = FilterMode.Point;
grassTex.Create();
screenSize = new Vector2(pixelScreenWidth, pixelScreenHeight);
}
else
{
src.filterMode = FilterMode.Bilinear;
tempTex.filterMode = FilterMode.Bilinear;
tempTex.Release();
tempTex.Create();
grassTex.filterMode = FilterMode.Bilinear;
grassTex.Release();
grassTex.Create();
}
if (cloudState == ShaderState.On)
{
var cloudMaterial = CoreUtils.CreateEngineMaterial("Custom/Clouds");
cloudMaterial.SetFloat("_Speed", cloudSpeed);
cloudMaterial.SetFloat("_Coverage", cloudCoverage);
cloudMaterial.SetFloat("_Thickness", cloudThickness);
cloudMaterial.SetFloat("_Direction", cloudDirection);
cloudMaterial.SetTexture("_FineDetail", cloudsFineDetail);
cloudMaterial.SetTexture("_MediumDetail", cloudsMediumDetail);
cloudMaterial.SetTexture("_LargeDetail", cloudsLargeDetail);
var cloudTexTemp = RenderTexture.GetTemporary(2048, 2048);
cloudTexTemp.filterMode = FilterMode.Bilinear;
cloudTexTemp.wrapMode = TextureWrapMode.Repeat;
cloudTex.filterMode = FilterMode.Bilinear;
cloudTex.wrapMode = TextureWrapMode.Repeat;
Graphics.Blit(cloudTexTemp, cloudTex, cloudMaterial);
RenderTexture.ReleaseTemporary(cloudTexTemp);
lightSource.cookie = cloudTex;
lightSource.cookieSize = cloudZoom;
}
else
{
lightSource.cookie = null;
}
outlineMaterial.SetVector("_ScreenSize", screenSize);
Camera grassCamera = null;
if (grassState == ShaderState.On)
{
grassCamera = _grassCameraObject.GetComponent<Camera>();
grassCamera.enabled = false;
}
if (grassState == ShaderState.On && outlineState != ShaderState.Debug && grassCamera != null)
{
// var grassCameraObject = new GameObject("GrassCamera");
// grassCameraObject.transform.SetParent(Camera.main.transform);
// var grassCamera = _grassCameraObject.GetComponent<Camera>();
grassCamera.CopyFrom(Camera.main);
grassCamera.targetTexture = grassTex;
grassCamera.cullingMask = -1;
grassCamera.clearFlags = CameraClearFlags.Nothing;
grassCamera.enabled = true;
grassCamera.RenderWithShader(grassReplacementShader, "RenderType");
grassCamera.enabled = false;
// Destroy(grassCameraObject);
grassBlendingMaterial.SetTexture("_GrassTex", grassTex);
}
if (outlineState != ShaderState.Off)
{
Graphics.Blit(src, tempTex, outlineMaterial);
if (grassState == ShaderState.On && outlineState != ShaderState.Debug)
Graphics.Blit(tempTex, dest, grassBlendingMaterial);
else
Graphics.Blit(tempTex, dest);
}
else if (grassState == ShaderState.On)
{
Graphics.Blit(src, tempTex, grassBlendingMaterial);
Graphics.Blit(tempTex, dest);
}
else
{
Graphics.Blit(src, tempTex);
Graphics.Blit(tempTex, dest);
}
if (grassCamera != null)
grassCamera.targetTexture = null;
RenderTexture.ReleaseTemporary(tempTex);
RenderTexture.ReleaseTemporary(grassTex);
RenderTexture.ReleaseTemporary(cloudTex);
Graphics.SetRenderTarget(dest);
}
}
// using UnityEngine;
// using UnityEngine.Rendering;
// [RequireComponent(typeof(Camera)),
// RequireComponent(typeof(SceneColorSetup))]
// public class PostProcesser : MonoBehaviour
// {
// [Header("Grass Overlaying Shaders")]
// public LayerMask grassLayer;
// public ShaderState grassState = ShaderState.On;
// public Shader grassReplacementShader;
// public float alphaThreshold = 0.5f;
// private GameObject _grassCameraObject;
// [Header("Pixel Shader")]
// public ShaderState pixelState = ShaderState.On;
// public bool dynamicPixelSize = false;
// public int screenHeight = 192;
// public float pixelsPerUnit = 24f;
// [Range(1f / 32f, 1)] public float zoom = 0.125f;
// [Header("Outline Shader")]
// public ShaderState outlineState = ShaderState.On;
// public Color outlineColor = Color.black;
// public Color edgeColor = Color.white;
// public float depthThreshold = 0.02f;
// public float normalThreshold = 0.05f;
// public Vector3 normalEdgeBias = Vector3.one;
// public float angleThreshold = 0.5f;
// public int angleFactorScale = 7;
// [Header("Cloud Shader")]
// public ShaderState cloudState = ShaderState.On;
// public Texture2D cloudsFineDetail;
// public Texture2D cloudsMediumDetail;
// public Texture2D cloudsLargeDetail;
// public Light lightSource;
// public float cloudSpeed = 0.1f;
// [Range(0, 1)] public float cloudThickness = 0.5f;
// [Range(0, 1)] public float cloudCoverage = 0.5f;
// [Range(0, 360)] public float cloudDirection = 30f;
// public float cloudZoom = 10f;
// public enum ShaderState
// {
// On,
// Off,
// Debug,
// }
// void OnEnable()
// {
// Camera.main.depthTextureMode = DepthTextureMode.DepthNormals;
// if (grassState != ShaderState.Debug)
// Camera.main.cullingMask = ~(1 << (int)Mathf.Log(grassLayer.value, 2));
// if (grassState == ShaderState.On && _grassCameraObject == null)
// {
// _grassCameraObject = new GameObject("GrassCamera");
// _grassCameraObject.transform.SetParent(Camera.main.transform);
// _grassCameraObject.AddComponent<Camera>();
// }
// }
// void OnDisable()
// {
// if (_grassCameraObject != null)
// {
// Destroy(_grassCameraObject);
// _grassCameraObject = null;
// }
// }
// void UpdateZoom()
// {
// if (Camera.main == null)
// return;
// Camera.main.orthographicSize = 1 / zoom;
// var farPlane = 20 / zoom;
// var pos = Camera.main.transform.localPosition;
// pos.z = -farPlane / 2;
// Camera.main.transform.SetLocalPositionAndRotation(pos, Quaternion.identity);
// Camera.main.farClipPlane = farPlane;
// }
// void OnValidate()
// {
// UpdateZoom();
// }
// void OnRenderImage(RenderTexture src, RenderTexture dest)
// {
// var outlineMaterial = CoreUtils.CreateEngineMaterial("Custom/PixelPerfectOutline");
// outlineMaterial.SetFloat("_DepthThreshold", depthThreshold);
// outlineMaterial.SetFloat("_AngleThreshold", angleThreshold);
// outlineMaterial.SetFloat("_AngleFactorScale", angleFactorScale);
// outlineMaterial.SetFloat("_NormalThreshold", normalThreshold);
// outlineMaterial.SetVector("_NormalEdgeBias", normalEdgeBias);
// outlineMaterial.SetInteger("_DebugOutline", outlineState == ShaderState.Debug ? 1 : 0);
// outlineMaterial.SetColor("_OutlineColor", outlineColor);
// outlineMaterial.SetColor("_EdgeColor", edgeColor);
// var grassBlendingMaterial = CoreUtils.CreateEngineMaterial("Custom/GrassBlending");
// grassBlendingMaterial.SetFloat("_AlphaThreshold", alphaThreshold);
// UpdateZoom();
// var pixelScreenHeight = screenHeight;
// if (dynamicPixelSize)
// {
// pixelScreenHeight = (int)(1 / zoom * pixelsPerUnit);
// }
// var pixelScreenWidth = (int)(pixelScreenHeight * Camera.main.aspect + 0.5f);
// var tempTex = RenderTexture.GetTemporary(src.descriptor);
// var grassTex = RenderTexture.GetTemporary(src.descriptor);
// var cloudTex = RenderTexture.GetTemporary(2048, 2048);
// var screenSize = new Vector2(Screen.width, Screen.height);
// if (pixelState == ShaderState.On)
// {
// src.filterMode = FilterMode.Point;
// tempTex.Release();
// tempTex.height = pixelScreenHeight;
// tempTex.width = pixelScreenWidth;
// tempTex.filterMode = FilterMode.Point;
// tempTex.Create();
// grassTex.Release();
// grassTex.height = pixelScreenHeight;
// grassTex.width = pixelScreenWidth;
// grassTex.filterMode = FilterMode.Point;
// grassTex.Create();
// screenSize = new Vector2(pixelScreenWidth, pixelScreenHeight);
// }
// else
// {
// src.filterMode = FilterMode.Bilinear;
// tempTex.filterMode = FilterMode.Bilinear;
// tempTex.Release();
// tempTex.Create();
// grassTex.filterMode = FilterMode.Bilinear;
// grassTex.Release();
// grassTex.Create();
// }
// if (cloudState == ShaderState.On)
// {
// var cloudMaterial = CoreUtils.CreateEngineMaterial("Custom/Clouds");
// cloudMaterial.SetFloat("_Speed", cloudSpeed);
// cloudMaterial.SetFloat("_Coverage", cloudCoverage);
// cloudMaterial.SetFloat("_Thickness", cloudThickness);
// cloudMaterial.SetFloat("_Direction", cloudDirection);
// cloudMaterial.SetTexture("_FineDetail", cloudsFineDetail);
// cloudMaterial.SetTexture("_MediumDetail", cloudsMediumDetail);
// cloudMaterial.SetTexture("_LargeDetail", cloudsLargeDetail);
// var cloudTexTemp = RenderTexture.GetTemporary(2048, 2048);
// cloudTexTemp.filterMode = FilterMode.Bilinear;
// cloudTexTemp.wrapMode = TextureWrapMode.Repeat;
// cloudTex.filterMode = FilterMode.Bilinear;
// cloudTex.wrapMode = TextureWrapMode.Repeat;
// Graphics.Blit(cloudTexTemp, cloudTex, cloudMaterial);
// RenderTexture.ReleaseTemporary(cloudTexTemp);
// lightSource.cookie = cloudTex;
// lightSource.cookieSize = cloudZoom;
// }
// else
// {
// lightSource.cookie = null;
// }
// outlineMaterial.SetVector("_ScreenSize", screenSize);
// var grassCamera = _grassCameraObject.GetComponent<Camera>();
// if (grassState == ShaderState.On && outlineState != ShaderState.Debug)
// {
// grassCamera.CopyFrom(Camera.main);
// grassCamera.targetTexture = grassTex;
// grassCamera.cullingMask = -1;
// grassCamera.clearFlags = CameraClearFlags.Nothing;
// grassCamera.RenderWithShader(grassReplacementShader, "RenderType");
// grassBlendingMaterial.SetTexture("_GrassTex", grassTex);
// }
// if (outlineState != ShaderState.Off)
// {
// Graphics.Blit(src, tempTex, outlineMaterial);
// if (grassState == ShaderState.On && outlineState != ShaderState.Debug)
// Graphics.Blit(tempTex, dest, grassBlendingMaterial);
// else
// Graphics.Blit(tempTex, dest);
// }
// else if (grassState == ShaderState.On)
// {
// Graphics.Blit(src, tempTex, grassBlendingMaterial);
// Graphics.Blit(tempTex, dest);
// }
// else
// {
// Graphics.Blit(src, tempTex);
// Graphics.Blit(tempTex, dest);
// }
// if (grassCamera != null)
// grassCamera.targetTexture = null;
// RenderTexture.ReleaseTemporary(tempTex);
// RenderTexture.ReleaseTemporary(grassTex);
// RenderTexture.ReleaseTemporary(cloudTex);
// Graphics.SetRenderTarget(dest);
// }
// }

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5ae31a9a3a3e946d1a6a904b9ab115c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,17 @@
using UnityEngine;
public class Rotator : MonoBehaviour
{
[SerializeField] float _speedX = 50f;
[SerializeField] float _speedY = 50f;
[SerializeField] float _speedZ = 50f;
void Update()
{
var rotationX = _speedX * Time.deltaTime;
var rotationY = _speedY * Time.deltaTime;
var rotationZ = _speedZ * Time.deltaTime;
transform.Rotate(rotationX, rotationY, rotationZ, Space.Self);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2aa0b8e5a118943d3a3dd673af44c3aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,78 @@
/*
Copies the camera buffer after rendering opaques, to _CameraOpaqueTexture property so the Scene Color node can work in the Built-in RP.
Attach this script to the Main Camera (and any other cameras you want an Opaque Texture for)
Will also work for Scene Views (Though if a new window is opened will need to enter & exit play mode)
Tested in 2022.2
@Cyanilux
*/
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteAlways]
public class SceneColorSetup : MonoBehaviour
{
private Camera cam;
private CommandBuffer cmd, cmd2;
void OnEnable()
{
cam = GetComponent<Camera>();
int rtID = Shader.PropertyToID("_CameraOpaqueTexture");
if (cmd == null)
{
cmd = new CommandBuffer
{
name = "Setup Opaque Texture"
};
cmd.GetTemporaryRT(rtID, cam.pixelWidth, cam.pixelHeight, 0);
// cmd.Blit(null, rtID);
}
if (cmd2 == null)
{
cmd2 = new CommandBuffer
{
name = "Release Opaque Texture"
};
cmd2.ReleaseTemporaryRT(rtID);
}
// Game View (camera script is assigned on)
cam.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cmd);
cam.AddCommandBuffer(CameraEvent.AfterEverything, cmd2);
// Scene View
#if UNITY_EDITOR
if (cam.tag == "MainCamera")
{
Camera[] sceneCameras = UnityEditor.SceneView.GetAllSceneCameras();
foreach (Camera c in sceneCameras)
{
c.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cmd);
c.AddCommandBuffer(CameraEvent.AfterEverything, cmd2);
}
}
#endif
}
void OnDisable()
{
// Game View (camera script is assigned on)
cam.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, cmd);
cam.RemoveCommandBuffer(CameraEvent.AfterEverything, cmd2);
// Scene View
#if UNITY_EDITOR
if (cam.tag == "MainCamera")
{
Camera[] sceneCameras = UnityEditor.SceneView.GetAllSceneCameras();
foreach (Camera c in sceneCameras)
{
c.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, cmd);
c.RemoveCommandBuffer(CameraEvent.AfterEverything, cmd2);
}
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 885a2b24c00804ed7b6d4990f0849e0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
using UnityEngine;
public class WaterLevelShift : MonoBehaviour
{
public float shift = 0.5f;
public float speed = 0.5f;
private float waterLevel = 0f;
void Start()
{
waterLevel = transform.position.y;
}
void Update()
{
float newY = waterLevel + Mathf.Sin(Time.time * speed) * shift;
transform.position = new Vector3(transform.position.x, newY, transform.position.z);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 869babbd4fd3b4bdbb667ec33cc4c4af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: