Google is committed to advancing racial equity for Black communities. See how.

Instant Placement developer guide for Android

Learn how to use the Instant Placement API in your own apps.

Configure a new session with Instant Placement

In a new ARCore session, enable Instant Placement mode.

// Create the ARCore session.
public void createSession() {
  session = new Session(this);
  Config config = new Config(session);
  // Set the Instant Placement mode.
  config.setInstantPlacementMode(InstantPlacementMode.LOCAL_Y_UP);
  session.configure(config);
}

Place an object

Use InstantPlacementPoint to create a trackable Instant Placement point. Retrieve the current pose with the getPose() method.

private placementIsDone = false;

public void onDrawFrame(GL10 gl) {
  Frame frame = session.update();

  // Place an object on tap.
  if (!placementIsDone && didUserTap()) {
    // Use estimated distance from the user's device to the real world, based
    // on expected user interaction and behavior.
    float approximateDistanceMeters = 1.0f;

    List<HitResult> results =
      frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
    if (!results.isEmpty()) {
      InstantPlacementPoint point = (InstantPlacementPoint) hit.getTrackable();
      anchor = point.createAnchor(point.getPose());
      placementIsDone = true;
      disableInstantPlacement();
    }
  }
}

Smooth the tracking method transition

When true depth becomes available, the distance will automatically correct, and the object will be placed at the correct position. The user will see the object grow or shrink. To avoid this sudden change, add an InstantPlacementPoint wrapper.

class WrappedInstantPlacement {
  public InstantPlacementPoint point;
  public InstantPlacementPoint.TrackingMethod previousTrackingMethod;
  public float previousDistanceToCamera;
  public float scaleFactor = 1.0f;

  public WrappedInstantPlacement(
      InstantPlacementPoint point,
      InstantPlacementPoint.TrackingMethod previousTrackingMethod,
      float previousDistanceToCamera) {
    this.point = point;
    this.previousTrackingMethod = previousTrackingMethod;
    this.previousDistanceToCamera = previousDistanceToCamera;
  }
}

Then, add the following to your activity's onDrawFrame:

  List<WrappedInstantPlacement> wrappedPoints = new ArrayList<>();

  public void onDrawFrame(GL10 gl) {
    Frame frame = session.update();

    // Place an object on tap.
    if (didUserTap()) {
      // Use the estimated distance from the user's device to the closest
      // available surface, based on expected user interaction and behavior.
      float approximateDistanceMeters = 2.0f;

      // Returns a single result if the hit test was successful.
      List<HitResult> results = frame.hitTestInstantPlacement(tapX, tapY,
          approximateDistanceMeters);
      for (HitResult hit : results) {
        InstantPlacementPoint point = (InstantPlacementPoint) hit.getTrackable();
        wrappedPoints.add(new WrappedInstantPlacement(point, point.getTrackingMethod(),
          distance(camera.getPose(), point.getPose())));
      }
    }

    for (WrappedInstantPlacement wrappedPoint : wrappedPoints) {
      InstantPlacementPoint point = wrappedPoint.point;
      if (point.getTrackingState() == TrackingState.STOPPED) {
        wrappedPoints.remove(wrappedPoint);
        continue;
      }
      if (point.getTrackingState() == TrackingState.PAUSED) {
        continue;
      }

      if (point.getTrackingMethod() == InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
         // Continue to use the estimated depth and pose. Record the distance to
         // the camera for use in the next frame if the transition to full
         // tracking happens.
         wrappedPoint.previousDistanceToCamera = distance(point.getPose(), camera.getPose());
      }
      else if (point.getTrackingMethod() == InstantPlacementPoint.TrackingMethod.FULL_TRACKING) {
        if (wrappedPoint.previousTrackingMethod == InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
          // Change from the estimated pose to the accurate pose. Adjust the
          // object scale to counteract the apparent change due to pose jump.
          wrappedPoint.scaleFactor = distance(point.getPose(), camera.getPose()) /
              wrappedPoint.previousDistanceToCamera;
          // Apply the scale factor to the model.
          // ...
          wrappedPoint.previousTrackingMethod = TrackingMethod.FULL_TRACKING;
        }
      }
    }
  }

  float distance(Pose p, Pose q) {
    return Math.sqrt(Math.pow(p.tx() - q.tx(), 2) + Math.pow(p.ty() - q.ty(), 2) + Math.pow(p.tz() - q.tz(), 2));
  }

Increase efficiency after object placement

Disable Instant Placement when the object is correctly placed to save CPU cycles and power.

private void disableInstantPlacement() {
  Config config = new Config(session);
  config.setInstantPlacementMode(InstantPlacementMode.DISABLED);
  session.configure(config);
}