Learn how to use the Instant Placement API in your own apps.
Prerequisites
Make sure that you understand fundamental AR concepts and how to configure an ARCore session before proceeding.
Configure a new session with Instant Placement
In a new ARCore session, enable Instant Placement mode .
Java
// Create the ARCore session.
public
void
createSession
()
{
session
=
new
Session
(
applicationContext
);
Config
config
=
new
Config
(
session
);
// Set the Instant Placement mode.
config
.
setInstantPlacementMode
(
InstantPlacementMode
.
LOCAL_Y_UP
);
session
.
configure
(
config
);
}
Kotlin
// Create the ARCore session.
fun
createSession
()
{
session
=
Session
(
applicationContext
);
val
config
=
Config
(
session
)
// Set the Instant Placement mode.
config
.
instantPlacementMode
=
Config
.
InstantPlacementMode
.
LOCAL_Y_UP
session
.
configure
(
config
)
}
Place an object
Use Frame.hitTestInstantPlacement()
to create a trackable Instant Placement point given a screen tap position.
Retrieve the current pose with the getPose()
method.
Java
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
=
2.0f
;
// Performs a ray cast given a screen tap position.
List<HitResult>
results
=
frame
.
hitTestInstantPlacement
(
tapX
,
tapY
,
approximateDistanceMeters
);
if
(
!
results
.
isEmpty
())
{
InstantPlacementPoint
point
=
(
InstantPlacementPoint
)
results
.
get
(
0
).
getTrackable
();
// Create an Anchor from the point's pose.
Anchor
anchor
=
point
.
createAnchor
(
point
.
getPose
());
placementIsDone
=
true
;
disableInstantPlacement
();
}
}
}
Kotlin
var
placementIsDone
=
false
;
fun
onDrawFrame
(
gl
:
GL10
)
{
val
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.
val
approximateDistanceMeters
=
2.0f
;
// Performs a ray cast given a screen tap position.
val
results
=
frame
.
hitTestInstantPlacement
(
tapX
,
tapY
,
approximateDistanceMeters
)
if
(
results
.
isNotEmpty
())
{
val
point
=
results
[
0
]
.
trackable
as
InstantPlacementPoint
// Create an Anchor from the point's pose.
val
anchor
=
point
.
createAnchor
(
point
.
pose
)
placementIsDone
=
true
disableInstantPlacement
()
}
}
}
Instant Placement supports screen space tracking with approximate distance
,
automatically switching to full tracking once the Instant Placement point is
anchored in the real world. Retrieve the current pose with getPose()
.
Get the current tracking method with getTrackingMethod()
.
Although ARCore can perform Instant Placement hit tests against surfaces of any orientation, hit results will always return a pose with +Y up, against the direction of gravity. On horizontal surfaces, hit tests return accurate positions much faster.
Smooth the tracking method transition
When true depth becomes available, ARCore will change a InstantPlacementPoint
's tracking method
from SCREENSPACE_WITH_APPROXIMATE_DISTANCE
to FULL_TRACKING
.
The point's pose will also change to reflect true depth.
This may lead to the object appearing to suddenly grow or shrink.
To avoid this sudden change, add an InstantPlacementPoint
wrapper.
Java
// Wrapper class to track state to reduce sudden visual changes in object size
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
;
}
}
Kotlin
// Wrapper class to track state to reduce sudden visual changes in object size
class
WrappedInstantPlacement
(
var
point
:
InstantPlacementPoint
,
var
previousTrackingMethod
:
InstantPlacementPoint
.
TrackingMethod
,
var
previousDistanceToCamera
:
Float
,
var
scaleFactor
:
Float
=
1.0f
)
Then, add the following to your activity.
Java
List<WrappedInstantPlacement>
wrappedPoints
=
new
ArrayList
<> ();
public
void
onDrawFrame
(
GL10
gl
)
{
Frame
frame
=
session
.
update
();
Camera
camera
=
frame
.
getCamera
();
// Place an object on tap.
if
(
didUserTap
())
{
// Instant Placement should only be applied if no results are available through hitTest.
List<HitResult>
results
=
frame
.
hitTest
(
tapX
,
tapY
);
if
(
results
.
isEmpty
())
{
// 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.
results
=
frame
.
hitTestInstantPlacement
(
tapX
,
tapY
,
approximateDistanceMeters
);
if
(
!
results
.
isEmpty
())
{
// Instant placement was successful.
InstantPlacementPoint
point
=
(
InstantPlacementPoint
)
results
.
get
(
0
).
getTrackable
();
wrappedPoints
.
add
(
new
WrappedInstantPlacement
(
point
,
point
.
getTrackingMethod
(),
distance
(
camera
.
getPose
(),
point
.
getPose
())));
}
}
else
{
// results contain valid hit tests which can be used directly, so instant placement is not required.
}
}
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 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
()
==
TrackingMethod
.
FULL_TRACKING
)
{
if
(
wrappedPoint
.
previousTrackingMethod
==
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
));
}
Kotlin
var
wrappedPoints
=
mutableListOf<WrappedInstantPlacement>
()
fun
onDrawFrame
(
gl
:
GL10?)
{
val
frame
=
session
.
update
()
val
camera
=
frame
.
camera
// Place an object on tap.
if
(
didUserTap
())
{
// Instant Placement should only be applied if no results are available through hitTest.
var
results
=
frame
.
hitTest
(
tapX
,
tapY
);
if
(
results
.
isEmpty
())
{
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
val
approximateDistanceMeters
=
2.0f
;
// Returns a single result if the hit test was successful.
results
=
frame
.
hitTestInstantPlacement
(
tapX
,
tapY
,
approximateDistanceMeters
);
if
(
results
.
isNotEmpty
())
{
// Instant placement was successful.
val
point
=
results
[
0
]
.
trackable
as
InstantPlacementPoint
val
wrapped
=
WrappedInstantPlacement
(
point
,
point
.
trackingMethod
,
point
.
pose
.
distance
(
camera
.
pose
))
wrappedPoints
.
add
(
wrapped
)
}
}
else
{
// Results contain valid hit tests which can be used directly, so Instant Placement
// is not required.
}
}
loop@
for
(
wrappedPoint
in
wrappedPoints
)
{
val
point
=
wrappedPoint
.
point
when
{
point
.
trackingState
==
TrackingState
.
STOPPED
-
>
{
wrappedPoints
.
remove
(
wrappedPoint
)
continue
@loop
}
point
.
trackingState
==
TrackingState
.
PAUSED
-
>
continue
@loop
point
.
trackingMethod
==
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
=
point
.
pose
.
distance
(
camera
.
pose
)
}
wrappedPoint
.
previousTrackingMethod
==
TrackingMethod
.
SCREENSPACE_WITH_APPROXIMATE_DISTANCE
&&
point
.
trackingMethod
==
TrackingMethod
.
FULL_TRACKING
-
>
{
// Change from the estimated pose to the accurate pose. Adjust the
// object scale to counteract the apparent change due to pose jump.
wrappedPoint
.
scaleFactor
=
point
.
pose
.
distance
(
camera
.
pose
)
/
wrappedPoint
.
previousDistanceToCamera
// Apply the scale factor to the model.
// ...
wrappedPoint
.
previousTrackingMethod
=
TrackingMethod
.
FULL_TRACKING
}
}
}
}
fun
Pose
.
distance
(
other
:
Pose
)
=
sqrt
(
(
tx
()
-
other
.
tx
()).
pow
(
2.0f
)
+
(
ty
()
-
other
.
ty
()).
pow
(
2.0f
)
+
(
tz
()
-
other
.
tz
()).
pow
(
2.0f
)
)
Increase efficiency after object placement
Disable Instant Placement when the object is correctly placed to save CPU cycles and power.
Java
void
disableInstantPlacement
()
{
Config
config
=
new
Config
(
session
);
config
.
setInstantPlacementMode
(
Config
.
InstantPlacementMode
.
DISABLED
);
session
.
configure
(
config
);
}
Kotlin
fun
disableInstantPlacement
()
{
val
config
=
Config
(
session
)
// Set the Instant Placement mode.
config
.
instantPlacementMode
=
Config
.
InstantPlacementMode
.
DISABLED
session
.
configure
(
config
)
}