using System; using System.Collections.Generic; using UnityEngine; namespace Streamingle.Gaze { /// /// Row-major layout of the calibration grid. Down/Up describe gaze pitch and /// Left/Right describe gaze yaw from the character's point of view. /// public enum GazeCalibrationPoint { DownLeft = 0, DownCenter = 1, DownRight = 2, CenterLeft = 3, Center = 4, CenterRight = 5, UpLeft = 6, UpCenter = 7, UpRight = 8 } public enum GazeChannelUsage { EyeLook = 0, Corrective = 1 } public enum GazeCorrectiveBlendMode { AdditiveFromCenter = 0, Override = 1 } public enum GazeCutResponse { Snap = 0, Smooth = 1 } [Serializable] public sealed class GazeBlendShapeChannel { [SerializeField] private string blendShapeName; [SerializeField] private GazeChannelUsage usage; [SerializeField] [Tooltip("Corrective channels normally add their direction-dependent delta from the Center sample to the live facial value.")] private GazeCorrectiveBlendMode correctiveBlendMode; public GazeBlendShapeChannel( string blendShapeName, GazeChannelUsage usage = GazeChannelUsage.EyeLook, GazeCorrectiveBlendMode correctiveBlendMode = GazeCorrectiveBlendMode.AdditiveFromCenter) { this.blendShapeName = blendShapeName; this.usage = usage; this.correctiveBlendMode = correctiveBlendMode; } public string BlendShapeName { get => blendShapeName; set => blendShapeName = value; } public GazeChannelUsage Usage { get => usage; set => usage = value; } public GazeCorrectiveBlendMode CorrectiveBlendMode { get => correctiveBlendMode; set => correctiveBlendMode = value; } } [Serializable] public sealed class GazeCalibrationSample { [SerializeField] private GazeCalibrationPoint point; [SerializeField] private bool captured; [SerializeField] private float[] weights = Array.Empty(); public GazeCalibrationSample(GazeCalibrationPoint point) { this.point = point; } public GazeCalibrationPoint Point => point; public bool Captured => captured; public IReadOnlyList Weights => weights; internal float GetWeight(int index) { return weights != null && index >= 0 && index < weights.Length ? weights[index] : 0f; } internal void SetWeights(IReadOnlyList values, int channelCount) { var count = Mathf.Max(0, channelCount); if (weights == null || weights.Length != count) weights = new float[count]; for (var i = 0; i < count; i++) weights[i] = values != null && i < values.Count ? values[i] : 0f; captured = true; } internal void Resize(int channelCount) { var count = Mathf.Max(0, channelCount); if (weights != null && weights.Length == count) return; var resized = new float[count]; if (weights != null) Array.Copy(weights, resized, Mathf.Min(weights.Length, resized.Length)); weights = resized; captured = false; } internal void Invalidate() { captured = false; } internal void SetPoint(GazeCalibrationPoint value) { point = value; } } /// /// Command passed from Timeline (or a future real-time source) to the final /// LateUpdate blendshape writer. /// public struct GazeTimelineState { public Vector3 TargetPosition; public bool HasTargetPosition; public Vector3 TargetDirection; public bool HasTargetDirection; public Transform Target; public bool UseMainCamera; public float Influence; public bool EnableCorrectives; public bool OverrideCutResponse; public GazeCutResponse CutResponse; public float CutTransitionDuration; public bool ResetSmoothing; public bool Deterministic; } public readonly struct GazeDirectionResult { public GazeDirectionResult( Vector3 worldDirection, Vector3 localDirection, float rawYaw, float rawPitch, float clampedYaw, float clampedPitch, float normalizedYaw, float normalizedPitch) { WorldDirection = worldDirection; LocalDirection = localDirection; RawYaw = rawYaw; RawPitch = rawPitch; ClampedYaw = clampedYaw; ClampedPitch = clampedPitch; NormalizedYaw = normalizedYaw; NormalizedPitch = normalizedPitch; } public Vector3 WorldDirection { get; } public Vector3 LocalDirection { get; } public float RawYaw { get; } public float RawPitch { get; } public float ClampedYaw { get; } public float ClampedPitch { get; } public float NormalizedYaw { get; } public float NormalizedPitch { get; } public bool WasClamped => !Mathf.Approximately(RawYaw, ClampedYaw) || !Mathf.Approximately(RawPitch, ClampedPitch); } }