I have an AR project built with Vuforia in Unity. When an interruption is made (phone-call, low battery warning, ect), the app crashes, in various ways - the Camera disables, or wont clear gui, and sometimes just seems to freeze the entire project. I’ve narrowed down the problem to the AR Camera. I assume its located on the QCarBehavior Script
Has anyone had a similar problem and found a way to resolve this?
please forgive my commented out code mess
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using UnityEngine;
// The QCARBehaviour class handles tracking and triggers native video
// background rendering. The class updates all Trackables in the scene.
[RequireComponent(typeof(Camera))]
public class QCARBehaviour : MonoBehaviour
{
#region NESTED
// void Awake(){
//
// mClearBuffers = 12;
// }
// The world center mode defines how the relative coordinates between
// Trackables and camera are translated into Unity world coordinates.
// If a world center is present the virtual camera in the Unity scene is
// transformed with respect to that.
// The world center mode is set through the Unity inspector.
public enum WorldCenterMode
{
// User defines a single Trackable that defines the world center.
USER,
// Tracker uses the first Trackable that comes into view as the world
// center (world center changes during runtime).
AUTO,
// Do not define a world center but only move Trackables with respect
// to a fixed camera.
NONE
}
// State of the camera.
private enum CameraState
{
UNINITED, // Camera is not yet initialized.
DEVICE_INITED, // Camera device is initialized.
RENDERING_INITED // Video rendering is initialized.
}
#endregion // NESTED
#region PROPERTIES
// This property is used to query the active world center mode.
public WorldCenterMode WorldCenterModeSetting
{
get { return mWorldCenterMode; }
}
// This property is used to query the world center Trackable
// (will return null in "NONE" mode).
public TrackableBehaviour WorldCenter
{
get { return mWorldCenter; }
}
#endregion // PROPERTIES
#region PRIVATE_MEMBER_VARIABLES
[SerializeField]
private CameraDevice.CameraDeviceMode CameraDeviceModeSetting =
CameraDevice.CameraDeviceMode.MODE_DEFAULT;
[SerializeField]
private int MaxSimultaneousImageTargets = 1;
// split detection of multiple targets over multiple frames
[SerializeField]
private bool MultiFrameEnabled = true;
// tie the framerate to the camera framerate
[SerializeField]
private bool SynchronousVideo = false;
[SerializeField]
[HideInInspector]
private WorldCenterMode mWorldCenterMode = WorldCenterMode.AUTO;
[SerializeField]
[HideInInspector]
private TrackableBehaviour mWorldCenter = null;
private List<ITrackerEventHandler> mTrackerEventHandlers =
new List<ITrackerEventHandler>();
private bool mIsInitialized = false;
private CameraState mCameraState = CameraState.UNINITED;
private Material mClearMaterial;
private Rect mViewportRect;
private int mClearBuffers;
private bool mHasStartedOnce = false;
private ScreenOrientation mProjectionOrientation = ScreenOrientation.Unknown;
private bool mCachedDrawVideoBackground;
private CameraClearFlags mCachedCameraClearFlags;
private Color mCachedCameraBackgroundColor;
#endregion // PRIVATE_MEMBER_VARIABLES
#region PUBLIC_METHODS
// This method registers a new Tracker event handler at the Tracker.
// These handlers are called as soon as ALL Trackables have been updated
// in this frame.
public void RegisterTrackerEventHandler(
ITrackerEventHandler trackerEventHandler)
{
mTrackerEventHandlers.Add(trackerEventHandler);
}
// This method unregisters a Tracker event handler.
// Returns "false" if event handler does not exist.
public bool UnregisterTrackerEventHandler(
ITrackerEventHandler trackerEventHandler)
{
return mTrackerEventHandlers.Remove(trackerEventHandler);
}
// This method is used to set the world center mode in the Unity editor.
// Switching modes is not supported at runtime.
public void SetWorldCenterMode(WorldCenterMode value)
{
if (!Application.isEditor)
return;
mWorldCenterMode = value;
}
// This method is used to set the world center in the Unity editor in
// "USER" mode. Switching modes is not supported at runtime.
public void SetWorldCenter(TrackableBehaviour value)
{
if (!Application.isEditor)
return;
mWorldCenter = value;
}
// This method simply returns the viewport rectangle.
public Rect GetViewportRectangle()
{
return mViewportRect;
}
// This method returns the surface orientation.
public ScreenOrientation GetSurfaceOrientation()
{
return (ScreenOrientation)getSurfaceOrientation();
}
#endregion // PUBLIC_METHODS
#region UNITY_MONOBEHAVIOUR_METHODS
// Starts up the QCAR extension with the properties that were set in the
// Unity inspector.
void Start()
{
// First we check if QCAR initialized correctly.
if (QCAR.CheckInitializationError() != QCAR.InitError.INIT_SUCCESS)
{
mIsInitialized = false;
return;
}
// Initialize the trackers if they haven't already been initialized.
if (TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER) == null)
{
TrackerManager.Instance.InitTracker(Tracker.Type.MARKER_TRACKER);
}
if (TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER) == null)
{
TrackerManager.Instance.InitTracker(Tracker.Type.IMAGE_TRACKER);
}
// Cache the camera start values
mCachedDrawVideoBackground = QCARManager.Instance.DrawVideoBackground;
mCachedCameraClearFlags = this.camera.clearFlags;
mCachedCameraBackgroundColor = this.camera.backgroundColor;
// Reset the camera clear flags and create a simple material
ResetCameraClearFlags();
mClearMaterial = new Material(Shader.Find("Diffuse"));
// Set QCAR hints from the Inspector options
QCAR.SetHint(QCAR.QCARHint.HINT_MAX_SIMULTANEOUS_IMAGE_TARGETS,
MaxSimultaneousImageTargets);
QCAR.SetHint(QCAR.QCARHint.HINT_IMAGE_TARGET_MULTI_FRAME_ENABLED,
MultiFrameEnabled ? 1 : 0);
// Set the Unity version for internal use
QCAR.SetUnityVersion();
// Find markers in the scene and register them with QCAR
MarkerBehaviour[] markerBehaviours = (MarkerBehaviour[])
UnityEngine.Object.FindObjectsOfType(typeof(MarkerBehaviour));
MarkerTracker markerTracker = (MarkerTracker)TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER);
if (markerTracker != null)
{
markerTracker.AddMarkers(markerBehaviours);
}
// Start the camera and tracker
StartQCAR();
// Initialize the QCARManager
QCARManager.Instance.WorldCenterMode = mWorldCenterMode;
QCARManager.Instance.WorldCenter = mWorldCenter;
QCARManager.Instance.ARCamera = camera;
QCARManager.Instance.Init();
// Initialize local variables
mIsInitialized = true;
mHasStartedOnce = true;
}
// Restart the camera and tracker if the QCARBehaviour is reenabled.
// Note that we check specifically that Start() has been called once
// where QCAR is fully initialized.
void OnEnable()
{
if (mHasStartedOnce)
{
StartQCAR();
}
}
// Updates the scene with new tracking data. Calls registered
// ITrackerEventHandlers
void Update()
{
if (!mIsInitialized)
return;
// Get the current orientation of the surface:
ScreenOrientation surfaceOrientation = (ScreenOrientation)getSurfaceOrientation();
// Check if we need to update the video background configuration and projection matrix:
if (QCAR.IsRendererDirty() || mProjectionOrientation != surfaceOrientation)
{
ConfigureVideoBackground();
UpdateProjection(surfaceOrientation);
}
// Bind a simple material to clear the OpenGL state
mClearMaterial.SetPass(0);
// QCARManager renders the camera image and updates the trackables
QCARManager.Instance.Update(mProjectionOrientation);
// Tell Unity that we may have changed the OpenGL state behind the scenes
GL.InvalidateState();
// Update the camera clear flags
UpdateCameraClearFlags();
// Let the trackable event handlers know that all trackables have been updated
foreach (ITrackerEventHandler handler in mTrackerEventHandlers)
{
handler.OnTrackablesUpdated();
}
}
// Called after the scene has been rendered
void OnPostRender()
{
// Clear the framebuffer as many times as defined upon init
if (mClearBuffers > 0)
{
GL.Clear(false, true, new Color(0.0f, 0.0f, 0.0f, 1.0f));
mClearBuffers--;
}
}
// Stops QCAR when the application is sent to the background.
void OnApplicationPause(bool pause)
{
// if(pause){
// //Application.LoadLevel(1);
// }
// else{
// //StartQCAR();
// //mClearBuffers = 12;
// }
if (pause)
{
StopQCAR();
// On iOS make sure suspend/resume doesn't animate an old image
#if UNITY_IPHONE
GL.Clear(false, true, new Color(0.0f, 0.0f, 0.0f, 1.0f));
#endif
}
else
{
StartQCAR();
// Clear any artifacts from the buffers on resume
mClearBuffers = 12;
}
}
// Stop the tracker and camera when QCARBehaviour is disabled.
void OnDisable()
{
StopQCAR();
ResetCameraClearFlags();
}
// Deinitialize QCAR and trackers when QCARBehaviour is destroyed.
void OnDestroy()
{
// Destroy all the datasets
ImageTracker imageTracker = (ImageTracker)TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER);
if (imageTracker != null)
{
imageTracker.DestroyAllDataSets(false);
}
// Destroy all the markers
MarkerTracker markerTracker = (MarkerTracker)TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER);
if (markerTracker != null)
{
markerTracker.DestroyAllMarkers(false);
}
// Deinit the QCARManager
QCARManager.Instance.Deinit();
// Deinit the trackers
if (TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER) != null)
{
TrackerManager.Instance.DeinitTracker(Tracker.Type.MARKER_TRACKER);
}
if (TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER) != null)
{
TrackerManager.Instance.DeinitTracker(Tracker.Type.IMAGE_TRACKER);
}
}
#endregion // UNITY_MONOBEHAVIOUR_METHODS
#region PRIVATE_METHODS
// Start the camera and trackers and update the camera projection
private void StartQCAR()
{
Debug.Log("StartQCAR");
CameraDevice.Instance.Init();
ConfigureVideoBackground();
CameraDevice.Instance.SelectVideoMode(CameraDeviceModeSetting);
CameraDevice.Instance.Start();
if (TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER) != null)
{
TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER).Start();
}
if (TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER) != null)
{
TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER).Start();
}
ScreenOrientation surfaceOrientation = (ScreenOrientation)getSurfaceOrientation();
UpdateProjection(surfaceOrientation);
}
// Stop the trackers, stop and deinit the camera
private void StopQCAR()
{
Debug.Log("StopQCAR");
if (TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER) != null)
{
TrackerManager.Instance.GetTracker(Tracker.Type.MARKER_TRACKER).Stop();
}
if (TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER) != null)
{
TrackerManager.Instance.GetTracker(Tracker.Type.IMAGE_TRACKER).Stop();
}
CameraDevice.Instance.Stop();
CameraDevice.Instance.Deinit();
}
// Reset the camera state and clear any artifacts from the buffers
private void ResetCameraClearFlags()
{
mCameraState = CameraState.UNINITED;
mClearBuffers = 12;
// Restore clear settings to Inspector values
this.camera.clearFlags = mCachedCameraClearFlags;
this.camera.backgroundColor = mCachedCameraBackgroundColor;
}
// Update the camera clear flags and background color in response
// to QCAR settings
private void UpdateCameraClearFlags()
{
// Specifically handle when running in the editor:
if (Application.isEditor)
{
mCameraState = CameraState.UNINITED;
return;
}
// Update camera clear flags if necessary
switch (mCameraState)
{
case CameraState.UNINITED:
mCameraState = CameraState.DEVICE_INITED;
break;
case CameraState.DEVICE_INITED:
// Check whether QCAR requires a transparent clear color
if (QCAR.RequiresAlpha())
{
// Camera clears both depth and color buffer,
// We set the clear color to transparent black as
// required by QCAR.
this.camera.clearFlags = CameraClearFlags.SolidColor;
this.camera.backgroundColor = new Color(0, 0, 0, 0);
Debug.Log("Setting camera clear flags to transparent black");
}
else
{
// Check whether QCAR is rendering the video in native
if (mCachedDrawVideoBackground)
{
// Clear only depth
this.camera.clearFlags = CameraClearFlags.Depth;
Debug.Log("Setting camera clear flags to depth only");
}
else
{
// Restore clear settings to Inspector values
this.camera.clearFlags = mCachedCameraClearFlags;
this.camera.backgroundColor = mCachedCameraBackgroundColor;
Debug.Log("Setting camera clear flags to Inspector values");
}
}
mCameraState = CameraState.RENDERING_INITED;
break;
case CameraState.RENDERING_INITED:
// Check if the DrawVideoBackground setting has changed
bool drawVideoBackground = QCARManager.Instance.DrawVideoBackground;
if (drawVideoBackground != mCachedDrawVideoBackground)
{
mCameraState = CameraState.DEVICE_INITED;
mCachedDrawVideoBackground = drawVideoBackground;
}
break;
}
}
// Configure the size and position of the video background rendered
// natively when QCARManager.DrawVideoBackground is true
private void ConfigureVideoBackground()
{
QCARRenderer.VideoBGCfgData config = QCARRenderer.Instance.GetVideoBackgroundConfig();
CameraDevice.VideoModeData videoMode = CameraDevice.Instance.GetVideoMode(CameraDeviceModeSetting);
config.enabled = 1;
config.synchronous = (SynchronousVideo ? 1 : 0);
config.position = new QCARRenderer.Vec2I(0, 0);
if (Screen.width > Screen.height)
{
float height = videoMode.height * (Screen.width / (float)
videoMode.width);
config.size = new QCARRenderer.Vec2I(Screen.width, (int) height);
if (config.size.y < Screen.height)
{
// Correcting rendering background size to handle missmatch
// between screen and video aspect ratios
config.size.x = (int) (Screen.height
* (videoMode.width / (float) videoMode.height));
config.size.y = Screen.height;
}
}
else
{
float width = videoMode.height * (Screen.height / (float)
videoMode.width);
config.size = new QCARRenderer.Vec2I((int) width, Screen.height);
if (config.size.x < Screen.width)
{
// Correcting rendering background size to handle missmatch
// between screen and video aspect ratios
config.size.x = Screen.width;
config.size.y = (int) (Screen.width *
(videoMode.width / (float) videoMode.height));
}
}
QCARRenderer.Instance.SetVideoBackgroundConfig(config);
int viewportX = config.position.x + (Screen.width - config.size.x) / 2;
int viewportY = config.position.y + (Screen.height - config.size.y) / 2;
mViewportRect = new Rect(viewportX, viewportY,
config.size.x, config.size.y);
}
// Update the camera projection matrix to match QCAR settings
private void UpdateProjection(ScreenOrientation orientation)
{
if (Application.isEditor)
{
// Skip this when running in the editor
return;
}
// This member stores the screen orientation used for the last projection matrix
// update. It is used to counter rotate the poses later.
mProjectionOrientation = orientation;
Matrix4x4 projectionMatrix = QCAR.GetProjectionGL(camera.nearClipPlane,
camera.farClipPlane, mProjectionOrientation);
if (mViewportRect.width != Screen.width)
{
float viewportDistort = mViewportRect.width / Screen.width;
projectionMatrix[0] *= viewportDistort;
}
if (mViewportRect.height != Screen.height)
{
float viewportDistort = mViewportRect.height / Screen.height;
projectionMatrix[5] *= viewportDistort;
}
this.camera.projectionMatrix = projectionMatrix;
}
#endregion // PRIVATE_METHODS
#region NATIVE_FUNCTIONS
#if !UNITY_EDITOR
[DllImport(QCARMacros.PLATFORM_DLL)]
private static extern int getSurfaceOrientation();
#else
private static int getSurfaceOrientation() { return 0; }
#endif
#endregion // NATIVE_FUNCTIONS
}