Vuforia Engine Lifecycle in Native

This page shows a step-by-step introduction to configuring and managing the Engine and application lifecycle.

The general workflow for Vuforia Engine may look like this:

  • Configure and create Engine
  • Configure and create Observer
  • Parse the State and retrieve Observations
  • Render augmentations based on Observations

Manage Engine Lifecycle

All Vuforia applications begin by creating and starting the Vuforia Engine. Only one Engine instance can be created at a time. Stop and destroy the Engine before creating another.

VuEngine* engine = NULL; 

// Create an engine instance using the default parameters 
vuEngineCreate(&engine, NULL, NULL); 

// Start the engine 
vuEngineStart(engine); 

// Stop the engine 
vuEngineStop(engine); 

// Destroy the engine 
vuEngineDestroy(engine); 

Always ensure that create methods are concluded with a destroy call to free up memory. Similarly, acquire methods need to be accompanied by a release call.

Add the Vuforia Engine lifecycle to your mobile lifecycle similar to this: 

  • Initialize and create app: Configure and create vuEngineCreate
  • Start, resume app and execute primary app logic: Add additional configurations and start vuEngineStart
  • During pause and background app logic: Stop vuEngineStop
  • Deinitialize and free memory in app: Destroy vuEngineDestroy

Configure the Engine and acquire the State 

Add basic configurations to the Engine such as a Vuforia Developer license key. 

void basic_engine_config() 
 { 
     // Create a configuration set 
     VuEngineConfigSet* configSet = NULL; 

     vuEngineConfigSetCreate(&configSet); 

     vuEngineCreate(&engine, configSet, NULL);

     // Create the license configuration element 
     VuLicenseConfig licenseConfig = { 0 }; 
     licenseConfig.key = "1234567890"; 

     // Add the license configuration to the set 
     vuEngineConfigSetAddLicenseConfig(configSet, &licenseConfig); 

     // Add more configuration elements 
     // ... 
 } 

Similar to Engine, destroy the configuration when finished with the AR session (by calling vuEngineConfigSetDestroy(configSet)). 

Acquire State 

After you have started the Engine, you can start to pull updates from the state: 

// Get update from Engine via pull mechanism: get latest state 
VuState* state = NULL; 
vuEngineAcquireLatestState(engine, &state); 

And when done,  

// Release the state 
vuStateRelease(state); 

Or alternatively, get updates with a push mechanism: 

// Get update from Engine via push mechanism: register a callback 
vuEngineRegisterStateHandler(engine, myHandler, NULL); 

Where myHandler is a method in which the state is retrieved and contained. A StateHandler can be unregistered by providing a NULL as the second parameter to the method. The third parameter is the client data that you pass with the state updates.  

vuEngineRegisterStateHandler(engine, NULL, NULL); 

NOTE: The callack that you register is running in the camera thread. You should therefore not assign heavy processes in this method. The callback should only be used for doing critical operations such as Computer Vision tasks and should not process tasks such as UI updates. Please refer to the Vuforia Native Samples that demonstrates best practice on thread handling. 

Create an Observer 

An Observer is created with a configuration structure designated to the Observer type. In most cases, this includes a databasePath and a targetName from a database.  By default an Observer is automatically activated (i.e. will start processing after creation). In this example, we create a simple Observer for an Image Target: 

// Create an image target config 
VuImageTargetConfig imageTargetConfig = vuImageTargetConfigDefault(); 
imageTargetConfig.databasePath = "StonesAndChips.xml"; 
imageTargetConfig.targetName = "stones"; 

// Create an Image Target Observer 
VuObserver* imageTargetObserver = NULL; 
vuEngineCreateImageTargetObserver(engine, &imageTargetObserver, imageTargetConfig, NULL); 

Add more configurations on your own initiative to set its scale, pose offset or control whether it should be activated upon creation. For other target types, more configurations may be necessary to add. If you significantly modify Observers via setters, which share the same database, it is generally recommended to deactivate the Observers during this operation for performance reasons. You can do that either by deactivating the activation in the Observer configuration or by calling vuObserverDeactivate(observer)/vuObserverActivate(observer). Refer to the native API overviews of each Vuforia feature to learn more.  

Since we have created the Observer for an Image Target, we also need to destroy it at the end of the AR session. 

// Destroy the observer 
vuObserverDestroy(imageTargetObserver); 

Parse the State and Retrieve Observations 

For our Image Target Observer, we can now retrieve information about its status by requesting it from the state by using the following function: 

// Create observation list
VuObservationList* obsList = NULL; 
vuObservationListCreate(&obsList); 

// Get and parse image target observations list 
 vuStateGetImageTargetObservations(state,obsList); 

Or, get the Observation directly from the Observer which is particular suitable for updating Observer individually which may also be more manageable.  

// Get and parse image target observations list from the
vuStateGetObservationsByObserver(state, imageTargetObserver, obsList); 

You can then parse the list by getting the list size and retrieve each element via a get method,  vuObservationListGetElement. From there you can parse all the info from your observation, such as pose info, target info (or status info).  

int32_t listSize = 0;
vuObservationListGetSize(obsList, &listSize);

// Parse the image target list
     for (int i = 0; i < listSize; i++)
     {
             VuObservation* obs = NULL;
             vuObservationListGetElement(obsList, i, &obs);
    
             VuImageTargetObservationTargetInfo targetInfo;
             vuImageTargetObservationGetTargetInfo(obs, &targetInfo);

             VuImageTargetObservationStatusInfo statusInfo;
             vuImageTargetObservationGetStatusInfo(obs, &statusInfo);

             VuPoseInfo poseInfo;
             vuObservationGetPoseInfo(obs, &poseInfo);
   
             if (poseInfo.poseStatus != OBSERVATION_POSE_STATUS_NO_POSE)
             {
                     // Do something with poseInfo and targetInfo
             }
         }

As an alternative, get all observations from the state and parse them type by type.  

// Get update from Engine via pull mechanism: get latest state
VuState* state = NULL;
vuEngineAcquireLatestState(engine, &state);

// Parse the state

// Create observation list
VuObservationList* obsList = NULL;
vuObservationListCreate(&obsList);
vuStateGetObservations(state, obsList);
int32_t listSize = 0;
vuObservationListGetSize(obsList, &listSize);

// Parse the list
for (int i = 0; i < listSize; i++)
     {
         VuObservation* obs = NULL;
         vuObservationListGetElement(obsList, i, &obs);

         if (vuObservationIsType(obs, VU_OBSERVATION_IMAGE_TARGET_TYPE) == VU_TRUE)
         {
                 VuImageTargetObservationTargetInfo targetInfo;
                 vuImageTargetObservationGetTargetInfo(obs, &targetInfo);

                 VuPoseInfo poseInfo;
                 vuObservationGetPoseInfo(obs, &poseInfo);    
    
                 if (poseInfo.poseStatus != OBSERVATION_POSE_STATUS_NO_POSE)
                 {
                         // Do something with poseInfo and targetInfo
                 
         }
         // Else if ..
     }

Again, make sure to free up the memory by destroying the created vuObservationList

// Destroy observation list
vuObservationListDestroy(obsList);

// Release the state
vuStateRelease(state);

You can find more information in Observer and Observations.

Render Content Based on Status 

Use the retrieved pose from the Observation of an Observer and render content in relation to the pose information of the target by using the RenderState. The RenderState is a component from the state that provides access to the projection matrix, view matrix, viewport, and background video mesh. See Rendering in Native for more information.  

Based on the observation statuses, a cube is rendered with the VuRenderState with respect to the pose information. The code example below is the assembly of the snippets from the above steps. 

void basic_image_target_update_render()
{
     VuEngine* engine = NULL;

     // Create an engine using the default parameters
     vuEngineCreate(&engine, NULL, NULL);

     // Create an image target config
     VuImageTargetConfig imageTargetConfig = vuImageTargetConfigDefault();
     imageTargetConfig.databasePath = "StonesAndChips.xml";
     imageTargetConfig.targetName = "stones";

     // Create an Image Target Observer
     VuObserver* imageTargetObserver = NULL;
     vuEngineCreateImageTargetObserver(engine, &imageTargetObserver, imageTargetConfig, NULL);

     // Start the engine
     vuEngineStart(engine);

     // Get update from Engine via pull mechanism: get latest state
     VuState* state = NULL;
     vuEngineAcquireLatestState(engine, &state);

     // Parse the state

     // Get Render State
     VuRenderState renderState;
     vuStateGetRenderState(state, &renderState);

     // Create observation list
     VuObservationList* obsList = NULL;
     vuObservationListCreate(&obsList);
     vuStateGetObservations(state, obsList);
     int32_t listSize = 0;
     vuObservationListGetSize(obsList, &listSize);

     // Parse the it list
     for (int i = 0; i < listSize; i++)
     {
         VuObservation* obs = NULL;
         vuObservationListGetElement(obsList, i, &obs);

         VuImageTargetObservationTargetInfo targetInfo;
         vuImageTargetObservationGetTargetInfo(obs, &targetInfo);

         VuPoseInfo poseInfo;
         vuObservationGetPoseInfo(obs, &poseInfo);

         if (poseInfo.poseStatus != OBSERVATION_POSE_STATUS_NO_POSE)
         {
             renderCube(renderState.projectionMatrix, poseInfo.pose,
                             vuVector2FToVector3F(targetInfo.size));
         }
     }
         //else if ..

     // Destroy observation list
     vuObservationListDestroy(obsList);

     // Release the state
     vuStateRelease(state);

     // Destroy the observer
     vuObserverDestroy(imageTargetObserver);

     // Stop the engine
     vuEngineStop(engine);

     // Destroy the engine
     vuEngineDestroy(engine);
}

void renderCube(VuMatrix44F projMatrix, VuMatrix44F poseMatrix, VuVector3F scale)
 {
     //
 }

Error Handling 

The Vuforia Engine API promotes implementing proper error handling in your application. This applies for example to creation and configuration calls. You would therefore need to add error handling in the above code examples to Engine creation, adding a license, Observer creation, and creation of Observation list. The Vuforia Engine API has various error codes specific to the Observer, Engine configuration, and platform, which are helpful in validation activities during the development process and end usage. Please refer to the API reference library for the complete list of error codes.  

Error management example for Engine creation: 

VuEngine* engine = NULL;

VuErrorCode error;

// Create an engine using default parameters
if (vuEngineCreate(&engine, NULL, &error) != VU_SUCCESS)
{
    VU_LOG_ERROR("failed to create a vuforia engine", error);
    return -1;
}

Error management example for Observer creation: 

VuImageTargetCreationError imageTargetCreationError;

// Create Image Target Observer
if (vuEngineCreateImageTargetObserver(engine, &imageTargetObserver, imageTargetConfig, &imageTargetCreationError) != VU_SUCCESS)
{
        LOG("Error creating image target observer: 0x%02x", imageTargetCreationError);
    return -1;
}