advancedskrald/ChessAR/Assets/Vuforia/Scripts/DefaultModelRecoEventHandler.cs

386 lines
12 KiB
C#
Raw Permalink Normal View History

/*==============================================================================
Copyright (c) 2017-2018 PTC Inc. All Rights Reserved.
Confidential and Proprietary - Protected under copyright and other laws.
Vuforia is a trademark of PTC Inc., registered in the United States and other
countries.
==============================================================================*/
using System.Linq;
using UnityEngine;
using Vuforia;
/// <summary>
/// A default implementation of Model Reco Event Handler.
/// It registers itself at the ModelRecoBehaviour and is notified of new search results.
/// </summary>
public class DefaultModelRecoEventHandler : MonoBehaviour, IObjectRecoEventHandler
{
#region PRIVATE_MEMBER_VARIABLES
private ModelTargetBehaviour mLastRecoModelTarget;
private bool mSearching;
private float mLastStatusCheckTime;
#endregion // PRIVATE_MEMBER_VARIABLES
#region PROTECTED_MEMBER_VARIABLES
// ModelRecoBehaviour reference to avoid lookups
protected ModelRecoBehaviour mModelRecoBehaviour;
// Target Finder reference to avoid lookups
protected TargetFinder mTargetFinder;
#endregion // PROTECTED_MEMBER_VARIABLES
#region PUBLIC_VARIABLES
/// <summary>
/// The Model Target used as template when a Model is recognized.
/// </summary>
[Tooltip("The Model Target used as Template when a model is recognized.")]
public ModelTargetBehaviour ModelTargetTemplate;
/// <summary>
/// Whether the model should be augmented with a bounding box.
/// Only applicable to Template model targets.
/// </summary>
[Tooltip("Whether the model should be augmented with a bounding box.")]
public bool ShowBoundingBox;
/// <summary>
/// Can be set in the Unity inspector to display error messages in UI.
/// </summary>
[Tooltip("UI Text label to display model reco errors.")]
public UnityEngine.UI.Text ModelRecoErrorText;
/// <summary>
/// Can be set in the Unity inspector to tell Vuforia whether it should:
/// - stop searching for new models, once a first model was found,
/// or:
/// - continue searching for new models, even after a first model was found.
/// </summary>
[Tooltip("Whether Vuforia should stop searching for other models, after the first model was found.")]
public bool StopSearchWhenModelFound = false;
/// <summary>
/// Can be set in the Unity inspector to tell Vuforia whether it should:
/// - stop searching for new models, while a target is being tracked and is in view,
/// or:
/// - continue searching for new models, even if a target is currently being tracked.
/// </summary>
[Tooltip("Whether Vuforia should stop searching for other models, while current model is tracked and visible.")]
public bool StopSearchWhileTracking = true;//true by default, as this is the recommended behaviour
#endregion // PUBLIC_VARIABLES
#region UNITY_MONOBEHAVIOUR_METHODS
/// <summary>
/// register for events at the ModelRecoBehaviour
/// </summary>
void Start()
{
// register this event handler at the model reco behaviour
var modelRecoBehaviour = GetComponent<ModelRecoBehaviour>();
if (modelRecoBehaviour)
{
modelRecoBehaviour.RegisterEventHandler(this);
}
// remember modelRecoBehaviour for later
mModelRecoBehaviour = modelRecoBehaviour;
}
void Update()
{
if (!VuforiaARController.Instance.HasStarted)
return;
if (mTargetFinder == null)
return;
// Check periodically if model target is tracked and in view
// The test is not necessary when the search is stopped after first model was found
float elapsed = Time.realtimeSinceStartup - mLastStatusCheckTime;
if (!StopSearchWhenModelFound && StopSearchWhileTracking && elapsed > 0.5f)
{
mLastStatusCheckTime = Time.realtimeSinceStartup;
if (mSearching)
{
if (IsModelTrackedInView(mLastRecoModelTarget))
{
// Switch Model Reco OFF when model is being tracked/in-view
mModelRecoBehaviour.ModelRecoEnabled = false;
mSearching = false;
}
}
else
{
if (!IsModelTrackedInView(mLastRecoModelTarget))
{
// Switch Mode Reco ON when no model is tracked/in-view
mModelRecoBehaviour.ModelRecoEnabled = true;
mSearching = true;
}
}
}
}
private void OnDestroy()
{
if (mModelRecoBehaviour != null)
{
mModelRecoBehaviour.UnregisterEventHandler(this);
}
mModelRecoBehaviour = null;
}
#endregion // UNITY_MONOBEHAVIOUR_METHODS
#region IModelRecoEventHandler_IMPLEMENTATION
/// <summary>
/// called when TargetFinder has been initialized successfully
/// </summary>
public void OnInitialized(TargetFinder targetFinder)
{
Debug.Log("ModelReco initialized.");
// Keep a reference to the Target Finder
mTargetFinder = targetFinder;
}
/// <summary>
/// visualize initialization errors
/// </summary>
public void OnInitError(TargetFinder.InitState initError)
{
// Reset target finder reference
mTargetFinder = null;
Debug.LogError("Model Reco init error: " + initError.ToString());
ShowErrorMessageInUI(initError.ToString());
}
/// <summary>
/// visualize update errors
/// </summary>
public void OnUpdateError(TargetFinder.UpdateState updateError)
{
Debug.LogError("Model Reco update error: " + updateError.ToString());
ShowErrorMessageInUI(updateError.ToString());
}
/// <summary>
/// when we start scanning, clear all trackables
/// </summary>
public void OnStateChanged(bool searching)
{
Debug.Log("ModelReco: state changed: " + (searching ? "searching" : "not searching"));
mSearching = searching;
if (searching)
{
// clear all known trackables
if (mTargetFinder != null)
mTargetFinder.ClearTrackables(false);
}
}
/// <summary>
/// Handles new search results.
/// </summary>
/// <param name="searchResult"></param>
public virtual void OnNewSearchResult(TargetFinder.TargetSearchResult searchResult)
{
Debug.Log("ModelReco: new search result available: " + searchResult.TargetName);
// Find or create the referenced model target
GameObject modelTargetGameObj = null;
bool builtFromTemplate = false;
var existingModelTarget = FindExistingModelTarget((TargetFinder.ModelRecoSearchResult)searchResult);
if (existingModelTarget)
{
modelTargetGameObj = existingModelTarget.gameObject;
builtFromTemplate = false;
}
else if (ModelTargetTemplate)
{
modelTargetGameObj = Instantiate(ModelTargetTemplate.gameObject);
builtFromTemplate = true;
}
if (!modelTargetGameObj)
{
Debug.LogError("Could not create a Model Target.");
return;
}
// Enable the new search result as a Model Target
ModelTargetBehaviour mtb = mTargetFinder.EnableTracking(
searchResult, modelTargetGameObj) as ModelTargetBehaviour;
if (mtb)
{
mLastRecoModelTarget = mtb;
// If the model target was created from a template,
// we augment it with a bounding box game object
if (builtFromTemplate && ShowBoundingBox)
{
var modelBoundingBox = mtb.ModelTarget.GetBoundingBox();
var bboxGameObj = CreateBoundingBox(mtb.ModelTarget.Name, modelBoundingBox);
// Parent the bounding box under the model target.
bboxGameObj.transform.SetParent(modelTargetGameObj.transform, false);
}
if (StopSearchWhenModelFound)
{
// Stop the target finder
mModelRecoBehaviour.ModelRecoEnabled = false;
}
}
}
#endregion // IModelRecoEventHandler_IMPLEMENTATION
#region PRIVATE_METHODS
private ModelTargetBehaviour FindExistingModelTarget(TargetFinder.ModelRecoSearchResult searchResult)
{
var modelTargetsInScene = Resources.FindObjectsOfTypeAll<ModelTargetBehaviour>().ToList().Where(mt => mt.ModelTargetType == ModelTargetType.PREDEFINED).ToArray();
if (modelTargetsInScene == null || modelTargetsInScene.Length == 0)
return null;
string targetName = searchResult.TargetName;
//string targetUniqueId = searchResult.UniqueTargetId;
foreach (var mt in modelTargetsInScene)
{
if (mt.TrackableName == targetName)
{
mt.gameObject.SetActive(true);
return mt;
}
}
return null;
}
private GameObject CreateBoundingBox(string modelTargetName, OrientedBoundingBox3D bbox)
{
var bboxGameObj = new GameObject(modelTargetName + "_BoundingBox");
bboxGameObj.transform.localPosition = bbox.Center;
bboxGameObj.transform.localRotation = Quaternion.identity;
bboxGameObj.transform.localScale = 2 * bbox.HalfExtents;
bboxGameObj.AddComponent<BoundingBoxRenderer>();
return bboxGameObj;
}
private void ShowErrorMessageInUI(string text)
{
if (ModelRecoErrorText)
ModelRecoErrorText.text = text;
}
public static Bounds GetModelTargetWorldBounds(ModelTargetBehaviour mtb)
{
var bbox = mtb.ModelTarget.GetBoundingBox();
var localCenter = bbox.Center;
var localExtents = bbox.HalfExtents;
// transform local center to World space
var worldCenter = mtb.transform.TransformPoint(localCenter);
// transform the local extents to World space
var axisX = mtb.transform.TransformVector(localExtents.x, 0, 0);
var axisY = mtb.transform.TransformVector(0, localExtents.y, 0);
var axisZ = mtb.transform.TransformVector(0, 0, localExtents.z);
Vector3 worldExtents = Vector3.zero;
worldExtents.x = Mathf.Abs(axisX.x) + Mathf.Abs(axisY.x) + Mathf.Abs(axisZ.x);
worldExtents.y = Mathf.Abs(axisX.y) + Mathf.Abs(axisY.y) + Mathf.Abs(axisZ.y);
worldExtents.z = Mathf.Abs(axisX.z) + Mathf.Abs(axisY.z) + Mathf.Abs(axisZ.z);
return new Bounds { center = worldCenter, extents = worldExtents };
}
private bool IsModelTrackedInView(ModelTargetBehaviour modelTarget)
{
if (!modelTarget)
return false;
if (modelTarget.CurrentStatus == TrackableBehaviour.Status.NO_POSE)
return false;
var cam = DigitalEyewearARController.Instance.PrimaryCamera;
if (!cam)
return false;
// Compute the center of the model in World coordinates
Bounds modelBounds = GetModelTargetWorldBounds(modelTarget);
var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(cam);
return GeometryUtility.TestPlanesAABB(frustumPlanes, modelBounds);
}
#endregion PRIVATE_METHODS
#region PUBLIC_METHODS
public TargetFinder GetTargetFinder()
{
return mTargetFinder;
}
public void ResetModelReco(bool destroyGameObjects)
{
var objectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
if (objectTracker != null)
{
objectTracker.Stop();
if (mTargetFinder != null)
{
mTargetFinder.ClearTrackables(destroyGameObjects);
mTargetFinder.Stop();
mTargetFinder.StartRecognition();
}
else
{
Debug.LogError("Could not reset TargetFinder");
}
objectTracker.Start();
}
else
{
Debug.LogError("Could not reset ObjectTracker");
}
}
#endregion // PUBLIC_METHODS
}