Crear una extension de entrada de imagen y datos de movimiento del dispositivo
Al crear una extension de entrada de imagen y datos de movimiento del dispositivo, los desarrolladores pueden ampliar EasyAR Sense con una implementacion de camara personalizada para admitir headsets u otros dispositivos de entrada especificos. El contenido siguiente presenta los pasos y las consideraciones para crear una extension de entrada de imagen y datos de movimiento del dispositivo.
Antes de empezar
- Comprenda conceptos basicos como camara y fotogramas de entrada.
- Lea Fuente de datos de fotogramas externa para conocer la descripcion detallada de las interfaces necesarias para crear una fuente de datos de fotogramas externa.
- Lea Datos de fotogramas de entrada externos para conocer los datos de fotogramas de camara y los datos de fotogramas de renderizado.
Crear la clase de fuente de datos de fotogramas externa
- Si necesita crear una extension de entrada de dispositivo 6DoF, herede de ExternalDeviceMotionFrameSource
- Si necesita crear una extension de entrada de dispositivo 3DoF, herede de ExternalDeviceRotationFrameSource
Ambas son subclases de MonoBehaviour, y el nombre del archivo debe ser igual al nombre de la clase.
Por ejemplo, para crear una extension de entrada de dispositivo 6DoF:
public class MyFrameSource : ExternalDeviceMotionFrameSource
{
}
Al crear una extension de headset, puede usar la plantilla com.easyar.sense.ext.hmdtemplate y modificarla como base. Esta plantilla se encuentra en el paquete comprimido del plugin de Unity descargado del sitio web de EasyAR.
Definicion del dispositivo
Sobrescriba IsHMD para definir si el dispositivo es un headset.
Por ejemplo, establezcalo en true en un headset.
public override bool IsHMD { get => true; }
Sobrescriba Display para definir la pantalla del dispositivo.
Por ejemplo, en un headset, la informacion de pantalla predeterminada Display.DefaultHMDDisplay define la rotacion de pantalla como 0.
protected override IDisplay Display => easyar.Display.DefaultHMDDisplay;
Disponibilidad
Sobrescriba IsAvailable para definir si el dispositivo esta disponible.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
protected override Optional<bool> IsAvailable => Application.platform == RuntimePlatform.Android;
Si IsAvailable no se puede determinar durante el ensamblaje de la session, puede sobrescribir la corrutina CheckAvailability() para bloquear el proceso de ensamblaje hasta que se confirme si esta disponible.
Origen de la session
Sobrescriba OriginType para definir el tipo de origen definido por el SDK del dispositivo.
Si OriginType es Custom, tambien debe sobrescribir Origin.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
protected override DeviceOriginType OriginType =>
#if EASYAR_HAVE_ROKID_UXR
hasUXRComponents ? DeviceOriginType.None :
#endif
DeviceOriginType.XROrigin;
Camara virtual
Si OriginType es Custom o None, debe sobrescribir Camera para proporcionar la camara virtual.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
protected override Camera Camera => hasUXRComponents ? (cameraCandidate ? cameraCandidate : Camera.main) : base.Camera;
Camara fisica
Sobrescriba DeviceCameras con el tipo DeviceFrameSourceCamera para proporcionar informacion de la camara fisica del dispositivo. Estos datos se usaran al introducir datos de fotogramas de camara. La creacion debe estar completa cuando CameraFrameStarted sea true.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
private DeviceFrameSourceCamera deviceCamera;
protected override List<FrameSourceCamera> DeviceCameras => new List<FrameSourceCamera> { deviceCamera };
{
var imageDimensions = new int[2];
RokidExtensionAPI.RokidOpenXR_API_GetImageDimensions(imageDimensions);
size = new Vector2Int(imageDimensions[0], imageDimensions[1]);
deviceCamera = new DeviceFrameSourceCamera(CameraDeviceType.Back, 0, size, new Vector2(50, 50), new DeviceFrameSourceCamera.CameraExtrinsics(Pose.identity, true), AxisSystemType.Unity);
started = true;
}
Sobrescriba CameraFrameStarted para proporcionar la marca que indica que ha empezado la entrada de fotogramas de camara.
Por ejemplo:
protected override bool CameraFrameStarted => started;
Inicio y detencion de la session
Sobrescriba OnSessionStart(ARSession) y realice la inicializacion especifica de AR. Asegurese de llamar primero a base.OnSessionStart.
Por ejemplo:
protected override void OnSessionStart(ARSession session)
{
base.OnSessionStart(session);
StartCoroutine(InitializeCamera());
}
Este es un lugar adecuado para abrir la camara del dispositivo, como una camara RGB o VST, especialmente si esas camaras no estan disenadas para permanecer siempre abiertas. Tambien es un lugar adecuado para obtener datos de calibracion que no cambiaran durante todo el ciclo de vida. A veces puede ser necesario esperar a que el dispositivo este listo o a que se actualicen los datos antes de poder obtenerlos.
Tambien es un lugar adecuado para iniciar el bucle de entrada de datos. Tambien puede escribir este bucle en Update() u otros metodos, especialmente cuando los datos deben obtenerse en un punto concreto del orden de ejecucion de Unity. No introduzca datos antes de que la session este lista (ready).
Si es necesario, tambien puede omitir el proceso de inicio y comprobar los datos en cada actualizacion; esto depende por completo de los requisitos concretos.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
private IEnumerator InitializeCamera()
{
yield return new WaitUntil(() => (RokidTrackingStatus)RokidExtensionAPI.RokidOpenXR_API_GetHeadTrackingStatus() >= RokidTrackingStatus.Detecting && (RokidTrackingStatus)RokidExtensionAPI.RokidOpenXR_API_GetHeadTrackingStatus() < RokidTrackingStatus.Tracking_Paused);
var focalLength = new float[2];
RokidExtensionAPI.RokidOpenXR_API_GetFocalLength(focalLength);
var principalPoint = new float[2];
RokidExtensionAPI.RokidOpenXR_API_GetPrincipalPoint(principalPoint);
var distortion = new float[5];
RokidExtensionAPI.RokidOpenXR_API_GetDistortion(distortion);
var imageDimensions = new int[2];
RokidExtensionAPI.RokidOpenXR_API_GetImageDimensions(imageDimensions);
size = new Vector2Int(imageDimensions[0], imageDimensions[1]);
var cameraParamList = new List<float> { focalLength[0], focalLength[1], principalPoint[0], principalPoint[1] }.Concat(distortion.ToList().GetRange(1, 4)).ToList();
cameraParameters = CameraParameters.tryCreateWithCustomIntrinsics(size.ToEasyARVector(), cameraParamList, CameraModelType.OpenCV_Fisheye, CameraDeviceType.Back, 0).Value;
deviceCamera = new DeviceFrameSourceCamera(CameraDeviceType.Back, 0, size, new Vector2(50, 50), new DeviceFrameSourceCamera.CameraExtrinsics(Pose.identity, true), AxisSystemType.Unity);
RokidExtensionAPI.RokidOpenXR_API_OpenCameraPreview(OnCameraDataUpdate);
started = true;
}
Sobrescriba OnSessionStop() y libere los recursos. Asegurese de llamar a base.OnSessionStop.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
protected override void OnSessionStop()
{
base.OnSessionStop();
RokidExtensionAPI.RokidOpenXR_API_CloseCameraPreview();
started = false;
StopAllCoroutines();
cameraParameters?.Dispose();
cameraParameters = null;
deviceCamera?.Dispose();
deviceCamera = null;
}
Introducir datos de fotogramas de camara
Despues de obtener una actualizacion de datos de fotogramas de camara, llame a HandleCameraFrameData(DeviceFrameSourceCamera, double, Image, CameraParameters, Pose, MotionTrackingStatus) / HandleCameraFrameData(DeviceFrameSourceCamera, double, Image, CameraParameters, Quaternion) para introducir los datos de fotogramas de camara.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
private static void OnCameraDataUpdate(IntPtr ptr, int dataSize, ushort width, ushort height, long timestamp)
{
if (!instance) { return; }
if (ptr == IntPtr.Zero || dataSize == 0 || timestamp == 0) { return; }
if (timestamp == instance.curTimestamp) { return; }
instance.curTimestamp = timestamp;
RokidExtensionAPI.RokidOpenXR_API_GetHistoryCameraPhysicsPose(timestamp, positionCache, rotationCache);
var pose = new Pose
{
position = new Vector3(positionCache[0], positionCache[1], -positionCache[2]),
rotation = new Quaternion(-rotationCache[0], -rotationCache[1], rotationCache[2], rotationCache[3]),
};
// NOTE: Use real tracking status when camera exposure if possible when writing your own device frame source.
var trackingStatus = ((RokidTrackingStatus)RokidExtensionAPI.RokidOpenXR_API_GetHeadTrackingStatus()).ToEasyARStatus();
var size = instance.size;
var pixelSize = instance.size;
var pixelFormat = PixelFormat.Gray;
var yLen = pixelSize.x * pixelSize.y;
var bufferBlockSize = yLen;
var bufferO = instance.TryAcquireBuffer(bufferBlockSize);
if (bufferO.OnNone) { return; }
var buffer = bufferO.Value;
buffer.tryCopyFrom(ptr, 0, 0, bufferBlockSize);
using (buffer)
using (var image = Image.create(buffer, pixelFormat, size.x, size.y, pixelSize.x, pixelSize.y))
{
instance.HandleCameraFrameData(instance.deviceCamera, timestamp * 1e-9, image, instance.cameraParameters, pose, trackingStatus);
}
}
Precaución
No olvide ejecutar Dispose() despues del uso, o liberar Image, Buffer y otros datos relacionados mediante mecanismos como using. De lo contrario, se produciran fugas de memoria graves, y la obtencion de buffers desde el buffer pool tambien puede fallar.
Introducir datos de fotogramas de renderizado
Despues de que los datos del dispositivo esten listos, llame a HandleRenderFrameData(double, Pose, MotionTrackingStatus) / HandleRenderFrameData(double, Quaternion) en cada fotograma de renderizado para introducir los datos de fotogramas de renderizado.
Por ejemplo, la implementacion en RokidFrameSource es la siguiente:
protected void LateUpdate()
{
if (!started) { return; }
if ((RokidTrackingStatus)RokidExtensionAPI.RokidOpenXR_API_GetHeadTrackingStatus() < RokidTrackingStatus.Detecting) { return; }
if ((RokidTrackingStatus)RokidExtensionAPI.RokidOpenXR_API_GetHeadTrackingStatus() >= RokidTrackingStatus.Tracking_Paused) { return; }
InputRenderFrameMotionData();
}
private void InputRenderFrameMotionData()
{
var timestamp = RokidExtensionAPI.RokidOpenXR_API_GetCameraPhysicsPose(positionCache, rotationCache);
var pose = new Pose
{
position = new Vector3(positionCache[0], positionCache[1], -positionCache[2]),
rotation = new Quaternion(-rotationCache[0], -rotationCache[1], rotationCache[2], rotationCache[3]),
};
if (timestamp == 0) { return; }
HandleRenderFrameData(timestamp * 1e-9, pose, ((RokidTrackingStatus)RokidExtensionAPI.RokidOpenXR_API_GetHeadTrackingStatus()).ToEasyARStatus());
}