InstantPlacementPoint

public class InstantPlacementPoint

Trackable Instant Placement point returned by Frame.hitTestInstantPlacement(float, float, float).

If ARCore has an accurate 3D pose for the InstantPlacementPoint returned by Frame.hitTestInstantPlacement(float, float, float) it will start with tracking method InstantPlacementPoint.TrackingMethod.FULL_TRACKING. Otherwise, it will start with tracking method InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE, and will transition to InstantPlacementPoint.TrackingMethod.FULL_TRACKING once ARCore has an accurate 3D pose. Once the tracking method is InstantPlacementPoint.TrackingMethod.FULL_TRACKING it will not revert to InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE.

When the tracking method changes from InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE in one frame to InstantPlacementPoint.TrackingMethod.FULL_TRACKING in the next frame, the pose will jump from its initial location based on the provided approximate distance to a new location at an accurate distance.

This instantaneous change in pose will change the apparent scale of any objects that are anchored to the InstantPlacementPoint. That is, an object will suddenly appear larger or smaller than it was in the previous frame.

To avoid the visual jump due to the sudden change in apparent object scale, use the following procedure:

  1. Keep track of the pose and tracking method of the InstantPlacementPoint in each frame.
  2. Wait for the tracking method to change to InstantPlacementPoint.TrackingMethod.FULL_TRACKING.
  3. Use the pose from the previous frame and the pose in the current frame to determine the object's distance to the device in both frames.
  4. Calculate the apparent change in scale due to the change in distance from the camera.
  5. Adjust the scale of the object to counteract the perceived change in scale, so that visually the object does not appear to change in size.
  6. Optionally, smoothly adjust the scale of the object back to its original value over several frames.

The following snippet shows how to use Instant Placement to place one object. The wrapper below shows how to use steps 1 through 5 to avoid the visual jump.

class ArPlacementActivity {
   private Session session = null;
   private placementIsDone = false;
   public void createSession() {
     session = new Session(this);
     Config config = new Config(session);
     config.setInstantPlacementMode(InstantPlacementMode.LOCAL_Y_UP);
     session.configure(config);
   }
   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 how we expect the app to be used by users.
       float approximateDistanceMeters = 1.0f;
       List<HitResult> results =
          frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
       if (!results.isEmpty()) {
         HitResult hit = results.get(0);
         InstantPlacementPoint point = (InstantPlacementPoint) hit.getTrackable();
         anchor = point.createAnchor(point.getPose());
         placementIsDone = true;
         disableInstantPlacement();
       }
     }
   }
   private void disableInstantPlacement() {
     // When the placement is done, you can disable Instant Placement to save computing.
     Config config = new Config(session);
     config.setInstantPlacementMode(InstantPlacementMode.DISABLED);
     session.configure(config);
   }
 }

This wrapper of InstantPlacementPoint can be used to offset the apparent scale shift that occurs when the tracking method changes from InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE to InstantPlacementPoint.TrackingMethod.FULL_TRACKING. For AR experiences where a sudden apparent scale change is acceptable, no wrapper is required.

class WrappedInstantPlacement {
   public InstantPlacementPoint point;
   public TrackingMethod previousTrackingMethod;
   public float previousDistanceToCamera;
   public float scaleFactor = 1.0f;
   public WrappedInstantPlacement(
       InstantPlacementPoint point,
       TrackingMethod previousTrackingMethod,
       float previousDistanceToCamera) {
     this.point = point;
     this.previousTrackingMethod = previousTrackingMethod;
     this.previousDistanceToCamera = previousDistanceToCamera;
   }
 }
 ArrayList<WrappedInstantPlacement> wrappedPoints =
     new ArrayList<WrappedInstantPlacement>();
 public void onDrawFrame(GL10 gl) {
   Frame frame = session.update();
   // Place an object on tap.
   if (didUserTap()) {
     // Use estimated distance from the user's device to the real world, based
     // on how we expect the app to be used by users.
     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() == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
       // Continue to use estimated depth and pose. Record distance to 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() == TrackingMethod.FULL_TRACKING) {
       if (wrappedPoint.previousPoseType ==
           TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
        // Transition from estimated pose to real pose. Adjust object scale to
        // counter-act apparent change is size due to pose jump.
        wrappedPoint.scaleFactor = distance(point.getPose(), camera.getPose()) /
            wrappedPoint.previousDistanceToCamera);
        // TODO: Apply the scale factor to the model.
        // ...
        previousPoseType = TrackingMethod.FULL_TRACKING;
       }
     }
   }
 }

Nested Classes

enum InstantPlacementPoint.TrackingMethod Tracking methods for InstantPlacementPoint

Public Methods

Anchor
createAnchor(Pose pose)
Creates an anchor that is attached to this trackable, using the given initial pose in the world coordinate space.
boolean
equals(Object obj)
Indicates whether some other object is a Trackable referencing the same logical trackable as this one.
Collection<Anchor>
getAnchors()
Gets the Anchors attached to this trackable.
Pose
getPose()
Gets the pose of the Instant Placement point.
InstantPlacementPoint.TrackingMethod
TrackingState
getTrackingState()
Gets this trackable's TrackingState.
int
hashCode()
Returns a hash code value for the object.

Inherited Methods

Public Methods

createAnchor

public Anchor createAnchor(
  Pose pose
)

Creates an anchor that is attached to this trackable, using the given initial pose in the world coordinate space. The type of trackable will determine the semantics of attachment and how the anchor's pose will be updated to maintain this relationship. Note that the relative offset between the pose of multiple anchors attached to a trackable may adjust slightly over time as ARCore updates its model of the world.

Details
Parameters
pose

equals

public boolean equals(
  Object obj
)

Indicates whether some other object is a Trackable referencing the same logical trackable as this one.

Details
Parameters
obj the reference object with which to compare.
Returns true if this object is the same as the obj argument; false otherwise.
See Also

getAnchors

public Collection<Anchor> getAnchors()

Gets the Anchors attached to this trackable.

getPose

public Pose getPose()

Gets the pose of the Instant Placement point. Use getTrackingMethod() to determine whether depth and scale are accurate or are based on the estimated depth provided to Frame.hitTestInstantPlacement(float, float, float).

If the tracking method is InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE, the pose will be based on the approximate distance provided to Frame.hitTestInstantPlacement(float, float, float).

When the tracking method changes from InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE to InstantPlacementPoint.TrackingMethod.FULL_TRACKING, there will be a noticeable pose jump as the depth changes, from one that's estimated based on the approximate distance provided to Frame.hitTestInstantPlacement(float, float, float) to the actual distance as determined by ARCore.

Consequently, the on-screen size, or apparent scale, of any object anchored to the pose will appear to change abruptly. To prevent a visible pop in 3D asset size when the tracking method changes, the scale of the object can be adjusted to counteract the apparent scale change when the tracking method changes. See the InstantPlacementPoint class-level documentation for an example.

getTrackingMethod

public InstantPlacementPoint.TrackingMethod getTrackingMethod()

getTrackingState

public TrackingState getTrackingState()

Gets this trackable's TrackingState.

hashCode

public int hashCode()

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.

Details
Returns a hash code value for this object.
See Also