using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// Handles the clickable ball objects spawned by AutoCalibrationManager, which lets the user set reference points.
/// Should be assigned to a prefab and instantiated with it.
/// Also draws the current primary controller model in its center, facing the camera.
///
[RequireComponent(typeof(Collider))]
public class AutoCalibBall : MonoBehaviour, IXRHoverable, IXRClickable
{
///
/// Reference to the manager that spawned this ball. Used for reporting its real and virtual positions when assigned.
///
[Tooltip("Reference to the manager that spawned this ball. Used for reporting its real and virtual positions when assigned.")]
public AutoCalibrationManager autoCalib;
///
/// Material used to draw the controller object each frame.
///
[Space(5)]
[Tooltip("Material used to draw the controller object each frame. ")]
public Material controllerDrawMat;
///
/// Reference to the object that's enabled when this ball has been used to add a reference point. Checkmark by default.
///
[Space(5)]
[Tooltip("Reference to the object that's enabled when this ball has been used to add a reference point. Checkmark by default. ")]
public GameObject checkmarkDisplay;
///
/// Reference to the object that's enabled before this ball has been used to add a reference point. Cross by default.
///
[Tooltip("Reference to the object that's enabled before this ball has been used to add a reference point. Cross by default. ")]
public GameObject crossDisplay;
private DisplayState currentStage = DisplayState.Cross;
private Vector3 virtualPos;
private Vector3 realPos;
///
/// Whether or not the user has assigned a reference using this ball. Used for display purposes.
///
private bool hasBeenSet = false;
///
/// Unique index of the ball, to be used to set the real and virtual positions to the proper index within AutoCalibrationManager.
///
private int ballIndex;
///
/// Whether or not AutoCalibrationManager has called Setup() on this object. Necessary before it can be used.
///
private bool isSetup = false;
private Collider col;
private ZEDManager zedManager
{
get
{
return autoCalib.zedManager;
}
}
///
/// Material applied to the outside ball when hovered over by ZEDXRGrabber. Loads one from the Resources folder if not set manually.
///
[Space(5)]
[Tooltip("Material applied to the outside ball when hovered over by ZEDXRGrabber. Loads one from the Resources folder if not set manually. ")]
public Material hoverMaterial;
private Dictionary baseMaterials = new Dictionary();
public void Awake()
{
if(!hoverMaterial)
{
hoverMaterial = Resources.Load("HoverMatAlpha");
}
col = GetComponent();
col.isTrigger = true;
//Make the proper overhead display appear (the checkmark or cross)
SetBallStateDisplay(currentStage); //Should be NotSet.
}
///
/// Assigns the manager and index for this ball, both necessary before it can be used to set a reference point.
///
public void Setup(AutoCalibrationManager calibmanager, int ballindex)
{
autoCalib = calibmanager;
ballIndex = ballindex;
isSetup = true;
}
public void Update()
{
//Draw the primary hand controller on the inside.
SetControllerSkin skin = PrimaryHandSwitcher.primaryHandObject.GetComponentInChildren();
if(skin != null)
{
Mesh mesh = skin.GetFirstControllerMesh();
if (mesh)
{
Quaternion drawrot = Quaternion.LookRotation(zedManager.transform.position - transform.position, Vector3.up);
Vector3 drawpos = transform.position - drawrot * mesh.bounds.center;
controllerDrawMat.SetPass(0);
Graphics.DrawMesh(mesh, drawpos, drawrot, controllerDrawMat, CameraAnchor.HIDE_FROM_ZED_LAYER);
}
}
}
public Transform GetTransform()
{
return transform;
}
///
/// Starts the process of adding a virtual and real reference point. Only called when first activated, as the collider is disabled afterward.
///
///
void IXRClickable.OnClick(ZEDXRGrabber clicker)
{
//TODO: Graphics stuff.
StartCoroutine(PlacingBall(clicker));
}
///
/// Applies the hover material to the outside ball and temporarily hides the overhead checkmark/cross.
///
void IXRHoverable.OnHoverStart()
{
SetBallStateDisplay(hasBeenSet ? DisplayState.Checkmark : DisplayState.Nothing);
foreach (Renderer rend in GetComponentsInChildren()) //TODO: Lots of code repetition here. Make a static utility somewhere.
{
if (!baseMaterials.ContainsKey(rend))
{
baseMaterials.Add(rend, rend.material);
}
rend.material = hoverMaterial;
}
}
///
/// Removes the hover material from the outside ball and re-enables the checkmark or cross, depending on whether it's been set up.
///
void IXRHoverable.OnHoverEnd()
{
if (col.enabled == true)
{
SetBallStateDisplay(hasBeenSet ? DisplayState.Checkmark : DisplayState.Cross);
}
foreach (Renderer rend in GetComponentsInChildren())
{
if (baseMaterials.ContainsKey(rend))
{
rend.material = baseMaterials[rend];
}
else Debug.LogError("Starting material for " + rend.gameObject + " wasn't cached before hover started.");
}
}
///
/// Handles the process of adding a reference point. First logs the starting point as the "virtual" point, and freezes the
/// 2D video so the user can align the virtual controller with the real-world image. When they click again, this adds
/// the "real" point and sends it to the AutoCalibrationManager.
///
private IEnumerator PlacingBall(ZEDXRGrabber clicker)
{
if(!isSetup)
{
throw new System.Exception("Tried to use AutoCalibBall to calibrate before Setup was called on it.");
}
col.enabled = false;
SetBallStateDisplay(DisplayState.Nothing);
//Display new message.
MessageDisplay.DisplayMessageAll("AUTOMATIC MODE\r\nLine up the virtual controller with its real-world counterpart. Then click again.");
virtualPos = zedManager.transform.InverseTransformPoint(clicker.transform.position);
//DrawOutputToPlane.pauseTextureUpdate = true; //Pause the video
zedManager.pauseLiveReading = true;
//TODO: Graphics stuff.
//TODO: Hide all other calibration balls.
//print("Paused - " + ballIndex);
//First wait to make sure the click key is no longer held, so we can click it a second time separately.
while (clicker.zedController.CheckClickButton(ControllerButtonState.Down))
{
yield return null;
}
//Now wait for it to go down again.
while (!clicker.zedController.CheckClickButton(ControllerButtonState.Down))
{
yield return null;
}
realPos = zedManager.transform.InverseTransformPoint(clicker.transform.position);
autoCalib.AddNewPositions(ballIndex, virtualPos, realPos);
zedManager.pauseLiveReading = false;
col.enabled = true;
MessageDisplay.DisplayMessageAll("AUTOMATIC MODE\r\nRepeat this process with the rest of the balls, or stop when you're satisfied.");
hasBeenSet = true;
SetBallStateDisplay(DisplayState.Checkmark);
}
///
/// Enables/disables the overhead checkmark or cross, depending on the provided ball stage.
///
///
private void SetBallStateDisplay(DisplayState newstage)
{
switch(newstage)
{
case DisplayState.Cross:
if (checkmarkDisplay) checkmarkDisplay.SetActive(false);
if (crossDisplay) crossDisplay.SetActive(true);
break;
default:
case DisplayState.Nothing:
if (checkmarkDisplay) checkmarkDisplay.SetActive(false);
if (crossDisplay) crossDisplay.SetActive(false);
break;
case DisplayState.Checkmark:
if (checkmarkDisplay) checkmarkDisplay.SetActive(true);
if (crossDisplay) crossDisplay.SetActive(false);
break;
}
currentStage = newstage;
}
private void OnDestroy()
{
StopAllCoroutines(); //Prevents weird stuff from happening if you leave Automatic mode while adding a reference point.
}
///
/// Options for what to display overhead. Used by SetBallStateDisplay.
///
private enum DisplayState
{
Nothing,
Cross,
Checkmark
}
}