The Jetpack XR SDK lets you use Jetpack SceneCore to create, control, and
manage Entity
instances such as
3D models, stereoscopic video,
and PanelEntity
using Jetpack SceneCore.
Jetpack SceneCore adopts two common architectural patterns to support 3D development: a scene graph and an entity-component system (ECS).
Use the scene graph to create and control entities
To create and control objects in 3D space, you must use Jetpack SceneCore's Session API to gain access to the scene graph. The scene graph aligns with the user's real world and lets you organize 3D entities such as panels and 3D models into a hierarchical structure, and hold the state of those entities.
Once you've gained access to the scene graph, you can use the APIs in Jetpack
Compose for XR to create spatial UI (for example,
SpatialPanel
and Orbiter
s)
within the scene graph. For 3D content such as 3D models, you can access the
Session directly. To learn more, see About the ActivitySpace
on this page.
Entity component system
An entity-component system follows the principle of composition over inheritance. You can expand the behavior of entities by attaching behavior-defining components, which lets you apply the same behavior to different types of entities. For more information, check out Add common behavior to entities on this page.
About the ActivitySpace
Each Session
has an ActivitySpace
that is
automatically created with the Session
. The ActivitySpace
is the top-level
Entity
in the scene graph.
The ActivitySpace represents a 3-dimensional space with a right-handed
coordinate system (the x-axis points to the right, the y-axis points up and the
z-axis back relative to the origin) and with meters for units that match the
real world. The origin for ActivitySpace
is somewhat arbitrary (as users can
reset the position of the ActivitySpace
within the real world), therefore it's
recommended to position content relative to each other instead of relative to
the origin.
Work with Entities
Entities are central to SceneCore. Most everything the user sees and interacts with are entities representing panels, 3D models and more.
Since the ActivitySpace
is the top-level node of the scene graph, by default,
all new entities are placed directly into the ActivitySpace
. You can relocate
entities along the scene graph by calling setParent
or
addChild
.
Entities have some default behaviors for things that are universal to all
entities, such as changing position, rotation, or visibility. Specific Entity
subclasses, like the GltfEntity
, have additional
behaviors that support the subclass.
Manipulate Entities
When you make a change to an Entity
property that belongs to the base Entity
class, the change will cascade down to all of its children. For example,
adjusting the Pose
of a parent Entity
results in all
of its children having the same adjustment. Making a change in a child Entity
does not impact its parent.
A Pose
represents the location and rotation of the Entity within 3D space. The
location is a Vector3
consisting of x, y, z numerical
positions. The rotation is represented by a
Quaternion
. The position of an Entity
is always
relative to its parent entity. In other words, an Entity
whose position is (0,
0, 0) will be placed at the origin of its parent entity.
//place the entity forward 2 meters
val modelPosition = Vector3(0f, 0f, -2f)
//rotate the entity by 180 degrees on the up axis (upside-down)
val newOrientation = Quaternion.fromEulerAngles(0f, 0f, 180f)
//update the position and rotation on the entity
entity.setPose(Pose(newPosition, newOrientation))
To change the visibility of an Entity
, use setHidden
.
//hide the entity
entity.setHidden(true)
To resize an Entity
while keeping its overall shape, use
setScale
.
//double the size of the entity
entity.setScale(2f)
Add common behavior to entities
You can use the following components to add common behavior to entities:
MovableComponent
: Allows the user to move entitiesResizableComponent
: Allows the user to resize entities with consistent UI patternsInteractableComponent
: Lets you capture input events for custom interactions
Instantiating components must be done through the appropriate creation method in
the Session
class. For example, to create a ResizableComponent
, call
session.createResizableComponent()
.
To add the specific component behavior to an Entity
use the
addComponent()
method.
Use MovableComponent to make an Entity user-movable
The MovableComponent
allows an Entity
to be movable
by the user. You can also specify whether the entity can be anchored to a
surface type like horizontal or vertical surfaces, or specific semantic surfaces
like table, wall or ceiling. To specify anchor options, specify a set of
AnchorPlacement
when creating the MovableComponent
.
Here's an example of an entity that can be moved and anchored to any vertical surface and only floor and ceiling horizontal surfaces.
val anchorPlacement = AnchorPlacement.createForPlanes(
planeTypeFilter = setOf(PlaneSemantic.FLOOR, PlaneSemantic.TABLE),
planeSemanticFilter = setOf(PlaneType.VERTICAL))
val movableComponent = xrSession.createMovableComponent(
systemMovable = false,
scaleInZ = false,
anchorPlacement = setOf(anchorPlacement)
)
entity.addComponent(movableComponent)
Use ResizableComponent to make an Entity user-resizable
The ResizableComponent
allows users to resize an
Entity
. The ResizableComponent
includes visual interaction cues that invite
the user to resize an Entity
. When creating the ResizeableComponent
, you can
specify a minimum or maximum size (in meters). You also have the option to
specify a fixed aspect ratio when resizing so that the width and height resize
proportionally to each other.
Here's an example of using the ResizableComponent
with a fixed aspect ratio:
val resizableComponent = xrSession.createResizableComponent()
resizableComponent.minimumSize = Dimensions(177f, 100f, 1f )
resizableComponent.fixedAspectRatio = 16f / 9f //Specify a 16:9 aspect ratio
entity.addComponent(resizableComponent)
Use InteractableComponent to capture user input events
The InteractableComponent
lets you capture input
events from the user, such as when the user engages or hovers over an Entity
.
When creating an InteractableComponent
, you must specify an
InputEventListener
to receive the input events. When
the user performs any input action, the onInputEvent
method will be called with the specific input information provided in the
InputEvent
parameter.
InputEvent.action
specifies the type of input such as hovering or tapping on an entityInputEvent.source
specifies where the input came from such as hand or controller inputInputEvent.pointerType
specifies whether the input came from the right hand or left hand
For a full list of all the InputEvent
constants, see the reference
documentation.
The following code snippet shows an example of using an InteractableComponent
to increase the size of an entity with the right hand and decreases with the
left hand.
private val executor by lazy { Executors.newSingleThreadExecutor() }
val interactableComponent = xrSession.createInteractableComponent(executor) {
//when the user disengages with the entity with their hands
if (it.source == InputEvent.SOURCE_HANDS && it.action == InputEvent.ACTION_UP) {
// increase size with right hand and decrease with left
if (it.pointerType == InputEvent.POINTER_TYPE_RIGHT){
entity.setScale(1.5f)
} else if (it.pointerType == InputEvent.POINTER_TYPE_LEFT){
entity.setScale(0.5f)
}
}
}
entity.addComponent(interactableComponent)