Trabalhar com o ARCore para o Jetpack XR

O ARCore para Jetpack XR permite que os apps funcionem com conceitos básicos de realidade aumentada (RA), usando primitivas de compreensão de cena de baixo nível e rastreamento de movimento. Use o ARCore para Jetpack XR ao criar experiências de RA e precisar usar dados planares ou ancorar conteúdo a um local fixo no espaço.

Entender o ciclo de vida de uma sessão

Todos os objetos rastreados pelo ARCore para Jetpack XR precisam ser acessados por uma sessão. Semelhante ao ciclo de vida de uma atividade, os objetos de sessão também têm um ciclo de vida que precisa ser mantido de acordo com o uso dos recursos de um objeto de sessão pelo app. Se o app tiver uma única atividade com suporte a XR, considere gerenciar o ciclo de vida da sessão usando um componente ciente do ciclo de vida.

Criar uma sessão

Uma sessão precisa ser criada antes de ser usada. A criação de uma sessão exige que o usuário tenha concedido a permissão android.permission.SCENE_UNDERSTANDING ao seu app.

Para criar uma sessão:

when (val result = Session.create(owner)) {
  is SessionCreateSuccess -> {
    session = result.session
  }
  is SessionCreatePermissionsNotGranted -> {
   // Request android.permission.SCENE_UNDERSTANDING.
  }
}

Consulte SessionCreateResult para saber por que uma sessão pode não ser criada.

Retomar uma sessão

A retomada de uma sessão precisa ser feita quando o app estiver pronto para processar mudanças de estado do ARCore para o Jetpack XR. Em muitos casos, isso é feito no callback onResume() da atividade, mas o app pode atrasar o processamento até a interação do usuário.

O snippet de código abaixo mostra um exemplo de como retomar uma sessão.

when (val result = session.resume()) {
  is SessionResumeSuccess -> {
    // Session has been created successfully.
    // Attach any successful handlers here.
  }
  is SessionResumePermissionsNotGranted -> {
    // Request android.permission.SCENE_UNDERSTANDING.
}

Consulte SessionResumeResult para saber por que uma sessão pode não ser retomada.

Pausar uma sessão

Quando a atividade for para segundo plano, pause a sessão usando Session.pause(). Pausar uma sessão interrompe temporariamente o rastreamento até que ela seja retomada, mantendo o estado do sistema de percepção.

Destruir uma sessão

Para descartar uma sessão permanentemente, use Session.destroy(). Isso libera os recursos usados pela sessão e destrói todos os estados da sessão.

Recuperar o estado das superfícies percebidas

O ARCore para Jetpack XR fornece o estado dos planos por meio de um StateFlow que emite o estado dos planos. A inscrição em planos em uma sessão notifica seu app quando os planos são adicionados, atualizados ou removidos.

Plane.subscribe(session).collect { planes ->
 // Planes have changed; update plane rendering
}

Um plano tem as seguintes propriedades:

  • label: uma descrição semântica de um determinado Plane. Pode ser um Wall, Floor, Ceiling ou Table.
  • centerPose: a pose do centro do plano detectado.
  • extents: as dimensões do plano detectado, em metros.
  • vertices: uma lista de vértices de um polígono convexo que se aproxima do plano.

Realizar um teste de hit em planos

Um teste de hit é um método de cálculo da interseção de um raio com objetos rastreados pela sessão. Uma aplicação comum de um teste de hit é apontar para uma tabela e colocar um objeto nesse local. A realização de um teste de hit resulta em uma lista de objetos de hit. Em outras palavras, um hit-test não para no primeiro objeto atingido. No entanto, muitas vezes você só tem interesse no primeiro objeto encontrado de um determinado tipo.

Para realizar um teste de hit, use Interaction.hitTest() com um Ray:

val results = Interaction.hitTest(session, ray)
// When interested in the first Table hit:
val tableHit = results.firstOrNull {
  val trackable = it.trackable
  trackable is Plane && trackable.state.value.label == Plane.Label.Table
}

Fixar o conteúdo em um local fixo no espaço

Para dar aos objetos virtuais uma posição no mundo real, use um Anchor. Um objeto de âncora ajuda seu app a acompanhar um local fixo no espaço físico.

Uma âncora é criada usando um Pose, que pode ser interpretado em relação a um Trackable existente ou não.

Criar uma âncora relativa a um rastreável

Quando uma âncora é criada em relação a um Trackable, como um Plane, que faz com que a âncora siga o Trackable anexado quando ele se move pelo espaço.

val anchor = trackable.createAnchor(pose)

Criar uma âncora sem um rastreável

Para criar uma âncora que não esteja anexada a um Trackable:

when (val result = Anchor.create(session, pose)) {
  is AnchorCreateSuccess -> // ...
  else -> // handle failure
}

Anexar uma entidade a uma âncora

Para renderizar um modelo nesse local, crie um GltfModel e defina a pose como a âncora. Confira se o modelo fica oculto quando o TrackingState da âncora é Stopped.

// renderSession is androidx.xr.core.Session
anchor.state.collect { state ->
  if (state.trackingState == TrackingState.Tracking) {
    gltfEntity.setPose(
      renderSession.perceptionSpace.transformPoseTo(state.pose, renderSession.activitySpace)
    )
  } else if (state.trackingState == TrackingState.Stopped) {
    entity.setHidden(true)
  }
}

Entender o TrackingState

Cada Trackable tem um TrackingState que precisa ser verificado antes de ser usado. Um Trackable com um TrackableState de Tracking tem o Pose atualizado ativamente pelo sistema. Um Trackable que é Paused pode se tornar Tracking no futuro, enquanto um que é Stopped nunca se tornará Tracking.

Manter uma âncora em todas as sessões

Uma âncora que não é mantida desaparece depois que uma sessão é destruída. Ao persistir uma âncora, o app lembra a posição dela nos dados particulares. Essa âncora pode ser recuperada em uma sessão subsequente e é ancorada no mesmo local do mundo.

Para manter uma âncora, use anchor.persist(), conforme mostrado aqui:

val uuid = anchor.persist()

Seu app pode recuperar a âncora usando a UUID em uma sessão futura:

when (val result = Anchor.load(session, uuid)) {
  is AnchorCreateSuccess -> // Loading was successful. The anchor is stored in result.anchor.
  else -> // handle failure
}

Quando você não precisar mais de uma âncora, chame unpersist(). Isso remove a âncora do armazenamento do app e torna o UUID fornecido irrecuperável para chamadas para Anchor.load().

Anchor.unpersist(session, uuid)

O app também pode solicitar uma lista de todas as âncoras persistidas que ainda estão presentes no armazenamento do app:

val uuids = Anchor.getPersistedAnchorUuids(session)