Créer, contrôler et gérer des entités

Le SDK Jetpack XR vous permet d'utiliser Jetpack SceneCore pour créer, contrôler et gérer des instances Entity telles que des modèles 3D, des vidéos stéréoscopiques et PanelEntity à l'aide de Jetpack SceneCore.

Jetpack SceneCore adopte deux modèles architecturaux courants pour prendre en charge le développement 3D : un graphique de scène et un système entité-composant (ECS, Entity-Component System).

Utiliser le graphique de scène pour créer et contrôler des entités

Pour créer et contrôler des objets dans un espace 3D, vous pouvez utiliser l'API Session de Jetpack SceneCore pour accéder au graphique de scène. Le graphique de scène s'aligne sur le monde réel de l'utilisateur et vous permet d'organiser les entités 3D telles que les panneaux et les modèles 3D dans une structure hiérarchique, et de conserver l'état de ces entités.

Une fois que vous avez accès au graphique de scène, vous pouvez utiliser les API de Jetpack Compose pour XR afin de créer une UI spatiale (par exemple, des instances SpatialPanel et Orbiter) dans le graphique de scène. Pour les contenus 3D tels que les modèles 3D, vous pouvez accéder directement à la session. Pour en savoir plus, consultez À propos d'ActivitySpace sur cette page.

Système de composants d'entités

Un système entité-composant suit le principe de composition plutôt que d'héritage. Vous pouvez étendre le comportement des entités en y associant des composants qui définissent le comportement. Cela vous permet d'appliquer le même comportement à différents types d'entités. Pour en savoir plus, consultez Ajouter un comportement commun aux entités sur cette page.

À propos d'ActivitySpace

Chaque Session possède un ActivitySpace qui est créé automatiquement avec le Session. ActivitySpace est le Entity de premier niveau dans le graphique de scène.

ActivitySpace représente un espace tridimensionnel avec un système de coordonnées "main droite" (l'axe X pointe vers la droite, l'axe Y vers le haut et l'axe Z vers l'arrière par rapport à l'origine) et avec des mètres pour les unités qui correspondent au monde réel. L'origine de ActivitySpace est quelque peu arbitraire (car les utilisateurs peuvent réinitialiser la position de ActivitySpace dans le monde réel). Il est donc recommandé de positionner le contenu les uns par rapport aux autres plutôt que par rapport à l'origine.

Utiliser des entités

Les entités sont au cœur de SceneCore. La plupart des éléments que l'utilisateur voit et avec lesquels il interagit sont des entités représentant des panneaux, des modèles 3D, etc.

Étant donné que ActivitySpace est le nœud de premier niveau du graphique de scène, par défaut, toutes les nouvelles entités sont placées directement dans ActivitySpace. Vous pouvez déplacer des entités le long du graphique de scène en appelant setParent() ou addChild().

Les entités ont des comportements par défaut pour les éléments universels à toutes les entités, comme la modification de la position, de la rotation ou de la visibilité. Certaines sous-classes Entity, comme GltfModelEntity, ont des comportements supplémentaires qui prennent en charge la sous-classe.

Manipuler des entités

Lorsque vous modifiez une propriété Entity appartenant à la classe Entity de base, la modification est répercutée sur tous ses enfants. Par exemple, si vous ajustez le Pose d'un Entity parent, tous ses enfants auront le même ajustement. Si vous apportez une modification à un Entity enfant, cela n'aura aucune incidence sur son parent.

Un Pose représente l'emplacement et la rotation de l'entité dans l'espace 3D. L'emplacement est un Vector3 composé de positions numériques x, y et z. La rotation est représentée par un Quaternion. La position d'un Entity est toujours relative à son entité parente. En d'autres termes, un Entity dont la position est (0, 0, 0) sera placé à l'origine de son entité parente.

// Place the entity forward 2 meters
val newPosition = 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))

Pour modifier la visibilité d'un Entity, utilisez setHidden().

// Hide the entity
entity.setHidden(true)

Pour redimensionner un Entity tout en conservant sa forme globale, utilisez setScale().

// Double the size of the entity
entity.setScale(2f)

Ajouter un comportement commun aux entités

Vous pouvez utiliser les composants suivants pour ajouter un comportement courant aux entités :

  • MovableComponent : permet à l'utilisateur de déplacer des entités.
  • ResizableComponent : permet à l'utilisateur de redimensionner les entités avec des modèles d'UI cohérents.
  • InteractableComponent : vous permet de capturer des événements d'entrée pour des interactions personnalisées.

