Criar, controlar e gerenciar entidades

Dispositivos XR relevantes
Estas orientações ajudam você a criar experiências para esses tipos de dispositivos XR.
Headsets XR
Óculos XR com fio

O SDK do Jetpack XR permite usar o SceneCore para criar, controlar e gerenciar Entity instâncias como modelos 3D, vídeos estereoscópicos e PanelEntity.

O Jetpack SceneCore adota dois padrões arquitetônicos comuns para oferecer suporte ao desenvolvimento 3D: um gráfico de cena e um sistema de componente de entidade (ECS, na sigla em inglês).

Usar o gráfico de cena para criar e controlar entidades

Para criar e controlar objetos no espaço 3D, use a API Session do Jetpack SceneCore para acessar o gráfico de cena. O gráfico de cena se alinha ao mundo real do usuário e permite organizar entidades 3D, como painéis e modelos 3D, em uma estrutura hierárquica e manter o estado dessas entidades.

Depois de acessar o gráfico de cena, use as APIs no Jetpack Compose para XR para criar uma interface espacial (por exemplo, SpatialPanel e Orbiter instâncias) dentro do gráfico de cena. Para conteúdo 3D, como modelos 3D, acesse a sessão diretamente. Para saber mais, consulte Sobre o ActivitySpace nesta página.

Sistema de componentes de entidade

Um sistema de componentes de entidade segue o princípio da composição em vez da herança. É possível expandir o comportamento das entidades anexando componentes que definem o comportamento, o que permite aplicar o mesmo comportamento a diferentes tipos de entidades. Para mais informações, consulte Adicionar comportamento comum a entidades nesta página.

Sobre o ActivitySpace

Cada Session tem um ActivitySpace criado automaticamente com o Session. O ActivitySpace é a Entity de nível superior no gráfico de cena.

O ActivitySpace representa um espaço tridimensional com um sistema de coordenadas destro (o eixo x aponta para a direita, o eixo y aponta para cima e o eixo z aponta para trás em relação à origem) e com metros para unidades que correspondem ao mundo real. A origem do ActivitySpace é um pouco arbitrária, já que os usuários podem redefinir a posição do ActivitySpace no mundo real. Portanto, é recomendável posicionar o conteúdo em relação um ao outro, em vez de em relação à origem.

Trabalhar com entidades

As entidades são fundamentais para o SceneCore. Quase tudo que o usuário vê e com que interage são entidades que representam painéis, modelos 3D e muito mais.

Como o ActivitySpace é o nó de nível superior do gráfico de cena, por padrão, todas as novas entidades são colocadas diretamente no ActivitySpace. É possível realocar entidades ao longo do gráfico de cena definindo o parent ou usando addChild().

As entidades têm alguns comportamentos padrão para coisas universais a todas as entidades, como mudar a posição, a rotação ou a visibilidade. Subclasses Entity específicas, como GltfModelEntity, têm comportamentos adicionais que oferecem suporte à subclasse.

Manipular entidades

Quando você faz uma mudança em uma propriedade Entity que pertence à classe Entity de base, a mudança é transmitida para todos os filhos. Por exemplo, ajustar o Pose de um Entity resulta em todos os filhos com o mesmo ajuste. Fazer uma mudança em uma Entity filha não afeta a mãe.

Uma Pose representa a localização e a rotação da entidade no espaço 3D. A localização é um Vector3 que consiste em posições numéricas x, y, z. A rotação é representada por um Quaternion. A posição de uma Entity é sempre relativa à entidade pai. Em outras palavras, uma Entity cuja posição é (0, 0, 0) será colocada na origem da entidade pai.

// 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))

Para desativar um Entity, use setEnabled(). Isso a torna invisível e interrompe todo o processamento feito nela.

// Disable the entity.
entity.setEnabled(false)

Para redimensionar um Entity mantendo a forma geral, use setScale().

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

Adicionar comportamento comum a entidades

É possível usar os seguintes componentes para adicionar comportamento comum a entidades:

A instanciação de componentes precisa ser feita pelo método de criação apropriado na classe Session. Por exemplo, para criar um ResizableComponent, chame ResizableComponent.create().

Para adicionar o comportamento do componente específico a um Entity, use o addComponent() método.

Usar MovableComponent para tornar uma entidade móvel para o usuário

O MovableComponent permite que uma Entity seja movida pelo usuário.

