Files
RoomAwareVR/Assets/_Scripts/Eye-Tracking/FieldOfView.cs
T

145 lines
4.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
public class FieldOfView : MonoBehaviour
{
public float viewRadius;
[Range(0,360)]
public float periAngle;
[Range(0,180)]
public float foveaAngle;
public Transform eyeLeft;
public Transform eyeRight;
public LayerMask targetMask;
public LayerMask obstacleMask;
public float meshResolution;
public MeshFilter periMeshFilter;
public MeshFilter foveaMeshFilter;
private Mesh periMesh;
private Mesh foveaMesh;
private void Start()
{
periMesh = new Mesh();
periMesh.name = "View Mesh";
periMeshFilter.mesh = periMesh;
foveaMesh = new Mesh();
foveaMesh.name = "View Mesh";
foveaMeshFilter.mesh = foveaMesh;
}
private void LateUpdate()
{
Vector3 gazeDirection = ((eyeLeft.forward + eyeRight.forward) * 0.5f).normalized;
float gazeAngle = AngleFromDir(gazeDirection, true);
DrawFOV(transform.eulerAngles.y, periMesh, periAngle);
DrawFOV(gazeAngle, foveaMesh, foveaAngle);
}
public Vector3 DirFromAngle(float angleDeg, bool angleIsGlobal)
{
if (!angleIsGlobal)
{
angleDeg += transform.eulerAngles.y;
}
return new Vector3(Mathf.Sin(angleDeg * Mathf.Deg2Rad), 0, Mathf.Cos(angleDeg * Mathf.Deg2Rad));
}
public float AngleFromDir(Vector3 dir, bool angleIsGlobal)
{
// Sicherheitsnetz: yKomponente ignorieren und Vektor normalisieren
dir.y = 0;
if (dir.sqrMagnitude < 0.0001f) return 0f;
// Atan2: (x, z) → Winkel in Rad, dann nach ° umrechnen
float angleDeg = Mathf.Atan2(dir.x, dir.z) * Mathf.Rad2Deg;
// Bereich schön sauber auf 0360 bringen
angleDeg = (angleDeg + 360f) % 360f;
// Soll der Winkel relativ zum Objekt sein?
if (!angleIsGlobal)
angleDeg -= transform.eulerAngles.y;
// Und noch einmal normalisieren, damit auch hier 0360 garantiert ist
return (angleDeg + 360f) % 360f;
}
void DrawFOV(float startAngle, Mesh viewMesh, float viewAngle)
{
int stepCount = Mathf.RoundToInt(foveaAngle * meshResolution);
float stepAngleSize = viewAngle / stepCount;
List<Vector3> viewPoints = new List<Vector3>();
for (int i = 0; i <= stepCount; i++)
{
float angle = startAngle - viewAngle / 2 + stepAngleSize * i;
Debug.DrawLine(transform.position, transform.position + DirFromAngle(angle, true) * viewRadius, Color.red);
ViewCastInfo newViewCast = ViewCast(angle);
viewPoints.Add(newViewCast.hitPoint);
}
int vertexcount = viewPoints.Count + 1;
Vector3[] vertices = new Vector3[vertexcount];
int[] triangles = new int[(vertexcount-2) * 3];
vertices[0] = Vector3.zero;
for (int i = 0; i < vertexcount-1; i++)
{
vertices[i+1] = transform.InverseTransformPoint(viewPoints[i]);
if (i < vertexcount - 2)
{
triangles[i * 3] = 0;
triangles[i * 3 + 1] = i + 1;
triangles[i * 3 + 2] = i + 2;
}
}
viewMesh.Clear();
viewMesh.vertices = vertices;
viewMesh.triangles = triangles;
viewMesh.RecalculateNormals();
}
ViewCastInfo ViewCast(float globalAngle)
{
Vector3 direction = DirFromAngle(globalAngle, true);
RaycastHit hit;
if (Physics.Raycast(transform.position, direction, out hit, viewRadius, obstacleMask))
{
return new ViewCastInfo(true, hit.point, hit.distance, globalAngle);
}
else
{
return new ViewCastInfo(false, transform.position + direction * viewRadius, viewRadius, globalAngle);
}
}
public struct ViewCastInfo
{
public bool didHit;
public Vector3 hitPoint;
public float distance;
public float angle;
public ViewCastInfo(bool _hit, Vector3 _hitPoint, float _distance, float _angle)
{
didHit = _hit;
hitPoint = _hitPoint;
distance = _distance;
angle = _angle;
}
}
}