Files
WoodHorror/Assets/StixGames/GrassShader/Scripts/GrassManipulationUtility.cs
Caleb Sandford deQuincey f2cbef3bb8 initial project commit
2025-06-20 11:37:30 +01:00

194 lines
6.4 KiB
C#

using System;
using System.Linq;
using UnityEngine;
namespace StixGames.GrassShader
{
/// <summary>
/// This class is a base point for displacing the grass.
/// It's really nothing else than a texture painter, with some vector transformations for the displacement.
/// </summary>
public static class GrassManipulationUtility
{
/// <summary>
/// Returns the texture from the transform or throws a exception if it wasn't found.
/// </summary>
/// <param name="target"></param>
/// <param name="getColorHeight">If true returns color/height texture instead of the displacement texture.</param>
/// <returns></returns>
public static Texture2D GetGrassTexture(Transform target, bool getColorHeight)
{
var material = target.GetComponent<Renderer>().material;
var tex = material.GetTexture(getColorHeight ? "_ColorMap" : "_Displacement") as Texture2D;
if (tex == null)
{
throw new InvalidOperationException("You have to add a texture (or texture-adder component) if you want to use dynamic grass.");
}
return tex;
}
/// <summary>
/// This uses the forward and right vectors as a matrix and returns its reverse.
/// </summary>
/// <param name="forward"></param>
/// <param name="right"></param>
/// <param name="inverseForward"></param>
/// <param name="inverseRight"></param>
public static void Invert2x2Matrix(Vector2 forward, Vector2 right, out Vector2 inverseForward, out Vector2 inverseRight)
{
float det = right.x * forward.y - right.y * forward.x;
inverseRight = new Vector2(forward.y, -right.y) / det;
inverseForward = new Vector2(-forward.x, right.x) / det;
}
/// <summary>
/// Gets the 2x2 matrix that tranforms from world to texture space.
/// Uses raycasts, so don't overuse it.
/// </summary>
/// <param name="pos"></param>
/// <param name="rayDir"></param>
/// <param name="maxDistance"></param>
/// <param name="offset"></param>
/// <param name="layerMask"></param>
/// <param name="texCoord"></param>
/// <param name="forward"></param>
/// <param name="right"></param>
/// <returns>Returns the transform of the object that was hit. That doesn't really belong in this function, but at least is uses one raycast less.</returns>
public static Transform GetWorldToTextureSpaceMatrix(Vector3 pos, Vector3 rayDir, float maxDistance, float offset, LayerMask layerMask, out Vector2 texCoord, out Vector2 forward, out Vector2 right)
{
Ray ray = new Ray(pos, rayDir.normalized);
RaycastHit hit;
Debug.DrawRay(ray.origin, ray.direction * maxDistance, Color.red);
if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
{
texCoord = hit.textureCoord;
forward = GetTexCoordDifference(hit.transform, pos, texCoord, Vector3.forward * offset, maxDistance, layerMask);
right = GetTexCoordDifference(hit.transform, pos, texCoord, Vector3.right * offset, maxDistance, layerMask);
return hit.transform;
}
texCoord = Vector2.zero;
forward = Vector2.zero;
right = Vector2.zero;
return null;
}
public static bool GetWorldToTextureSpaceMatrix(Ray ray, float offset, Collider[] colliders, out RaycastHit hit, out Vector2 forward, out Vector2 right)
{
var newHit = new RaycastHit();
if (colliders.Any(x => x.Raycast(ray, out newHit, Mathf.Infinity)))
{
forward = GetTexCoordDifference(ray.origin, newHit.textureCoord, Vector3.forward * offset, colliders);
right = GetTexCoordDifference(ray.origin, newHit.textureCoord, Vector3.right * offset, colliders);
hit = newHit;
return true;
}
hit = newHit;
forward = Vector2.zero;
right = Vector2.zero;
return false;
}
public static bool GetWorldToTextureSpaceMatrix(Ray ray, float offset, Collider collider, out RaycastHit hit, out Vector2 forward, out Vector2 right)
{
if (collider.Raycast(ray, out hit, Mathf.Infinity))
{
forward = GetTexCoordDifference(ray.origin, hit.textureCoord, Vector3.forward * offset, collider);
right = GetTexCoordDifference(ray.origin, hit.textureCoord, Vector3.right * offset, collider);
return true;
}
hit = new RaycastHit();
forward = Vector2.zero;
right = Vector2.zero;
return false;
}
private static Vector2 GetTexCoordDifference(Transform targetObject, Vector3 pos, Vector2 texCoords, Vector3 offset, float maxDistance, LayerMask layerMask)
{
Ray ray = new Ray(pos + offset, Vector3.down);
RaycastHit hit;
//Send ray in dir to check for
if (Physics.Raycast(ray, out hit, maxDistance, layerMask) && hit.transform == targetObject)
{
return (hit.textureCoord - texCoords) / offset.magnitude;
}
ray.direction = -ray.direction;
if (Physics.Raycast(ray, out hit, maxDistance, layerMask) && hit.transform == targetObject)
{
return (texCoords - hit.textureCoord) / offset.magnitude;
}
return Vector2.zero;
}
private static Vector2 GetTexCoordDifference(Vector3 pos, Vector2 texCoords, Vector3 offset, Collider[] colliders)
{
Ray ray = new Ray(pos + offset, Vector3.down);
RaycastHit hit = new RaycastHit();
//Send ray in dir to check for
if (colliders.Any(x => x.Raycast(ray, out hit, Mathf.Infinity)))
{
return (hit.textureCoord - texCoords) / offset.magnitude;
}
ray.direction = -ray.direction;
if (colliders.Any(x => x.Raycast(ray, out hit, Mathf.Infinity)))
{
return (texCoords - hit.textureCoord) / offset.magnitude;
}
return Vector2.zero;
}
private static Vector2 GetTexCoordDifference(Vector3 pos, Vector2 texCoords, Vector3 offset, Collider collider)
{
Ray ray = new Ray(pos + offset, Vector3.down);
RaycastHit hit;
//Send ray in dir to check for
if (collider.Raycast(ray, out hit, Mathf.Infinity))
{
return (hit.textureCoord - texCoords) / offset.magnitude;
}
ray.direction = -ray.direction;
if (collider.Raycast(ray, out hit, Mathf.Infinity))
{
return (texCoords - hit.textureCoord) / offset.magnitude;
}
return Vector2.zero;
}
/// <summary>
/// Converts a vector to a world space normal map color.
/// </summary>
/// <param name="vec"></param>
/// <returns></returns>
public static Vector2 VectorToColorSpace(Vector2 vec)
{
return (vec + Vector2.one) * 0.5f;
}
/// <summary>
/// Converts a world space normal map color (vector) to a world space vector.
/// </summary>
/// <param name="vec"></param>
/// <returns></returns>
public static Vector2 ColorToVectorSpace(Vector2 vec)
{
return vec * 2 - Vector2.one;
}
}
}