diff --git a/README.md b/README.md index fedeb39..4a08d46 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ From there, you need to follow all the below steps if applicable to your current 1. First, you need to build the native version of raylib. To do that, go inside the `modules/raylib-python-cffi/raylib-c` directory. 2. Then, make the build directories and go into them: `mkdir -p build/out; cd build` -3. Configure raylib: `cmake -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release -DPLATFORM=DRM -DENABLE_WAYLAND_DRM_LEASING=ON -DSUPPORT_CLIPBOARD_IMAGE=ON -DCMAKE_INSTALL_PREFIX:PATH=$PWD/out ..` +3. Configure raylib: `cmake -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release -DPLATFORM=DRM -DENABLE_WAYLAND_DRM_LEASING=ON -DSUPPORT_CLIPBOARD_IMAGE=ON -DBUILD_EXAMPLES=OFF -DSUPPORT_SSH_KEYBOARD_RPI=OFF -DDISABLE_EVDEV_INPUT=ON -DCMAKE_INSTALL_PREFIX:PATH=$PWD/out ..` 4. Finally, build and install raylib: `make install -j$(nproc)` 5. After that, you need to build the Python bindings. To do that, go to the `modules/raylib-python-cffi` directory. Assuming you did everything correctly, you should be able to go 2 directories back (`../..`) to get there. 6. If you're on normal Linux and are not using Nix, do this command to build the package: `PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$PWD/raylib-c/build/out/lib64/pkgconfig/" ENABLE_WAYLAND_DRM_LEASING=YES RAYLIB_PLATFORM=DRM python3 setup.py bdist_wheel` -7. If you are using Nix/NixOS, do this command to build the package: `PKG_CONFIG_PATH_FOR_TARGET="$PKG_CONFIG_PATH_FOR_TARGET:$PWD/raylib-c/build/out/lib64/pkgconfig/" ENABLE_WAYLAND_DRM_LEASING=YES RAYLIB_PLATFORM=DRM python3 setup.py bdist_wheel; pip install dist/*.whl` +7. If you are using Nix/NixOS, do this command to build the package: `PKG_CONFIG_PATH_FOR_TARGET="$PKG_CONFIG_PATH_FOR_TARGET:$PWD/raylib-c/build/out/lib64/pkgconfig/" ENABLE_WAYLAND_DRM_LEASING=YES RAYLIB_PLATFORM=DRM python3 setup.py bdist_wheel` 8. Finally, install the package: `pip install dist/*.whl` ### Building `PyEvdi` (Linux) diff --git a/libunreal/edid.py b/libunreal/edid.py index 5c1bf3d..f103585 100644 --- a/libunreal/edid.py +++ b/libunreal/edid.py @@ -4,9 +4,10 @@ from dataclasses import dataclass import uuid @dataclass -class EvdiDisplaySpec: +class UnrealXRDisplayMetadata: edid: bytes device_vendor: str + device_quirks: dict[str, str | int] max_width: int max_height: int max_refresh_rate: int diff --git a/libunreal/linux/__init__.py b/libunreal/linux/__init__.py index 3344b39..cecdae4 100644 --- a/libunreal/linux/__init__.py +++ b/libunreal/linux/__init__.py @@ -2,13 +2,13 @@ import subprocess import os from libunreal.supported_devices import supported_devices -from libunreal.edid import EvdiDisplaySpec +from libunreal.edid import UnrealXRDisplayMetadata import pyedid -def upload_new_device_edid(display_spec: EvdiDisplaySpec, edid: bytes | bytearray): +def upload_new_device_edid(display_spec: UnrealXRDisplayMetadata, edid: bytes | bytearray): pass -def fetch_xr_glass_edid(allow_unsupported_devices) -> EvdiDisplaySpec: +def fetch_xr_glass_edid(allow_unsupported_devices) -> UnrealXRDisplayMetadata: # Scan for all VGA devices and their IDs pci_device_comand = subprocess.run(["lspci"], capture_output=True) @@ -72,11 +72,11 @@ def fetch_xr_glass_edid(allow_unsupported_devices) -> EvdiDisplaySpec: max_refresh = int(manufacturer_supported_devices[edid.name]["max_refresh"]) - return EvdiDisplaySpec(raw_edid_file, edid.manufacturer_pnp_id, max_width, max_height, max_refresh, card_device, monitor.replace(f"{card_device}-", "")) + return UnrealXRDisplayMetadata(raw_edid_file, edid.manufacturer_pnp_id, manufacturer_supported_devices[edid.name], max_width, max_height, max_refresh, card_device, monitor.replace(f"{card_device}-", "")) raise ValueError("Could not find supported device. Check if the device is plugged in. If it is plugged in and working correctly, check the README or open an issue.") -def upload_edid_firmware(display: EvdiDisplaySpec, fw: bytes | bytearray): +def upload_edid_firmware(display: UnrealXRDisplayMetadata, fw: bytes | bytearray): if display.linux_drm_connector == "" or display.linux_drm_card == "": raise ValueError("Linux DRM connector and/or Linux DRM card not specified!") diff --git a/libunreal/supported_devices.py b/libunreal/supported_devices.py index a0f7ba1..7eafd62 100644 --- a/libunreal/supported_devices.py +++ b/libunreal/supported_devices.py @@ -5,7 +5,9 @@ supported_devices: dict[str, dict[str, dict[str, str | int]]] = { "Air": { "max_width": 1920, "max_height": 1080, - "max_refresh": 120 + "max_refresh": 120, + "sensor_init_delay": 10, + "z_vector_disabled": True, } } } diff --git a/main.py b/main.py index 327cbf2..107a2b2 100755 --- a/main.py +++ b/main.py @@ -1,9 +1,14 @@ #!/usr/bin/env python3 from sys import platform +import logging import atexit import json import os +# Silence pyray init messages +raylib_python_logger = logging.getLogger("raylib") +raylib_python_logger.setLevel(logging.ERROR) + from platformdirs import user_data_dir, user_config_dir from loguru import logger import PyEvdi @@ -16,6 +21,7 @@ import libunreal default_configuration: dict[str, str | int] = { "display_angle": 45, + "display_pixel_spacing": 45, "display_count": 3, "allow_unsupported_devices": False, "allow_unsupported_vendors": False, @@ -106,7 +112,7 @@ def main(): # Get the display EDID logger.info("Attempting to read display EDID file") - edid: libunreal.EvdiDisplaySpec | None = None + edid: libunreal.UnrealXRDisplayMetadata | None = None if configuration["override_default_edid"] or configuration["allow_unsupported_vendors"]: # We need to parse it to get the maximum width, height, and refresh rate for EVDI's calculations @@ -134,7 +140,7 @@ def main(): if max_refresh == 0: raise ValueError("Could not determine maximum refresh rate from EDID file, and the refresh rate overrides aren't set!") - edid = libunreal.EvdiDisplaySpec(edid_file, parsed_edid_file.name if parsed_edid_file.name else "", max_width, max_height, max_refresh, "", "") + edid = libunreal.UnrealXRDisplayMetadata(edid_file, parsed_edid_file.name if parsed_edid_file.name else "", {}, max_width, max_height, max_refresh, "", "") else: edid = libunreal.fetch_xr_glass_edid(configuration["allow_unsupported_devices"]) @@ -176,6 +182,7 @@ def main(): logger.info("Initialized displays. Entering rendering loop") render_loop(edid, cards) + if __name__ == "__main__": print("Welcome to UnrealXR!\n") main() diff --git a/render.py b/render.py index b765038..2a07d7d 100644 --- a/render.py +++ b/render.py @@ -1,11 +1,11 @@ -from time import sleep +import time import math from loguru import logger import PyEvdi import pyray -from libunreal import EvdiDisplaySpec, MCUCallbackWrapper, start_mcu_event_listener +from libunreal import UnrealXRDisplayMetadata, MCUCallbackWrapper, start_mcu_event_listener previous_pitch = 0.0 previous_yaw = 0.0 @@ -64,7 +64,7 @@ def text_message(message: str): def stub_brightness_function(brightness: int): pass -def render_loop(display_metadata: EvdiDisplaySpec, cards: list[PyEvdi.Card]): +def render_loop(display_metadata: UnrealXRDisplayMetadata, cards: list[PyEvdi.Card]): logger.info("Starting sensor event listener") mcu_callbacks = MCUCallbackWrapper(roll_callback, pitch_callback, yaw_callback, text_message, stub_brightness_function, stub_brightness_function) @@ -73,7 +73,7 @@ def render_loop(display_metadata: EvdiDisplaySpec, cards: list[PyEvdi.Card]): logger.info("Beginning sensor initialization. Awaiting first sensor update") while (not has_gotten_pitch_callback_before) or (not has_gotten_yaw_callback_before) or (not has_gotten_roll_callback_before): - sleep(0.01) + time.sleep(0.01) logger.info("Initialized sensors") @@ -89,17 +89,34 @@ def render_loop(display_metadata: EvdiDisplaySpec, cards: list[PyEvdi.Card]): movement_vector = pyray.Vector3() look_vector = pyray.Vector3() - logger.error("QUIRK: Waiting 10 seconds before reading sensors due to sensor drift bugs") - sleep(10) - logger.error("Continuing...") + has_z_vector_disabled_quirk = False + has_sensor_init_delay_quirk = False + sensor_init_start_time = time.time() + + if "z_vector_disabled" in display_metadata.device_quirks: + logger.warning("QUIRK: The Z vector has been disabled for your specific device") + has_z_vector_disabled_quirk = True + + if "sensor_init_delay" in display_metadata.device_quirks: + logger.warning(f"QUIRK: Waiting {str(display_metadata.device_quirks["sensor_init_delay"])} second(s) before reading sensors") + logger.warning("|| MOVEMENT WILL NOT BE OPERATIONAL DURING THIS TIME. ||") + sensor_init_start_time = time.time() + has_sensor_init_delay_quirk = True while not pyray.window_should_close(): - look_vector.x = (current_yaw-previous_yaw)*6.5 - look_vector.y = (current_pitch-previous_pitch)*6.5 - # the Z vector is more trouble than its worth so it just doesn't get accounted for... - #look_vector.z = (current_roll-previous_roll)*6.5 + if has_sensor_init_delay_quirk: + if time.time() - sensor_init_start_time >= int(display_metadata.device_quirks["sensor_init_delay"]): + # Unset the quirk state + logger.info("Movement is now enabled.") + has_sensor_init_delay_quirk = False + else: + look_vector.x = (current_yaw-previous_yaw)*6.5 + look_vector.y = (current_pitch-previous_pitch)*6.5 - pyray.update_camera_pro(camera, movement_vector, look_vector, 0.0) + if not has_z_vector_disabled_quirk: + look_vector.z = (current_roll-previous_roll)*6.5 + + pyray.update_camera_pro(camera, movement_vector, look_vector, 0.0) pyray.begin_drawing() pyray.clear_background(pyray.BLACK)