Os eventos de movimento são enviados ao componente quando as decorações são interagidas. O comportamento padrão do sistema, criado com MovableComponent.createSystemMovable(), move sua Entity quando as decorações são arrastadas:

val movableComponent = MovableComponent.createSystemMovable(session)
entity.addComponent(movableComponent)

O parâmetro opcional scaleInZ (por padrão, definido como true) faz com que a entidade ajuste automaticamente a escala à medida que é movida para longe do usuário, de maneira semelhante a como os painéis são dimensionados pelo sistema no espaço inicial. Devido à natureza "em cascata" do sistema de componentes de entidade, a escala do pai afeta todos os filhos.

Também é possível especificar se a entidade pode ser ancorada a um tipo de superfície, como superfícies horizontais ou verticais, ou superfícies semânticas específicas, como mesa, parede ou teto. Para especificar opções de ancoragem, especifique um conjunto de AnchorPlacement ao criar o MovableComponent. Neste exemplo, a entidade pode ser movida e ancorada a qualquer superfície horizontal de piso ou mesa:

val anchorPlacement = AnchorPlacement.createForPlanes(
    anchorablePlaneOrientations = setOf(PlaneOrientation.VERTICAL),
    anchorablePlaneSemanticTypes = setOf(PlaneSemanticType.FLOOR, PlaneSemanticType.TABLE)
)

val movableComponent = MovableComponent.createAnchorable(
    session = session,
    anchorPlacement = setOf(anchorPlacement)
)
entity.addComponent(movableComponent)

Usar ResizableComponent para tornar uma entidade redimensionável para o usuário

O ResizableComponent permite que os usuários redimensionem uma Entity. O ResizableComponent inclui dicas de interação visual que convidam o usuário a redimensionar uma Entity. Ao criar o ResizableComponent, é possível especificar um tamanho mínimo ou máximo (em metros). Você também tem a opção de especificar uma proporção fixa ao redimensionar para que a largura e a altura sejam redimensionadas proporcionalmente.

Ao criar um ResizableComponent, especifique um resizeEventListener que processe os eventos de atualização. É possível responder a diferentes ResizeState eventos, como RESIZE_STATE_ONGOING ou RESIZE_STATE_END.

Confira um exemplo de como usar o ResizableComponent com uma proporção fixa em uma SurfaceEntity:

val resizableComponent = ResizableComponent.create(session) { event ->
    if (event.resizeState == ResizeEvent.ResizeState.END) {
        // update the Entity to reflect the new size
        surfaceEntity.shape = SurfaceEntity.Shape.Quad(FloatSize2d(event.newSize.width, event.newSize.height))
    }
}
resizableComponent.minimumEntitySize = FloatSize3d(177f, 100f, 1f)
resizableComponent.isFixedAspectRatioEnabled = true // Maintain a fixed aspect ratio when resizing

surfaceEntity.addComponent(resizableComponent)

Usar InteractableComponent para capturar eventos de entrada do usuário

O InteractableComponent permite capturar eventos de entrada do usuário, como quando ele se envolve ou passa o cursor sobre um Entity. Ao criar um InteractableComponent, especifique um listener que receba os eventos de entrada. Quando o usuário realiza qualquer ação de entrada, o listener é chamado com as informações de entrada fornecidas no InputEvent parâmetro.

Para uma lista completa de todas as constantes InputEvent, consulte a documentação de referência.

O snippet de código a seguir mostra um exemplo de como usar um InteractableComponent para aumentar o tamanho de uma entidade com a mão direita e diminuir com a mão esquerda.

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.RIGHT) {
            entity.setScale(1.5f)
        } else if (it.pointerType == InputEvent.Pointer.LEFT) {
            entity.setScale(0.5f)
        }
    }
}
entity.addComponent(interactableComponent)

Criar modelos 3D personalizados no ambiente de execução

A API Custom Mesh permite gerar formas 3D de maneira programática diretamente no código, em vez de carregar recursos estáticos, como arquivos glTF. Ao criar uma geometria personalizada em tempo real, é possível renderizar dados processuais, formas personalizadas dinâmicas e ambientes 3D aparentemente infinitos, como terrenos que são gerados continuamente à medida que os usuários exploram. Além disso, a geração de malhas no ambiente de execução contribui para tamanhos binários menores, eliminando a necessidade de empacotar inúmeras variações de um único recurso 3D.