Pakiet Jetpack XR SDK umożliwia tworzenie, kontrolowanie i zarządzanie instancjami Entity, takimi jak modele 3D, filmy stereoskopowe i PanelEntity, za pomocą Jetpack SceneCore.
Jetpack SceneCore wykorzystuje 2 popularne wzorce architektoniczne do obsługi tworzenia aplikacji 3D: graf sceny i system komponentów jednostek (ECS).
Tworzenie i kontrolowanie obiektów za pomocą grafu sceny
Aby tworzyć obiekty w przestrzeni 3D i nimi sterować, możesz użyć interfejsu Session API z Jetpack SceneCore, aby uzyskać dostęp do wykresu sceny. Graf sceny jest dopasowany do rzeczywistego świata użytkownika i umożliwia organizowanie obiektów 3D, takich jak panele i modele 3D, w strukturę hierarchiczną oraz przechowywanie stanu tych obiektów.
Po uzyskaniu dostępu do grafu sceny możesz używać interfejsów API w Jetpack Compose na potrzeby XR do tworzenia przestrzennego interfejsu (np. instancji SpatialPanel i Orbiter) w grafie sceny. W przypadku treści 3D, takich jak modele 3D, możesz uzyskać dostęp do sesji bezpośrednio. Więcej informacji znajdziesz w sekcji Informacje o przestrzeni aktywności na tej stronie.
System komponentów encji
System komponentów jednostek jest zgodny z zasadą kompozycji zamiast dziedziczenia. Możesz rozszerzyć działanie elementów, dołączając do nich komponenty definiujące zachowanie, co pozwala stosować to samo zachowanie do różnych typów elementów. Więcej informacji znajdziesz w sekcji Dodawanie do encji wspólnych zachowań na tej stronie.
Informacje o ActivitySpace
Każdy Session ma ActivitySpace, który jest tworzony automatycznie za pomocą Session. ActivitySpace to Entity najwyższego poziomu w grafie sceny.
ActivitySpace to przestrzeń trójwymiarowa z prawoskrętnym układem współrzędnych (oś X jest skierowana w prawo, oś Y w górę, a oś Z do tyłu względem początku układu współrzędnych) i jednostkami w metrach, które odpowiadają rzeczywistym wymiarom. Początek układu współrzędnych ActivitySpace jest nieco arbitralny (użytkownicy mogą zresetować jego położenie w rzeczywistym świecie), dlatego zalecamy pozycjonowanie treści względem siebie, a nie względem początku układu współrzędnych.ActivitySpace
Praca z encjami
Elementy są kluczowe w SceneCore. Większość elementów, które użytkownik widzi i z którymi wchodzi w interakcję, to obiekty reprezentujące panele, modele 3D i inne elementy.
Ponieważ ActivitySpace jest węzłem najwyższego poziomu w grafie sceny, domyślnie wszystkie nowe elementy są umieszczane bezpośrednio w ActivitySpace. Możesz przenieść elementy wzdłuż wykresu sceny, ustawiając jego parent lub używając addChild().
Jednostki mają pewne domyślne zachowania w przypadku elementów uniwersalnych dla wszystkich jednostek, takich jak zmiana pozycji, rotacji lub widoczności. Określone Entitypodklasy, takie jak GltfModelEntity, mają dodatkowe zachowania, które obsługują podklasę.
Manipulowanie encjami
Jeśli wprowadzisz zmianę we właściwości Entity należącej do klasy bazowej Entity, zostanie ona zastosowana do wszystkich jej elementów podrzędnych. Na przykład zmiana Pose elementu nadrzędnego Entity powoduje, że wszystkie jego elementy podrzędne mają taką samą zmianę. Zmiana w podrzędnym Entity nie ma wpływu na jego element nadrzędny.
Symbol Pose reprezentuje lokalizację i obrót jednostki w przestrzeni 3D. Lokalizacja to Vector3 składająca się z wartości liczbowych x, y i z. Obrót jest oznaczony symbolem Quaternion. Położenie Entity jest zawsze względne w stosunku do encji nadrzędnej. Innymi słowy, Entity, którego pozycja to (0, 0, 0), zostanie umieszczony w punkcie początkowym jednostki nadrzędnej.
// 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))
Aby wyłączyć Entity, użyj setEnabled(). Spowoduje to, że stanie się on niewidoczny i wszelkie przetwarzanie na nim zostanie zatrzymane.
// Disable the entity. entity.setEnabled(false)
Aby zmienić rozmiar Entity, zachowując jego ogólny kształt, użyj setScale().
// Double the size of the entity entity.setScale(2f)
Dodawanie do encji typowych zachowań
Aby dodać do obiektów typowe zachowania, możesz użyć tych komponentów:
MovableComponent: umożliwia użytkownikowi przenoszenie elementów.ResizableComponent: umożliwia użytkownikowi zmianę rozmiaru elementów za pomocą spójnych wzorców interfejsu.InteractableComponent: umożliwia rejestrowanie zdarzeń wejściowych w przypadku interakcji niestandardowych.
Tworzenie instancji komponentów musi odbywać się za pomocą odpowiedniej metody tworzenia w klasie Session. Na przykład aby utworzyć ResizableComponent, wywołaj funkcję
ResizableComponent.create().
Aby dodać do elementu Entity określone działanie komponentu, użyj metody addComponent().
Używanie MovableComponent, aby umożliwić użytkownikowi przesuwanie jednostki
MovableComponent umożliwia użytkownikowi przesuwanie Entity.
Zdarzenia ruchu są wysyłane do komponentu, gdy użytkownik wchodzi w interakcję z dekoracjami. Domyślne zachowanie systemu, utworzone za pomocą
MovableComponent.createSystemMovable(), przesuwa Entity, gdy
dekoracje są przeciągane:
val movableComponent = MovableComponent.createSystemMovable(session) entity.addComponent(movableComponent)
Opcjonalny parametr scaleInZ (domyślnie ustawiony na true) powoduje, że element Entity automatycznie dostosowuje swój rozmiar, gdy jest oddalany od użytkownika, podobnie jak system skaluje panele w przestrzeni domowej.
Ze względu na „kaskadowy” charakter systemu komponentów encji skala elementu nadrzędnego będzie miała wpływ na wszystkie jego elementy podrzędne.
Możesz też określić, czy element może być przytwierdzony do typu powierzchni, np. poziomej lub pionowej, albo do konkretnych powierzchni semantycznych, takich jak stół, ściana lub sufit. Aby określić opcje kotwicy, podczas tworzenia elementu MovableComponent podaj zestaw AnchorPlacement. W tym przykładzie element, który można przesuwać i przytwierdzać do dowolnej podłogi lub poziomej powierzchni stołu:
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)
Użyj kodu ResizableComponent, aby umożliwić zmianę rozmiaru jednostki przez użytkownika
ResizableComponent umożliwia użytkownikom zmianę rozmiaru Entity. ResizableComponent zawiera wizualne wskazówki dotyczące interakcji, które zachęcają użytkownika do zmiany rozmiaru Entity. Podczas tworzenia ResizableComponent możesz określić minimalny lub maksymalny rozmiar (w metrach). Podczas zmiany rozmiaru możesz też określić stały współczynnik proporcji, aby szerokość i wysokość zmieniały się proporcjonalnie względem siebie.
Podczas tworzenia ResizableComponent określ resizeEventListener, który obsługuje zdarzenia aktualizacji. Możesz odpowiadać na różne ResizeState zdarzenia, takie jak RESIZE_STATE_ONGOING czy RESIZE_STATE_END.
Oto przykład użycia właściwości ResizableComponent ze stałymi proporcjami na urządzeniu 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)
Używaj InteractableComponent do rejestrowania zdarzeń związanych z działaniami użytkownika
InteractableComponent umożliwia rejestrowanie zdarzeń wejściowych od użytkownika, takich jak interakcja z Entity lub najechanie na nią kursorem. Podczas tworzenia elementu InteractableComponent określ detektor, który będzie odbierać zdarzenia wejściowe.
Gdy użytkownik wykona dowolne działanie związane z wprowadzaniem danych, wywoływana jest funkcja nasłuchująca z informacjami o wprowadzonych danych podanymi w parametrze InputEvent.
InputEvent.actionokreśla typ działania, np. najechanie lub kliknięcie elementu.InputEvent.sourceokreśla, skąd pochodzi dane wejściowe, np. z ręki lub kontrolera.InputEvent.pointerTypeokreśla, czy dane wejściowe pochodzą z prawej czy lewej ręki.
Pełną listę wszystkich stałych InputEvent znajdziesz w dokumentacji.
Poniższy fragment kodu pokazuje przykład użycia InteractableComponent do zwiększania rozmiaru obiektu prawą ręką i zmniejszania go lewą ręką.
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)
Tworzenie niestandardowych modeli 3D w czasie działania
Interfejs Custom Mesh API umożliwia programowe generowanie kształtów 3D bezpośrednio w kodzie, zamiast wczytywania statycznych zasobów, takich jak pliki glTF. Dzięki tworzeniu niestandardowej geometrii w czasie rzeczywistym możesz renderować dane proceduralne, dynamiczne kształty niestandardowe i pozornie nieskończone środowiska 3D, takie jak teren, który jest stale generowany podczas eksploracji przez użytkowników. Generowanie siatek w czasie działania przyczynia się też do zmniejszenia rozmiaru plików binarnych, ponieważ eliminuje konieczność pakowania niezliczonych odmian pojedynczego zasobu 3D.