DefaultModelRecoEventHandler.cs
10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/*==============================================================================
Copyright (c) 2019 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>
/// 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 || mLastRecoModelTarget == 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;
var existingModelTarget = FindExistingModelTarget((TargetFinder.ModelRecoSearchResult)searchResult);
if (existingModelTarget)
{
modelTargetGameObj = existingModelTarget.gameObject;
}
if (!modelTargetGameObj)
{
modelTargetGameObj = new GameObject("ModelTarget_" + searchResult.TargetName);
}
// Enable the new search result as a Model Target
ModelTargetBehaviour mtb = mTargetFinder.EnableTracking(
searchResult, modelTargetGameObj) as ModelTargetBehaviour;
if (mtb)
{
mLastRecoModelTarget = mtb;
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>();
if (modelTargetsInScene.Length == 0)
return null;
string targetName = searchResult.TargetName;
foreach (var mt in modelTargetsInScene)
{
if (mt.TrackableName == targetName)
{
mt.gameObject.SetActive(true);
return mt;
}
}
return null;
}
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
}