Jetpack XR SDK를 사용하면 Jetpack SceneCore를 사용하여 3D 모델, 스테레오스코피 비디오, PanelEntity
과 같은 Entity
인스턴스를 만들고 제어하고 관리할 수 있습니다.
Jetpack SceneCore는 3D 개발을 지원하기 위해 장면 그래프와 엔티티-컴포넌트 시스템 (ECS)이라는 두 가지 일반적인 아키텍처 패턴을 채택합니다.
장면 그래프를 사용하여 엔티티 생성 및 제어
3D 공간에서 객체를 만들고 제어하려면 Jetpack SceneCore의 세션 API를 사용하여 장면 그래프에 액세스하면 됩니다. 장면 그래프는 사용자의 실제 환경과 일치하며 패널, 3D 모델과 같은 3D 엔티티를 계층 구조로 정리하고 이러한 엔티티의 상태를 유지할 수 있습니다.
장면 그래프에 액세스하면 Jetpack Compose for XR의 API를 사용하여 장면 그래프 내에 공간 UI (예: SpatialPanel
및 Orbiter
인스턴스)를 만들 수 있습니다. 3D 모델과 같은 3D 콘텐츠의 경우 세션에 직접 액세스할 수 있습니다. 자세한 내용은 이 페이지의 ActivitySpace 정보를 참고하세요.
엔티티 컴포넌트 시스템
엔티티-컴포넌트 시스템은 상속보다 컴포지션 원칙을 따릅니다. 동작 정의 구성요소를 연결하여 항목의 동작을 확장할 수 있습니다. 이렇게 하면 다양한 유형의 항목에 동일한 동작을 적용할 수 있습니다. 자세한 내용은 이 페이지의 엔티티에 공통 동작 추가를 참고하세요.
ActivitySpace 정보
각 Session
에는 Session
와 함께 자동으로 생성되는 ActivitySpace
가 있습니다. ActivitySpace
은 장면 그래프의 최상위 Entity
입니다.
ActivitySpace는 오른손 좌표계(x축은 오른쪽을 가리키고 y축은 위쪽을 가리키며 z축은 원점을 기준으로 뒤쪽을 가리킴)와 실제 세계와 일치하는 단위(미터)를 사용하는 3차원 공간을 나타냅니다. ActivitySpace
의 원점은 다소 임의적입니다 (사용자가 실제 세계에서 ActivitySpace
의 위치를 재설정할 수 있으므로). 따라서 원점을 기준으로 콘텐츠를 배치하는 대신 서로를 기준으로 콘텐츠를 배치하는 것이 좋습니다.
항목 작업
항목은 SceneCore의 중심입니다. 사용자가 보고 상호작용하는 대부분의 항목은 패널, 3D 모델 등을 나타내는 항목입니다.
ActivitySpace
은 장면 그래프의 최상위 노드이므로 기본적으로 모든 새 항목은 ActivitySpace
에 직접 배치됩니다. setParent()
또는 addChild()
를 호출하여 장면 그래프를 따라 엔티티를 재배치할 수 있습니다.
엔티티에는 위치, 회전 또는 표시 여부 변경과 같이 모든 엔티티에 공통적인 사항에 관한 기본 동작이 있습니다. GltfModelEntity
와 같은 특정 Entity
서브클래스에는 서브클래스를 지원하는 추가 동작이 있습니다.
항목 조작
기본 Entity
클래스에 속하는 Entity
속성을 변경하면 변경사항이 모든 하위 요소로 캐스케이드됩니다. 예를 들어 상위 Entity
의 Pose
를 조정하면 모든 하위 요소가 동일하게 조정됩니다. 하위 Entity
를 변경해도 상위에는 영향을 미치지 않습니다.
Pose
는 3D 공간 내 항목의 위치와 회전을 나타냅니다. 위치는 x, y, z 숫자 위치로 구성된 Vector3
입니다. 회전은 Quaternion
으로 표현됩니다. Entity
의 위치는 항상 상위 항목을 기준으로 합니다. 즉, 위치가 (0, 0, 0)인 Entity
는 상위 항목의 원점에 배치됩니다.
// 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))
Entity
의 공개 상태를 변경하려면 setHidden()
를 사용합니다.
// Hide the entity entity.setHidden(true)
전체 모양을 유지하면서 Entity
의 크기를 조정하려면 setScale()
를 사용합니다.
// Double the size of the entity entity.setScale(2f)
엔티티에 공통 동작 추가
다음 구성요소를 사용하여 엔티티에 일반적인 동작을 추가할 수 있습니다.
MovableComponent
: 사용자가 항목을 이동할 수 있습니다.ResizableComponent
: 사용자가 일관된 UI 패턴으로 엔티티의 크기를 조절할 수 있습니다.InteractableComponent
: 맞춤 상호작용의 입력 이벤트를 캡처할 수 있습니다.
구성요소 인스턴스화는 Session
클래스의 적절한 생성 메서드를 통해 실행해야 합니다. 예를 들어 ResizableComponent
를 만들려면 ResizableComponent.create()
를 호출합니다.
Entity
에 특정 구성요소 동작을 추가하려면 addComponent()
메서드를 사용합니다.
MovableComponent
을 사용하여 엔티티를 사용자가 이동할 수 있도록 설정
MovableComponent
을 사용하면 사용자가 Entity
을 이동할 수 있습니다. 엔티티를 수평 또는 수직 표면과 같은 표면 유형이나 테이블, 벽, 천장과 같은 특정 시맨틱 표면에 고정할 수 있는지 여부도 지정할 수 있습니다. 앵커 옵션을 지정하려면 MovableComponent
를 만들 때 AnchorPlacement
집합을 지정합니다.
다음은 이동할 수 있고 모든 수직 표면과 바닥 및 천장 수평 표면에만 고정할 수 있는 엔티티의 예입니다.
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)
사용자가 엔티티를 이동하면 scaleInZ
매개변수가 홈 스페이스에서 패널이 시스템에 의해 확장되는 방식과 유사하게 사용자와 멀어질 때 엔티티의 크기를 자동으로 조정합니다. 엔티티 구성요소 시스템의 '캐스케이드' 특성으로 인해 상위의 스케일이 모든 하위에 영향을 미칩니다.
ResizableComponent
을 사용하여 Entity의 크기를 사용자가 조정할 수 있도록 설정
ResizableComponent
를 사용하면 사용자가 Entity
의 크기를 조절할 수 있습니다. ResizableComponent
에는 사용자가 Entity
의 크기를 조절하도록 유도하는 시각적 상호작용 신호가 포함되어 있습니다. ResizableComponent
를 만들 때 최소 또는 최대 크기 (미터)를 지정할 수 있습니다. 크기를 조절할 때 너비와 높이가 서로 비례하여 조절되도록 고정된 가로세로 비율을 지정할 수도 있습니다.
ResizableComponent
를 사용할 때는 onResizeUpdate
또는 onResizeEnd
과 같은 특정 크기 조절 이벤트에 응답하기 위해 ResizeListener
를 지정해야 합니다.
다음은 SurfaceEntity
에서 고정된 가로세로 비율로 ResizableComponent
을 사용하는 예입니다.
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)
InteractableComponent
를 사용하여 사용자 입력 이벤트 캡처
InteractableComponent
를 사용하면 사용자가 Entity
에 참여하거나 마우스를 가져갈 때와 같은 사용자로부터의 입력 이벤트를 캡처할 수 있습니다. InteractableComponent
를 만들 때 입력 이벤트를 수신할 InputEventListener
를 지정해야 합니다. 사용자가 입력 작업을 실행하면 InputEvent
매개변수에 제공된 특정 입력 정보와 함께 onInputEvent
메서드가 호출됩니다.
InputEvent.action
은 항목에서 마우스를 가져가기 또는 탭하기와 같은 입력 유형을 지정합니다.InputEvent.source
는 손 또는 컨트롤러 입력과 같은 입력이 어디에서 왔는지 지정합니다.InputEvent.pointerType
는 입력이 오른손에서 왔는지 왼손에서 왔는지 지정합니다.
모든 InputEvent
상수의 전체 목록은 참조 문서를 확인하세요.
다음 코드 스니펫은 InteractableComponent
를 사용하여 오른쪽 손으로 항목의 크기를 늘리고 왼쪽 손으로 줄이는 방법을 보여줍니다.
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)