Skip to content

Instantly share code, notes, and snippets.

@nmathew87
Created February 13, 2020 18:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nmathew87/e1e10311c660b6ee19f1366cdb6f49e8 to your computer and use it in GitHub Desktop.
Save nmathew87/e1e10311c660b6ee19f1366cdb6f49e8 to your computer and use it in GitHub Desktop.
Updated reticle controller for HelloWorld sample in PlacenoteSDK-Unity. This controller makes the ARKit FeaturePoint HitTest much smoother and more robust than the default version.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
namespace HelloWorld
{
public class ReticleController : MonoBehaviour
{
[SerializeField] GameObject mReticle;
[SerializeField] Text notifications;
[SerializeField] ARPointCloudManager mPointCloudManager;
private Vector3 lastCursorPosition;
private int mCurrentTrackedFeatureCount = 1000;
private float timeOfLastPtCloudUpdate;
static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();
public ARRaycastManager m_RaycastManager;
private IEnumerator mGoToTarget;
private IEnumerator mContinuousHittest;
void Start()
{
mContinuousHittest = ContinuousHittest();
lastCursorPosition = new Vector3(-200f, -200f, -200f);
mPointCloudManager.pointCloudsChanged += OnPointCloudChanged;
}
// starts the cursor
public void StartReticle()
{
mReticle.SetActive(false);
StartCoroutine(mContinuousHittest);
}
public void StopReticle()
{
StopCoroutine(mContinuousHittest);
mReticle.SetActive(false);
}
// Add this
void OnPointCloudChanged(ARPointCloudChangedEventArgs eventargs)
{
if (eventargs.updated.Count == 1)
{
foreach (var ptcloud in eventargs.updated)
{
mCurrentTrackedFeatureCount = ptcloud.positions.Length;
Debug.Log("Cursor: Current tracked feature count = " + mCurrentTrackedFeatureCount);
}
timeOfLastPtCloudUpdate = Time.time;
}
}
private IEnumerator ContinuousHittest()
{
bool withinDistance = false;
int badTrackingCounter = 0;
while (true)
{
// getting screen point
var screenPosition = new Vector2(Screen.width / 2, Screen.height / 2);
// World Hit Test - Type PlaneWithinBounds
if (m_RaycastManager.Raycast(screenPosition, s_Hits, TrackableType.FeaturePoint))
{
// Raycast hits are sorted by distance, so get the closest hit.
var targetPose = s_Hits[0].pose;
// move reticle to the hit test point
//mReticle.transform.position = targetPose.position;
// Show reticle if there's an active hit result
//mReticle.SetActive(true);
Vector3 screenCenter = Camera.main.ScreenToWorldPoint(screenPosition);
float distanceToReticle = Vector3.Magnitude(targetPose.position - screenCenter);
float reticleDistanceChange = (targetPose.position - lastCursorPosition).magnitude;
Debug.Log("Cursor: Distance = " + distanceToReticle);
if (reticleDistanceChange < 0.03f)
{
// do nothing
}
else
{
if (mCurrentTrackedFeatureCount < 30) // && trustworthyHitTestDistanceChange > 0.15f
{
// too few features
Debug.Log("Cursor: Too few features");
mReticle.SetActive(false);
}
else if ((Time.time - timeOfLastPtCloudUpdate > 3.0f && distanceToReticle > 1.1f) ||
(Time.time - timeOfLastPtCloudUpdate > 3.0f && distanceToReticle <= 1.1f && reticleDistanceChange > 0.14f))
{
mReticle.SetActive(false);
//GetComponent<PromptManager>().SetPrompt("No surface detected");
}
else if (withinDistance == false && distanceToReticle > 1.7f)
{
mReticle.SetActive(false);
//GetComponent<PromptManager>().SetPrompt("Move closer to activate the focus ring");
badTrackingCounter = 0;
}
else if (withinDistance == true && distanceToReticle > 1.73f) // hysteresis for distance
{
// too far
withinDistance = false;
mReticle.SetActive(false);
//GetComponent<PromptManager>().SetPrompt("Move closer to activate the focus ring");
badTrackingCounter = 0;
}
else
{
Debug.Log("Cursor: Passed all checks");
// Passed all checks
withinDistance = true;
badTrackingCounter++;
if (badTrackingCounter > 10)
{
Debug.Log("Cursor: Showing cursor");
mReticle.SetActive(true);
mReticle.transform.LookAt(Camera.main.transform);
//GetComponent<PromptManager>().SetPrompt("");
// update the last cursor position
lastCursorPosition = targetPose.position;
//mReticle.transform.position = targetPose.position;
// stop the previous animation
if (mGoToTarget != null)
{
StopCoroutine(mGoToTarget);
}
// start new animation to go to this destination
mGoToTarget = GoToTarget(targetPose.position);
StartCoroutine(mGoToTarget);
}
}
// hide reticle if there's no hit test result
//mReticle.SetActive(false);
}
// go to next frame
yield return null;
}
}
}
IEnumerator GoToTarget(Vector3 destination)
{
float distance = (destination - mReticle.transform.position).magnitude;
while (distance > 0)
{
float step = distance * Time.deltaTime / 0.2f;
// Move our position a step closer to the target.
mReticle.transform.position = Vector3.MoveTowards(mReticle.transform.position, destination, step);
// update distance
distance = (destination - mReticle.transform.position).magnitude;
yield return null;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment