Ground Plane Native User Guide

In this article, the steps to configure and initialize the Vuforia Ground Plane feature using the Vuforia C++ API is demonstrated. We present here the main components that are required for a functional Ground Plane application.

The main components of a Ground Plane app include the initialization of the DeviceTracker and SmartTerrain, performing Hit Tests and the creation of Anchors.

Project Organization

Set up your development environment with the latest Vuforia Engine.

  • Download the Vuforia Engine SDK for iOS, Android, and UWP here
  • Unpack the SDK into your development environment

NOTE:  Ground Plane is only compatible with devices supported by Platform Enablers (ARKit/ARCore) or devices that have been specifically calibrated by Vuforia Engine. See Ground Plane Recommended Devices for a list of devices that are expected to provide the best experience.

Project Structure

For a general introduction to the project structure, we recommend consulting the Native Vuforia Engine Sample article as well as the Ground Plane API Overview that describes the app lifecycle.

Refer to the initAR()and startAR() methods in to see how a Vuforia Engine session is initialized and started.

In order to use the Ground Plane feature, the SmartTerrain tracker needs to be initialized in addition to the Device Tracker in initTrackers():

// Initialize positional device and SmartTerrain trackers
Vuforia::TrackerManager& trackerManager = Vuforia::TrackerManager::getInstance();

// Initialize DeviceTracker:
Vuforia::Tracker* deviceTracker = trackerManager.initTracker(Vuforia::PositionalDeviceTracker::getClassType());

// Initialize SmartTerrain:
Vuforia::Tracker* smartTerrain = trackerManager.initTracker(Vuforia::SmartTerrain::getClassType());

Similarly, the SmartTerrain Tracker needs to be started alongside the Device Tracker in startTrackers().

HitTests and Anchors

Once the Vuforia Engine is active and the Trackers have been initiated, we can perform a HitTest to create an Anchor that is suitable for content placement on the registered surface or at mid-air:

const auto& hitTestResults = smartTerrain->hitTest(hitTestPoint, hitTestHint, state, deviceHeightInMeters);
const auto& numResults = hitTestResults.size();
LOG("Number of hit test results: %d, create anchors: %d", numResults, createAnchor);
bool success = true;

// Use first HitTestResult to create anchor (closest):
++mHitTestAnchorCounter;
const std::string& name = HITTESTANCHOR_NAME + std::to_string(mHitTestAnchorCounter);
const auto& hitTestAnchor = positionalDeviceTracker->createAnchor(name.c_str(), *hitTestResult);
if (hitTestAnchor != nullptr)
{
    LOG("Successfully created hit test anchor with name '%s'", hitTestAnchor->getName());
    mHitTestAnchors.push_back(hitTestAnchor);
    
    if (mHitTestAnchors.size() > MAX_HIT_TEST_ANCHORS)
    {
        LOG("Maximum number of hit test anchors reached. Will destroy oldest anchor.");
        if (!positionalDeviceTracker->destroyAnchor(mHitTestAnchors.front()))
        {
            LOG("Failed to destroy hit test anchor");
        }
        mHitTestAnchors.pop_front();
    }
}
const auto& anchors = positionalDeviceTracker->getAnchors();

In the next code snippet, we create Anchor Points mid-air:

// Create free-floating anchor:
Vuforia::Matrix34F anchorPose;

// Use last cached device pose as free-floating anchor pose
anchorPose = mRenderData.freeFloatingAnchorPose;

// Create free-floating anchor at current device pose
++mFreeFloatingAnchorCounter;
const std::string& name = FREEFLOATINGANCHOR_NAME + std::to_string(mFreeFloatingAnchorCounter);
Vuforia::Anchor* freeFloatingAnchor = positionalDeviceTracker->createAnchor(name.c_str(), anchorPose);
if (freeFloatingAnchor != nullptr)
{
    LOG("Successfully created free-floating anchor with name '%s'", freeFloatingAnchor->getName());
    mFreeFloatingAnchors.push_back(freeFloatingAnchor);
}
const auto& anchors = positionalDeviceTracker->getAnchors();

Next, you should check the Trackable Results obtained from the trackable state and use the results to register and render digital content. Start by getting the trackable’s result with getTrackableResult() and pose with getPose(). Then, check for the trackable’s type with getClassType() and finally call to render on the position of the trackable.

for (const auto& trackableResult : trackableResultList)
{
    // Get the trackable and retrieve pose:
    const Vuforia::Trackable& trackable = trackableResult->getTrackable();
    Vuforia::Matrix44F poseMatrix = Vuforia::Tool::convertPose2GLMatrix(trackableResult->getPose());
    
    if (trackableResult->isOfType(Vuforia::AnchorResult::getClassType()))
    {
        //Call to render on Anchor pose
        //mMidAirPoseMatrix = modelViewMatrix;
    }
    //...
}

For rendering models and digital content, refer to the native samples’ Renderer which shows how to render a bounding box around a trackable Vuforia Target. 

For the remaining cycle of deinitializing and unloading the data, we recommend to also consult the Native Sample’s use of deInitAR()stopAR() and deInitTrackers() on their proper usage.