1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.xr.runtime.openxr 18 19 import androidx.annotation.RestrictTo 20 import androidx.xr.runtime.internal.Anchor 21 import androidx.xr.runtime.internal.AnchorResourcesExhaustedException 22 import androidx.xr.runtime.internal.Hand 23 import androidx.xr.runtime.internal.HitResult 24 import androidx.xr.runtime.internal.PerceptionManager 25 import androidx.xr.runtime.internal.Plane 26 import androidx.xr.runtime.internal.Trackable 27 import androidx.xr.runtime.math.Pose 28 import androidx.xr.runtime.math.Ray 29 import androidx.xr.runtime.math.Vector3 30 import java.util.Arrays 31 import java.util.UUID 32 33 /** Implementation of the perception capabilities of a runtime using OpenXR. */ 34 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) 35 public class OpenXrPerceptionManager 36 internal constructor(private val timeSource: OpenXrTimeSource) : PerceptionManager { 37 createAnchornull38 override fun createAnchor(pose: Pose): Anchor { 39 val nativeAnchor = nativeCreateAnchor(pose, lastUpdateXrTime) 40 checkNativeAnchorIsValid(nativeAnchor) 41 val anchor = OpenXrAnchor(nativeAnchor, xrResources) 42 anchor.update(lastUpdateXrTime) 43 xrResources.addUpdatable(anchor as Updatable) 44 return anchor 45 } 46 47 // TODO: b/345315434 - Implement this method correctly once we have the ability to conduct 48 // hit tests in the native OpenXrManager. hitTestnull49 override fun hitTest(ray: Ray): List<HitResult> { 50 val hitData = 51 nativeHitTest( 52 maxResults = 5, 53 ray.origin.x, 54 ray.origin.y, 55 ray.origin.z, 56 ray.direction.x, 57 ray.direction.y, 58 ray.direction.z, 59 lastUpdateXrTime, 60 ) 61 return Arrays.asList(*hitData).toList().map { toHitResult(it, ray.origin) } 62 } 63 getPersistedAnchorUuidsnull64 override fun getPersistedAnchorUuids(): List<UUID> { 65 val anchorUuids = nativeGetPersistedAnchorUuids() 66 return Arrays.asList(*anchorUuids) 67 .toList() 68 .map { OpenXrAnchor.UUIDFromByteArray(it) } 69 .filterNotNull() 70 } 71 loadAnchornull72 override fun loadAnchor(uuid: UUID): Anchor { 73 val nativeAnchor = nativeLoadAnchor(uuid) 74 when (nativeAnchor) { 75 -2L -> throw IllegalStateException("Failed to load anchor.") 76 -10L -> throw AnchorResourcesExhaustedException() 77 } 78 val anchor = OpenXrAnchor(nativeAnchor, xrResources, loadedUuid = uuid) 79 anchor.update(lastUpdateXrTime) 80 xrResources.addUpdatable(anchor as Updatable) 81 return anchor 82 } 83 loadAnchorFromNativePointernull84 override fun loadAnchorFromNativePointer(nativePointer: Long): Anchor { 85 val anchor = OpenXrAnchor(nativePointer, xrResources) 86 anchor.update(lastUpdateXrTime) 87 xrResources.addUpdatable(anchor as Updatable) 88 return anchor 89 } 90 unpersistAnchornull91 override fun unpersistAnchor(uuid: UUID) { 92 check(nativeUnpersistAnchor(uuid)) { "Failed to unpersist anchor." } 93 } 94 95 internal val xrResources = XrResources() 96 override val trackables: Collection<Trackable> = xrResources.trackablesMap.values 97 override val leftHand: Hand 98 get() = xrResources.leftHand 99 100 override val rightHand: Hand 101 get() = xrResources.rightHand 102 103 private var lastUpdateXrTime: Long = 0L 104 105 /** 106 * Updates the perception manager. 107 * 108 * @param xrTime the number of nanoseconds since the start of the OpenXR epoch. 109 */ updatenull110 public fun update(xrTime: Long) { 111 val planes = nativeGetPlanes() 112 // Add new planes to the list of trackables. 113 for (plane in planes) { 114 if (xrResources.trackablesMap.containsKey(plane)) continue 115 116 val planeTypeInt = nativeGetPlaneType(plane, xrTime) 117 check(planeTypeInt >= 0) { "Failed to get plane type." } 118 119 val trackable = 120 OpenXrPlane(plane, Plane.Type.fromOpenXrType(planeTypeInt), timeSource, xrResources) 121 xrResources.addTrackable(plane, trackable) 122 xrResources.addUpdatable(trackable as Updatable) 123 } 124 125 for (updatable in xrResources.updatables) { 126 updatable.update(xrTime) 127 } 128 129 lastUpdateXrTime = xrTime 130 } 131 clearnull132 internal fun clear() { 133 xrResources.clear() 134 } 135 toHitResultnull136 private fun toHitResult(hitData: HitData, origin: Vector3): HitResult { 137 val trackable = 138 xrResources.trackablesMap[hitData.id] 139 ?: throw IllegalStateException("Trackable not found.") 140 141 return HitResult( 142 distance = (hitData.pose.translation - origin).length, 143 hitPose = hitData.pose, 144 trackable = trackable, 145 ) 146 } 147 checkNativeAnchorIsValidnull148 private fun checkNativeAnchorIsValid(nativeAnchor: Long) { 149 when (nativeAnchor) { 150 -2L -> throw IllegalStateException("Failed to create anchor.") // kErrorRuntimeFailure 151 -10L -> throw AnchorResourcesExhaustedException() // kErrorLimitReached 152 } 153 } 154 nativeCreateAnchornull155 private external fun nativeCreateAnchor(pose: Pose, timestampNs: Long): Long 156 157 private external fun nativeGetPlanes(): LongArray 158 159 private external fun nativeGetPlaneType(planeId: Long, timestampNs: Long): Int 160 161 private external fun nativeHitTest( 162 maxResults: Int, 163 originX: Float, 164 originY: Float, 165 originZ: Float, 166 directionX: Float, 167 directionY: Float, 168 directionZ: Float, 169 timestampNs: Long, 170 ): Array<HitData> 171 172 private external fun nativeGetPersistedAnchorUuids(): Array<ByteArray> 173 174 private external fun nativeLoadAnchor(uuid: UUID): Long 175 176 private external fun nativeUnpersistAnchor(uuid: UUID): Boolean 177 } 178