Create, control, and manage entities

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 Orbiters) 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:

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.

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)