L'instanciation des composants doit être effectuée à l'aide de la méthode de création appropriée dans la classe Session. Par exemple, pour créer un ResizableComponent, appelez ResizableComponent.create().

Pour ajouter le comportement spécifique du composant à un Entity, utilisez la méthode addComponent().

Utiliser MovableComponent pour rendre une entité déplaçable par l'utilisateur

Le MovableComponent permet à l'utilisateur de déplacer un Entity. Vous pouvez également spécifier si l'entité peut être ancrée à un type de surface, comme une surface horizontale ou verticale, ou à des surfaces sémantiques spécifiques, comme une table, un mur ou un plafond. Pour spécifier des options d'ancrage, indiquez un ensemble de AnchorPlacement lors de la création de MovableComponent.

Voici un exemple d'entité qui peut être déplacée et ancrée sur n'importe quelle surface verticale et uniquement sur les surfaces horizontales du sol et du plafond.

val anchorPlacement = AnchorPlacement.createForPlanes(
    planeTypeFilter = setOf(PlaneSemantic.FLOOR, PlaneSemantic.TABLE),
    planeSemanticFilter = setOf(PlaneType.VERTICAL)
)

val movableComponent = MovableComponent.create(
    session = session,
    systemMovable = false,
    scaleInZ = false,
    anchorPlacement = setOf(anchorPlacement)
)
entity.addComponent(movableComponent)

Lorsque l'utilisateur déplace l'entité, le paramètre scaleInZ ajuste automatiquement son échelle à mesure qu'elle s'éloigne de l'utilisateur, de la même manière que les panneaux sont mis à l'échelle par le système dans l'espace personnel. En raison de la nature "en cascade" du système de composants d'entité, l'échelle du parent aura un impact sur tous ses enfants.

Utiliser ResizableComponent pour redimensionner une entité

ResizableComponent permet aux utilisateurs de redimensionner un Entity. Le ResizableComponent inclut des repères visuels d'interaction qui invitent l'utilisateur à redimensionner un Entity. Lorsque vous créez le ResizableComponent, vous pouvez spécifier une taille minimale ou maximale (en mètres). Vous pouvez également spécifier un format fixe lors du redimensionnement afin que la largeur et la hauteur soient redimensionnées proportionnellement l'une par rapport à l'autre.

Lorsque vous utilisez ResizableComponent, vous devez spécifier un ResizeListener pour répondre à des événements de redimensionnement spécifiques tels que onResizeUpdate ou onResizeEnd.

Voici un exemple d'utilisation de ResizableComponent avec un format fixe sur un SurfaceEntity :

val resizableComponent = ResizableComponent.create(session)
resizableComponent.minimumSize = Dimensions(177f, 100f, 1f)
resizableComponent.fixedAspectRatio = 16f / 9f // Specify a 16:9 aspect ratio

resizableComponent.addResizeListener(
    executor,
    object : ResizeListener {
        override fun onResizeEnd(entity: Entity, finalSize: Dimensions) {

            // update the size in the component
            resizableComponent.size = finalSize

            // update the Entity to reflect the new size
            (entity as SurfaceEntity).canvasShape = SurfaceEntity.CanvasShape.Quad(finalSize.width, finalSize.height)
        }
    },
)

entity.addComponent(resizableComponent)

Utiliser InteractableComponent pour capturer les événements d'entrée utilisateur

InteractableComponent vous permet de capturer les événements d'entrée de l'utilisateur, par exemple lorsqu'il interagit avec un Entity ou le survole. Lorsque vous créez un InteractableComponent, vous devez spécifier un InputEventListener pour recevoir les événements d'entrée. Lorsque l'utilisateur effectue une action d'entrée, la méthode onInputEvent est appelée avec les informations d'entrée spécifiques fournies dans le paramètre InputEvent.

Pour obtenir la liste complète de toutes les constantes InputEvent, consultez la documentation de référence.

L'extrait de code suivant montre comment utiliser un InteractableComponent pour augmenter la taille d'une entité avec la main droite et la diminuer avec la main gauche.

val executor = Executors.newSingleThreadExecutor()
val interactableComponent = InteractableComponent.create(session, 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)