항목 만들기, 제어, 관리

Jetpack XR SDK를 사용하면 Jetpack SceneCore를 사용하여 3D 모델, 3D 동영상, PanelEntity과 같은 Entity 인스턴스를 만들고 제어하고 관리할 수 있습니다.

Jetpack SceneCore는 3D 개발을 지원하기 위해 두 가지 일반적인 아키텍처 패턴, 즉 장면 그래프엔티티-구성요소 시스템 (ECS)을 채택합니다.

장면 그래프를 사용하여 엔티티 만들기 및 제어

3D 공간에서 객체를 만들고 제어하려면 Jetpack SceneCore의 Session API를 사용하여 장면 그래프에 액세스해야 합니다. 장면 그래프는 사용자의 실제 세계와 일치하며 패널 및 3D 모델과 같은 3D 항목을 계층 구조로 구성하고 이러한 항목의 상태를 유지할 수 있습니다.

장면 그래프에 액세스한 후 XR용 Jetpack Compose의 API를 사용하여 장면 그래프 내에 공간 UI (예: SpatialPanelOrbiter)를 만들 수 있습니다. 3D 모델과 같은 3D 콘텐츠의 경우 세션에 직접 액세스할 수 있습니다. 자세한 내용은 이 페이지의 ActivitySpace 정보를 참고하세요.

항목 구성요소 시스템

항목-구성요소 시스템은 상속보다 컴포지션의 원칙을 따릅니다. 동작 정의 구성요소를 연결하여 항목의 동작을 확장할 수 있으므로 동일한 동작을 여러 유형의 항목에 적용할 수 있습니다. 자세한 내용은 이 페이지의 항목에 공통 동작 추가를 참고하세요.

ActivitySpace 정보

Session에는 Session와 함께 자동으로 생성되는 ActivitySpace가 있습니다. ActivitySpace는 장면 그래프의 최상위 Entity입니다.

ActivitySpace는 오른손잡이 좌표계 (x축은 오른쪽을, y축은 위쪽을, z축은 원점을 기준으로 뒤쪽을 가리킴)와 실제 세계와 일치하는 단위인 미터로 3차원 공간을 나타냅니다. ActivitySpace의 원점은 다소 임의적입니다 (사용자가 실제 세계에서 ActivitySpace의 위치를 재설정할 수 있음). 따라서 원점을 기준으로 하는 대신 콘텐츠를 서로 상대적으로 배치하는 것이 좋습니다.

항목 작업

항목은 SceneCore의 핵심입니다. 사용자가 보고 상호작용하는 대부분의 모든 것은 패널, 3D 모델 등을 나타내는 항목입니다.

ActivitySpace는 장면 그래프의 최상위 노드이므로 기본적으로 모든 새 항목은 ActivitySpace에 직접 배치됩니다. setParent 또는 addChild를 호출하여 장면 그래프를 따라 항목을 재배치할 수 있습니다.

항목에는 위치, 회전, 표시 여부 변경과 같이 모든 항목에 공통적인 사항에 관한 몇 가지 기본 동작이 있습니다. GltfEntity와 같은 특정 Entity 서브클래스에는 서브클래스를 지원하는 추가 동작이 있습니다.

항목 조작

기본 Entity 클래스에 속한 Entity 속성을 변경하면 변경사항이 모든 하위 요소로 계단식 방식으로 적용됩니다. 예를 들어 상위 요소 EntityPose를 조정하면 모든 하위 요소가 동일하게 조정됩니다. 하위 Entity를 변경해도 상위 요소에는 영향을 미치지 않습니다.

Pose는 3D 공간 내에서 엔티티의 위치와 회전을 나타냅니다. 위치는 x, y, z 숫자 위치로 구성된 Vector3입니다. 회전은 Quaternion로 표현됩니다. Entity의 위치는 항상 상위 항목을 기준으로 합니다. 즉, 위치가 (0, 0, 0)인 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))

Entity의 공개 상태를 변경하려면 setHidden를 사용합니다.

//hide the entity
entity.setHidden(true)

전체 도형을 유지하면서 Entity의 크기를 조절하려면 setScale를 사용합니다.

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

항목에 공통 동작 추가

다음 구성요소를 사용하여 항목에 일반적인 동작을 추가할 수 있습니다.

구성요소를 인스턴스화하려면 Session 클래스의 적절한 생성 메서드를 통해야 합니다. 예를 들어 ResizableComponent를 만들려면 session.createResizableComponent()를 호출합니다.

Entity에 특정 구성요소 동작을 추가하려면 addComponent() 메서드를 사용하세요.

MovableComponent를 사용하여 엔티티를 사용자가 이동할 수 있도록 만들기

MovableComponent를 사용하면 사용자가 Entity를 이동할 수 있습니다. 또한 항목을 가로 또는 세로 표면과 같은 표면 유형이나 테이블, 벽, 천장과 같은 특정 시맨틱 표면에 고정할 수 있는지 지정할 수 있습니다. 앵커 옵션을 지정하려면 MovableComponent를 만들 때 AnchorPlacement 집합을 지정합니다.

다음은 모든 수직 표면과 바닥 및 천장 수평 표면으로만 이동하고 고정할 수 있는 항목의 예입니다.

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)

ResizableComponent를 사용하여 사용자가 크기를 조절할 수 있는 항목 만들기

ResizableComponent를 사용하면 사용자가 Entity의 크기를 조절할 수 있습니다. ResizableComponent에는 사용자가 Entity의 크기를 조절하도록 유도하는 시각적 상호작용 신호가 포함되어 있습니다. ResizeableComponent를 만들 때 최소 또는 최대 크기 (미터)를 지정할 수 있습니다. 너비와 높이가 서로 비례하여 크기가 조절되도록 크기를 조절할 때 고정 가로세로 비율을 지정할 수도 있습니다.

다음은 고정 가로세로 비율로 ResizableComponent를 사용하는 예입니다.

val resizableComponent = xrSession.createResizableComponent()
resizableComponent.minimumSize = Dimensions(177f, 100f, 1f )
resizableComponent.fixedAspectRatio = 16f / 9f //Specify a 16:9 aspect ratio
entity.addComponent(resizableComponent)

InteractableComponent를 사용하여 사용자 입력 이벤트 캡처

InteractableComponent를 사용하면 사용자가 Entity에 참여하거나 마우스를 가져갈 때와 같이 사용자의 입력 이벤트를 캡처할 수 있습니다. InteractableComponent를 만들 때 입력 이벤트를 수신할 InputEventListener를 지정해야 합니다. 사용자가 입력 작업을 실행하면 onInputEvent 메서드가 InputEvent 매개변수에 제공된 특정 입력 정보와 함께 호출됩니다.

모든 InputEvent 상수의 전체 목록은 참조 문서를 참고하세요.

다음 코드 스니펫은 InteractableComponent를 사용하여 오른손으로 항목의 크기를 늘리고 왼손으로 줄이는 예를 보여줍니다.

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)