diff --git a/.gitmodules b/.gitmodules index 04aee479..900d4693 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,9 +16,6 @@ [submodule "deps/openxr"] path = deps/openxr url = https://github.com/khronosgroup/openxr-sdk -[submodule "deps/pico"] - path = deps/pico - url = https://github.com/lovr-org/pico_native_sdk [submodule "deps/oculus-openxr"] path = deps/oculus-openxr url = https://github.com/lovr-org/ovr_openxr_mobile_sdk diff --git a/CMakeLists.txt b/CMakeLists.txt index eb6b903a..191721a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ option(LOVR_ENABLE_TIMER "Enable the timer module" ON) option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON) option(LOVR_USE_OPENXR "Enable the OpenXR backend for the headset module" ON) option(LOVR_USE_WEBXR "Enable the WebXR backend for the headset module" OFF) -option(LOVR_USE_PICO "Enable the Pico backend for the headset module" OFF) option(LOVR_USE_DESKTOP "Enable the keyboard/mouse backend for the headset module" ON) option(LOVR_USE_STEAM_AUDIO "Enable the Steam Audio spatializer (be sure to also set LOVR_STEAM_AUDIO_PATH)" OFF) option(LOVR_USE_OCULUS_AUDIO "Enable the Oculus Audio spatializer (be sure to also set LOVR_OCULUS_AUDIO_PATH)" OFF) @@ -55,6 +54,7 @@ if(EMSCRIPTEN) set(CMAKE_EXECUTABLE_SUFFIX ".html") set(LOVR_USE_WEBXR ON) elseif(ANDROID) + set(ANDROID_FLAVOR "oculus" CACHE STRING "Which Android flavor to build (oculus or pico)") find_package(Java REQUIRED) set(LOVR_USE_DESKTOP OFF) if(LOVR_BUILD_EXE) @@ -175,10 +175,17 @@ endif() if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENXR) include_directories(deps/openxr/include) if(ANDROID) - set(LOVR_OPENXR_OCULUS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/oculus-openxr" CACHE STRING "The path to the Oculus OpenXR loader") add_library(openxr_loader SHARED IMPORTED) - set_target_properties(openxr_loader PROPERTIES IMPORTED_LOCATION "${LOVR_OPENXR_OCULUS_PATH}/Libs/Android/${ANDROID_ABI}/Release/libopenxr_loader.so") set(LOVR_OPENXR openxr_loader) + if(ANDROID_FLAVOR STREQUAL "oculus") + set(LOVR_OPENXR_OCULUS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/oculus-openxr" CACHE STRING "The path to the Oculus OpenXR loader") + set_target_properties(openxr_loader PROPERTIES IMPORTED_LOCATION "${LOVR_OPENXR_OCULUS_PATH}/Libs/Android/${ANDROID_ABI}/Release/libopenxr_loader.so") + elseif(ANDROID_FLAVOR STREQUAL "pico") + set(LOVR_OPENXR_PICO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/pico-openxr" CACHE STRING "The path to the Pico OpenXR loader") + set_target_properties(openxr_loader PROPERTIES IMPORTED_LOCATION "${LOVR_OPENXR_PICO_PATH}/Libs/Android/${ANDROID_ABI}/libopenxr_loader.so") + else() + message(FATAL_ERROR "Unsupported Android flavor (expected: oculus or pico)") + endif() else() if(LOVR_SYSTEM_OPENXR) pkg_search_module(OPENXR openxr) @@ -194,14 +201,6 @@ if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENXR) endif() endif() -# Pico Native SDK (1.3.3) -if(LOVR_ENABLE_HEADSET AND LOVR_USE_PICO) - set(LOVR_PICO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/pico/aar" CACHE STRING "The path to the Pico SDK folder") - add_library(Pvr_NativeSDK SHARED IMPORTED) - set_target_properties(Pvr_NativeSDK PROPERTIES IMPORTED_LOCATION "${LOVR_PICO_PATH}/jni/${ANDROID_ABI}/libPvr_NativeSDK.so") - set(LOVR_PICO Pvr_NativeSDK) -endif() - # pthreads if(LOVR_ENABLE_THREAD AND NOT (WIN32 OR EMSCRIPTEN)) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -302,7 +301,7 @@ set(LOVR_SRC src/util.c ) -if(LOVR_BUILD_EXE AND NOT LOVR_USE_PICO) +if(LOVR_BUILD_EXE) list(APPEND LOVR_SRC src/main.c) endif() @@ -331,7 +330,6 @@ target_link_libraries(lovr ${LOVR_OPENGL} ${LOVR_OPENXR} ${LOVR_OCULUS_AUDIO} - ${LOVR_PICO} ${LOVR_PTHREADS} ${LOVR_EMSCRIPTEN_FLAGS} ) @@ -449,10 +447,6 @@ if(LOVR_ENABLE_HEADSET) target_compile_definitions(lovr PRIVATE LOVR_USE_OPENXR) target_sources(lovr PRIVATE src/modules/headset/headset_openxr.c) endif() - if(LOVR_USE_PICO) - target_compile_definitions(lovr PRIVATE LOVR_USE_PICO) - target_sources(lovr PRIVATE src/modules/headset/headset_pico.c) - endif() if(LOVR_USE_WEBXR) target_compile_definitions(lovr PRIVATE LOVR_USE_WEBXR) target_sources(lovr PRIVATE src/modules/headset/headset_webxr.c) @@ -636,6 +630,7 @@ elseif(EMSCRIPTEN) target_compile_definitions(lovr PRIVATE LOVR_WEBGL) configure_file(src/resources/lovr.ico favicon.ico COPYONLY) elseif(ANDROID) + target_sources(lovr PRIVATE src/core/os_android.c) target_link_libraries(lovr log EGL GLESv3 android dl) target_compile_definitions(lovr PRIVATE LOVR_GLES) target_include_directories(lovr PRIVATE "${ANDROID_NDK}/sources/android/native_app_glue") @@ -651,35 +646,20 @@ elseif(ANDROID) if(LOVR_BUILD_EXE) set(ANDROID_JAR "${ANDROID_SDK}/platforms/${ANDROID_PLATFORM}/android.jar") set(ANDROID_TOOLS "${ANDROID_SDK}/build-tools/${ANDROID_BUILD_TOOLS_VERSION}") + set(ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/AndroidManifest_${ANDROID_FLAVOR}.xml" CACHE STRING "The AndroidManifest.xml file to use") # If assets are included in the apk then add '-A assets' to aapt, otherwise don't add any flags if(ANDROID_ASSETS) set(ANDROID_ASSETS -A ${ANDROID_ASSETS}) endif() - # Flavor-specific config: - # - Imported targets need to have their libraries manually copied to raw/lib/ - # - Figure out which Java class (Activity) and AndroidManifest.xml to use - # - Oculus uses the regular android os layer, pico implements its own in the headset backend - # - Some of the Pico SDK is in a jar that has to be added to the classpath and d8 invocation + if(ANDROID_PACKAGE) + set(PACKAGE_RENAME "--rename-manifest-package" "${ANDROID_PACKAGE}") + endif() + if(LOVR_USE_OPENXR) - set(MANIFEST "oculus") - set(ACTIVITY "openxr") - target_sources(lovr PRIVATE src/core/os_android.c) get_target_property(OPENXR_LIB ${LOVR_OPENXR} IMPORTED_LOCATION) file(COPY ${OPENXR_LIB} DESTINATION raw/lib/${ANDROID_ABI}) - set(ANDROID_CLASSPATH "${ANDROID_JAR}") - elseif(LOVR_USE_PICO) - set(MANIFEST "pico") - set(ACTIVITY "pico") - get_target_property(PICO_LIB ${LOVR_PICO} IMPORTED_LOCATION) - file(COPY ${PICO_LIB} DESTINATION raw/lib/${ANDROID_ABI}) - set(EXTRA_JAR "${LOVR_PICO_PATH}/classes.jar") - if(WIN32) - set(ANDROID_CLASSPATH "${ANDROID_JAR};${EXTRA_JAR}") - else() - set(ANDROID_CLASSPATH "${ANDROID_JAR}:${EXTRA_JAR}") - endif() endif() if(LOVR_USE_OCULUS_AUDIO) @@ -692,20 +672,14 @@ elseif(ANDROID) file(COPY ${PHONON_LIB} DESTINATION raw/lib/${ANDROID_ABI}) endif() - set(ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/AndroidManifest_${MANIFEST}.xml" CACHE STRING "The AndroidManifest.xml file to use") - - if(ANDROID_PACKAGE) - set(PACKAGE_RENAME "--rename-manifest-package" "${ANDROID_PACKAGE}") - endif() - # Make an apk add_custom_target( buildAPK ALL WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy "${ANDROID_MANIFEST}" AndroidManifest.xml - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity_${ACTIVITY}.java Activity.java - COMMAND ${Java_JAVAC_EXECUTABLE} -classpath "${ANDROID_CLASSPATH}" -d . Activity.java - COMMAND ${ANDROID_TOOLS}/d8 --min-api ${ANDROID_NATIVE_API_LEVEL} --output raw ${EXTRA_JAR} org/lovr/app/Activity.class + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity.java Activity.java + COMMAND ${Java_JAVAC_EXECUTABLE} -classpath "${ANDROID_JAR}" -d . Activity.java + COMMAND ${ANDROID_TOOLS}/d8 --min-api ${ANDROID_NATIVE_API_LEVEL} --output raw org/lovr/app/Activity.class COMMAND ${ANDROID_TOOLS}/aapt package -f diff --git a/Tupfile.lua b/Tupfile.lua index ab1e0179..49a4db5c 100644 --- a/Tupfile.lua +++ b/Tupfile.lua @@ -24,7 +24,6 @@ config = { headsets = { desktop = true, openxr = false, -- if provided, should be path to folder containing OpenXR loader library - pico = false, webxr = false }, spatializers = { @@ -39,6 +38,7 @@ config = { buildtools = '30.0.3', keystore = '/path/to/keystore', keystorepass = 'pass:password', + flavor = 'oculus', -- or pico manifest = nil, -- path to custom AndroidManifest.xml package = nil, -- package id, like org.lovr.app project = nil -- path to LÖVR project to include in apk @@ -156,7 +156,7 @@ if target == 'wasm' then end if target == 'android' then - assert(config.headsets.pico or config.headsets.openxr, 'Please enable pico or openxr') + assert(config.headsets.openxr, 'You probably want to enable OpenXR') hosts = { win32 = 'windows-x86_64', macos = 'darwin-x86_64', linux = 'linux-x86_64' } host = hosts[tup.getconfig('TUP_PLATFORM')] cc = ('%s/toolchains/llvm/prebuilt/%s/bin/clang'):format(config.android.ndk, host) @@ -320,12 +320,6 @@ if config.headsets.openxr then end end -if config.headsets.pico then - assert(target == 'android', 'Pico is not supported on this target') - lflags += '-lPvr_NativeSDK' - copy('deps/pico/jni/arm64-v8a/libPvr_NativeSDK.so', '$(bin)/%b') -end - if config.spatializers.oculus then cflags_headset_oculus += '-Ideps/AudioSDK/Include' ovraudio_libs = { @@ -445,43 +439,30 @@ if target == 'android' then config.android[key] = #value > 0 and value or config.android[key] end - activity = - config.headsets.pico and 'src/resources/Activity_pico.java' or - config.headsets.openxr and 'src/resources/Activity_openxr.java' - java = 'bin/Activity.java' class = 'org/lovr/app/Activity.class' binclass = 'bin/' .. class jar = 'bin/lovr.jar' dex = 'bin/apk/classes.dex' - - androidversion = config.android.version - androidjar = ('%s/platforms/android-%d/android.jar'):format(config.android.sdk, androidversion) - extrajar = config.headsets.pico and 'deps/pico/classes.jar' or nil - classpathsep = tup.getconfig('TUP_PLATFORM') == 'win32' and ';' or ':' - classpath = table.concat({ androidjar, extrajar }, classpathsep) - - package = #config.android.package > 0 and ('--rename-manifest-package ' .. config.android.package) or '' - project = #config.android.project > 0 and ('-A ' .. config.android.project) or '' - - manifest = config.android.manifest or - config.headsets.pico and 'src/resources/AndroidManifest_pico.xml' or - config.headsets.openxr and 'src/resources/AndroidManifest_oculus.xml' - - tools = config.android.sdk .. '/build-tools/' .. config.android.buildtools - - ks = config.android.keystore - kspass = config.android.keystorepass - unaligned = 'bin/.lovr.apk.unaligned' unsigned = 'bin/.lovr.apk.unsigned' apk = 'bin/lovr.apk' + manifest = config.android.manifest or ('src/resources/AndroidManifest_%s.xml'):format(config.android.flavor) + package = #config.android.package > 0 and ('--rename-manifest-package ' .. config.android.package) or '' + project = #config.android.project > 0 and ('-A ' .. config.android.project) or '' + + version = config.android.version + ks = config.android.keystore + kspass = config.android.keystorepass + androidjar = ('%s/platforms/android-%d/android.jar'):format(config.android.sdk, version) + tools = config.android.sdk .. '/build-tools/' .. config.android.buildtools + copy(manifest, 'bin/AndroidManifest.xml') - copy(activity, java) - tup.rule(java, '^ JAVAC %b^ javac -classpath $(classpath) -d bin %f', binclass) + copy('src/resources/Activity.java', java) + tup.rule(java, '^ JAVAC %b^ javac -classpath $(androidjar) -d bin %f', binclass) tup.rule(binclass, '^ JAR %b^ jar -cf %o -C bin $(class)', jar) - tup.rule({ jar, extrajar }, '^ D8 %b^ $(tools)/d8 --min-api $(androidversion) --output bin/apk %f', dex) + tup.rule(jar, '^ D8 %b^ $(tools)/d8 --min-api $(version) --output bin/apk %f', dex) tup.rule( { 'bin/AndroidManifest.xml', extra_inputs = { lib('*'), dex } }, '^ AAPT %b^ $(tools)/aapt package $(package) -F %o -M %f -0 so -I $(androidjar) $(project) bin/apk', diff --git a/deps/pico b/deps/pico deleted file mode 160000 index 418d3142..00000000 --- a/deps/pico +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 418d31425e3873749bbd6a7cb97641635df6e398 diff --git a/src/api/l_headset.c b/src/api/l_headset.c index f33a7a28..dc7d5ae9 100644 --- a/src/api/l_headset.c +++ b/src/api/l_headset.c @@ -12,7 +12,6 @@ StringEntry lovrHeadsetDriver[] = { [DRIVER_DESKTOP] = ENTRY("desktop"), [DRIVER_OPENXR] = ENTRY("openxr"), - [DRIVER_PICO] = ENTRY("pico"), [DRIVER_WEBXR] = ENTRY("webxr"), { 0 } }; @@ -74,32 +73,11 @@ StringEntry lovrDeviceAxis[] = { { 0 } }; -typedef struct { - lua_State* L; - int ref; -} HeadsetRenderData; - -static HeadsetRenderData headsetRenderData; - static void renderHelper(void* userdata) { - HeadsetRenderData* renderData = userdata; - lua_State* L = renderData->L; -#ifdef LOVR_USE_PICO - luax_geterror(L); - if (lua_isnil(L, -1) && renderData->ref != LUA_REFNIL) { - lua_pushcfunction(L, luax_getstack); - lua_rawgeti(L, LUA_REGISTRYINDEX, renderData->ref); - if (lua_pcall(L, 0, 0, -2)) { - luax_seterror(L); - } - lua_pop(L, 1); // pop luax_getstack - } - lua_pop(L, 1); -#else + lua_State* L = userdata; if (lua_isfunction(L, -1)) { lua_call(L, 0, 0); } -#endif } static Device luax_optdevice(lua_State* L, int index) { @@ -596,20 +574,7 @@ static int l_lovrHeadsetAnimate(lua_State* L) { static int l_lovrHeadsetRenderTo(lua_State* L) { lua_settop(L, 1); - -#ifdef LOVR_USE_PICO - if (headsetRenderData.ref != LUA_NOREF) { - luaL_unref(L, LUA_REGISTRYINDEX, headsetRenderData.ref); - } - - headsetRenderData.ref = luaL_ref(L, LUA_REGISTRYINDEX); - lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); - headsetRenderData.L = lua_tothread(L, -1); - lua_pop(L, 1); -#else - headsetRenderData.L = L; -#endif - lovrHeadsetDisplayDriver->renderTo(renderHelper, &headsetRenderData); + lovrHeadsetDisplayDriver->renderTo(renderHelper, L); lovrGraphicsSetViewMatrix(0, NULL); lovrGraphicsSetViewMatrix(1, NULL); lovrGraphicsSetProjection(0, NULL); @@ -769,7 +734,5 @@ int luaopen_lovr_headset(lua_State* L) { luax_atexit(L, lovrHeadsetDestroy); lovrHeadsetInit(drivers, driverCount, supersample, offset, msaa, overlay); - - headsetRenderData.ref = LUA_NOREF; return 1; } diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 4eef8fc1..6f9c92fe 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -401,7 +401,7 @@ Color lovrGraphicsGetBackgroundColor() { void lovrGraphicsSetBackgroundColor(Color color) { state.backgroundColor = state.linearBackgroundColor = color; -#if !defined(LOVR_WEBGL) && !defined(LOVR_USE_PICO) +#if !defined(LOVR_WEBGL) gammaCorrect(&state.linearBackgroundColor); #endif } @@ -840,7 +840,7 @@ void lovrGraphicsFlushMesh(Mesh* mesh) { } void lovrGraphicsClear(Color* color, float* depth, int* stencil) { -#if !defined(LOVR_WEBGL) && !defined(LOVR_USE_PICO) +#if !defined(LOVR_WEBGL) if (color) gammaCorrect(color); #endif if (color || depth || stencil) lovrGraphicsFlush(); diff --git a/src/modules/graphics/graphics.h b/src/modules/graphics/graphics.h index 2cce1fe7..b26b508d 100644 --- a/src/modules/graphics/graphics.h +++ b/src/modules/graphics/graphics.h @@ -236,7 +236,6 @@ void lovrGpuDraw(DrawCommand* draw); void lovrGpuStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata); void lovrGpuPresent(void); void lovrGpuDirtyTexture(void); -void lovrGpuResetState(void); void lovrGpuTick(const char* label); double lovrGpuTock(const char* label); const GpuFeatures* lovrGpuGetFeatures(void); diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index a996d069..fe01aaeb 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1539,35 +1539,6 @@ void lovrGpuDirtyTexture() { state.textures[state.activeTexture] = NULL; } -// This doesn't actually reset all state, just state that is known to be changed externally -void lovrGpuResetState() { - if (state.vertexArray) { - glBindVertexArray(state.vertexArray->vao); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.vertexArray->ibo); - } - - for (size_t i = 0; i < MAX_BUFFER_TYPES; i++) { - if (!state.vertexArray || i != BUFFER_INDEX) { - glBindBuffer(convertBufferType(i), state.buffers[i]); - } - } - - glBindFramebuffer(GL_FRAMEBUFFER, state.framebuffer); - glUseProgram(state.program); - - if (state.blendEnabled) { - glEnable(GL_BLEND); - } else { - glDisable(GL_BLEND); - } - - if (state.depthEnabled) { - glEnable(GL_DEPTH_TEST); - } else { - glDisable(GL_DEPTH_TEST); - } -} - void lovrGpuTick(const char* label) { #ifdef LOVR_GL lovrAssert(state.activeTimer == ~0u, "Attempt to start a new GPU timer while one is already active!"); diff --git a/src/modules/headset/headset.c b/src/modules/headset/headset.c index 5f484dec..d5ee1b3c 100644 --- a/src/modules/headset/headset.c +++ b/src/modules/headset/headset.c @@ -21,9 +21,6 @@ bool lovrHeadsetInit(HeadsetDriver* drivers, size_t count, float supersample, fl #ifdef LOVR_USE_OPENXR case DRIVER_OPENXR: interface = &lovrHeadsetOpenXRDriver; break; #endif -#ifdef LOVR_USE_PICO - case DRIVER_PICO: interface = &lovrHeadsetPicoDriver; break; -#endif #ifdef LOVR_USE_WEBXR case DRIVER_WEBXR: interface = &lovrHeadsetWebXRDriver; break; #endif diff --git a/src/modules/headset/headset.h b/src/modules/headset/headset.h index 08af1573..54d424cb 100644 --- a/src/modules/headset/headset.h +++ b/src/modules/headset/headset.h @@ -13,7 +13,6 @@ struct Texture; typedef enum { DRIVER_DESKTOP, DRIVER_OPENXR, - DRIVER_PICO, DRIVER_WEBXR } HeadsetDriver; @@ -145,7 +144,6 @@ typedef struct HeadsetInterface { // Available drivers extern HeadsetInterface lovrHeadsetOpenXRDriver; -extern HeadsetInterface lovrHeadsetPicoDriver; extern HeadsetInterface lovrHeadsetWebXRDriver; extern HeadsetInterface lovrHeadsetDesktopDriver; diff --git a/src/modules/headset/headset_pico.c b/src/modules/headset/headset_pico.c deleted file mode 100644 index af565c26..00000000 --- a/src/modules/headset/headset_pico.c +++ /dev/null @@ -1,611 +0,0 @@ -#include "headset/headset.h" -#include "event/event.h" -#include "graphics/graphics.h" -#include "graphics/canvas.h" -#include "resources/boot.lua.h" -#include "api/api.h" -#include "core/maf.h" -#include "core/os.h" -#include "util.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Platform - -static struct { - fn_permission* onPermissionEvent; -} os; - -bool os_init() { - os_open_console(); - return true; -} - -void os_destroy() { - // -} - -const char* os_get_name() { - return "Android"; -} - -uint32_t os_get_core_count() { - return sysconf(_SC_NPROCESSORS_ONLN); -} - -// To make regular printing work, a thread makes a pipe and redirects stdout and stderr to the write -// end of the pipe. The read end of the pipe is forwarded to __android_log_write. -static struct { - int handles[2]; - pthread_t thread; -} logState; - -static void* log_main(void* data) { - int* fd = data; - pipe(fd); - dup2(fd[1], STDOUT_FILENO); - dup2(fd[1], STDERR_FILENO); - setvbuf(stdout, 0, _IOLBF, 0); - setvbuf(stderr, 0, _IONBF, 0); - ssize_t length; - char buffer[1024]; - while ((length = read(fd[0], buffer, sizeof(buffer) - 1)) > 0) { - buffer[length] = '\0'; - __android_log_write(ANDROID_LOG_DEBUG, "LOVR", buffer); - } - return 0; -} - -void os_open_console() { - pthread_create(&logState.thread, NULL, log_main, logState.handles); - pthread_detach(logState.thread); -} - -#define NS_PER_SEC 1000000000ULL - -double os_get_time() { - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - return (double) t.tv_sec + (t.tv_nsec / (double) NS_PER_SEC); -} - -void os_sleep(double seconds) { - seconds += .5e-9; - struct timespec t; - t.tv_sec = seconds; - t.tv_nsec = (seconds - t.tv_sec) * NS_PER_SEC; - while (nanosleep(&t, &t)); -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPermissionEvent(JNIEnv* jni, jobject activity, jint permission, jboolean granted) { - if (os.onPermissionEvent) { - os.onPermissionEvent(permission, granted); - } -} - -void os_request_permission(os_permission permission) { - // TODO -} - -void os_poll_events() { - // -} - -void os_on_quit(fn_quit* callback) { - // -} - -void os_on_focus(fn_focus* callback) { - // -} - -void os_on_resize(fn_resize* callback) { - // -} - -void os_on_key(fn_key* callback) { - // -} - -void os_on_text(fn_text* callback) { - // TODO -} - -void os_on_permission(fn_permission* callback) { - os.onPermissionEvent = callback; -} - -bool os_window_open(const os_window_config* config) { - return true; -} - -bool os_window_is_open() { - return false; -} - -void os_window_get_size(int* width, int* height) { - if (width) *width = 0; - if (height) *height = 0; -} - -void os_window_get_fbsize(int* width, int* height) { - *width = 0; - *height = 0; -} - -void os_window_set_vsync(int interval) { - // -} - -void os_window_swap() { - // -} - -fn_gl_proc* os_get_gl_proc_address(const char* function) { - return eglGetProcAddress(function); -} - -size_t os_get_home_directory(char* buffer, size_t size) { - return 0; -} - -size_t os_get_data_directory(char* buffer, size_t size) { - buffer[0] = '\0'; - return 0; -} - -size_t os_get_working_directory(char* buffer, size_t size) { - return getcwd(buffer, size) ? strlen(buffer) : 0; -} - -size_t os_get_executable_path(char* buffer, size_t size) { - ssize_t length = readlink("/proc/self/exe", buffer, size - 1); - if (length >= 0) { - buffer[length] = '\0'; - return length; - } else { - return 0; - } -} - -static char apkPath[1024]; -size_t os_get_bundle_path(char* buffer, size_t size, const char** root) { - size_t length = strlen(apkPath); - if (length >= size) return 0; - memcpy(buffer, apkPath, length); - buffer[length] = '\0'; - *root = "/assets"; - return length; -} - -void os_get_mouse_position(double* x, double* y) { - *x = *y = 0.; -} - -void os_set_mouse_mode(os_mouse_mode mode) { - // -} - -bool os_is_mouse_down(os_mouse_button button) { - return false; -} - -bool os_is_key_down(os_key key) { - return false; -} - -// Headset backend - -typedef struct { - GLint id; - Canvas* instance; -} NativeCanvas; - -static struct { - float offset; - float clipNear; - float clipFar; - uint32_t displayWidth; - uint32_t displayHeight; - float headPosition[4]; - float headOrientation[4]; - float fov; - float ipd; - struct { - bool active; - uint16_t buttons; - uint16_t changed; - float trigger; - float thumbstick[2]; - float position[4]; - float orientation[4]; - float hapticStrength; - float hapticDuration; - } controllers[2]; - arr_t(NativeCanvas) canvases; - void (*renderCallback)(void*); - void* renderUserdata; -} state; - -static bool pico_init(float supersample, float offset, uint32_t msaa, bool overlay) { - state.offset = offset; - state.clipNear = .1f; - state.clipFar = 100.f; - return true; -} - -static void pico_start(void) { - // -} - -static void pico_destroy(void) { - arr_free(&state.canvases); - memset(&state, 0, sizeof(state)); -} - -// TODO use presence of controllers to determine G2 vs Neo (isControllerServiceExisted) -static bool pico_getName(char* name, size_t length) { - strncpy(name, "Pico", length - 1); - name[length - 1] = '\0'; - return true; -} - -// The Unity/Unreal SDKs expose true origin types (Pvr_SetTrackingOrigin) but there does not appear -// to be a way to access this from the Native SDK. Pose information appears to be relative to the -// initial head pose. -static HeadsetOrigin pico_getOriginType(void) { - return ORIGIN_HEAD; -} - -static double pico_getDisplayTime(void) { - return os_get_time(); -} - -static void pico_getDisplayDimensions(uint32_t* width, uint32_t* height) { - *width = state.displayWidth; - *height = state.displayHeight; -} - -static const float* pico_getDisplayMask(uint32_t* count) { - *count = 0; - return NULL; -} - -static uint32_t pico_getViewCount(void) { - return 2; -} - -static bool pico_getViewPose(uint32_t view, float* position, float* orientation) { - vec3_init(position, state.headPosition); - quat_init(orientation, state.headOrientation); - position[1] += state.offset; - return view < 2; -} - -static bool pico_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down) { - *left = *right = *up = *down = state.fov; - return view < 2; -} - -static void pico_getClipDistance(float* clipNear, float* clipFar) { - *clipNear = state.clipNear; - *clipFar = state.clipFar; -} - -static void pico_setClipDistance(float clipNear, float clipFar) { - state.clipNear = clipNear; - state.clipFar = clipFar; -} - -// The Unity/Unreal SDKs expose something called "SeeThrough" that is very similar to the Oculus -// Guardian API, but this does not appear to be in the Native SDK -static void pico_getBoundsDimensions(float* width, float* depth) { - *width = *depth = 0.f; -} - -static const float* pico_getBoundsGeometry(uint32_t* count) { - *count = 0; - return NULL; -} - -static bool pico_getPose(Device device, float* position, float* orientation) { - if (device == DEVICE_HEAD) { - vec3_init(position, state.headPosition); - quat_init(orientation, state.headOrientation); - position[1] += state.offset; - return true; - } - - if (device == DEVICE_HAND_LEFT || device == DEVICE_HAND_RIGHT) { - uint32_t index = device - DEVICE_HAND_LEFT; - vec3_init(position, state.controllers[index].position); - quat_init(orientation, state.controllers[index].orientation); - position[1] += state.offset; - return state.controllers[index].active; - } - - return false; -} - -static bool pico_getVelocity(Device device, float* velocity, float* angularVelocity) { - return false; // Controllers only expose acceleration and angular velocity, so we skip it -} - -static bool pico_isDown(Device device, DeviceButton button, bool* down, bool* changed) { - if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) { - return false; - } - - uint32_t index = device - DEVICE_HAND_LEFT; - - if (!state.controllers[index].active) { - return false; - } - - bool active = true; - uint16_t mask = 0; - - switch (button) { - case BUTTON_TRIGGER: mask = 1 << 0; break; - case BUTTON_THUMBSTICK: mask = 1 << 1; break; - case BUTTON_GRIP: mask = 1 << 2; break; - case BUTTON_MENU: mask = 1 << 3; break; - case BUTTON_A: mask = 1 << 4; active = index == 1; break; - case BUTTON_X: mask = 1 << 4; active = index == 0; break; - case BUTTON_B: mask = 1 << 5; active = index == 1; break; - case BUTTON_Y: mask = 1 << 5; active = index == 0; break; - default: return false; - } - - *down = state.controllers[index].buttons & mask; - *changed = state.controllers[index].changed & mask; - return active; -} - -static bool pico_isTouched(Device device, DeviceButton button, bool* touched) { - return false; -} - -static bool pico_getAxis(Device device, DeviceAxis axis, float* value) { - if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) { - return false; - } - - uint32_t index = device - DEVICE_HAND_LEFT; - - if (!state.controllers[index].active) { - return false; - } - - switch (axis) { - case AXIS_TRIGGER: - *value = state.controllers[index].trigger; - return true; - case AXIS_THUMBSTICK: - value[0] = state.controllers[index].thumbstick[0]; - value[1] = state.controllers[index].thumbstick[1]; - return true; - default: - return false; - } -} - -static bool pico_vibrate(Device device, float strength, float duration, float frequency) { - if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) { - return false; - } - - uint32_t index = device - DEVICE_HAND_LEFT; - - state.controllers[index].hapticStrength = strength; - state.controllers[index].hapticDuration = duration; - return true; -} - -static struct ModelData* pico_newModelData(Device device, bool animated) { - return NULL; -} - -static bool pico_animate(Device device, struct Model* model) { - return false; -} - -static void pico_renderTo(void (*callback)(void*), void* userdata) { - state.renderCallback = callback; - state.renderUserdata = userdata; -} - -static void pico_update(float dt) { - // -} - -HeadsetInterface lovrHeadsetPicoDriver = { - .driverType = DRIVER_PICO, - .init = pico_init, - .start = pico_start, - .destroy = pico_destroy, - .getName = pico_getName, - .getOriginType = pico_getOriginType, - .getDisplayTime = pico_getDisplayTime, - .getDisplayDimensions = pico_getDisplayDimensions, - .getDisplayMask = pico_getDisplayMask, - .getViewCount = pico_getViewCount, - .getViewPose = pico_getViewPose, - .getViewAngles = pico_getViewAngles, - .getClipDistance = pico_getClipDistance, - .setClipDistance = pico_setClipDistance, - .getBoundsDimensions = pico_getBoundsDimensions, - .getBoundsGeometry = pico_getBoundsGeometry, - .getPose = pico_getPose, - .getVelocity = pico_getVelocity, - .isDown = pico_isDown, - .isTouched = pico_isTouched, - .getAxis = pico_getAxis, - .vibrate = pico_vibrate, - .newModelData = pico_newModelData, - .animate = pico_animate, - .renderTo = pico_renderTo, - .update = pico_update -}; - -// Activity callbacks - -#include -#include -#include - -static struct lua_State* L; -static struct lua_State* T; -static Variant cookie; - -static void lovrPicoBoot(void) { - lovrAssert(os_init(), "Failed to initialize platform"); - - L = luaL_newstate(); - luax_setmainthread(L); - luaL_openlibs(L); - luax_preload(L); - - lua_pushcfunction(L, luax_getstack); - if (luaL_loadbuffer(L, (const char*) src_resources_boot_lua, src_resources_boot_lua_len, "@boot.lua") || lua_pcall(L, 0, 1, -2)) { - fprintf(stderr, "%s\n", lua_tostring(L, -1)); - return; - } - - T = lua_newthread(L); - lua_pushvalue(L, -2); - lua_xmove(L, T, 1); - - lovrSetErrorCallback(luax_vthrow, T); - lovrSetLogCallback(luax_vlog, T); -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPicoOnCreate(JNIEnv* jni, jobject activity, jstring apk) { - const char* path = (*jni)->GetStringUTFChars(jni, apk, NULL); - size_t length = strlen(path); - if (length < sizeof(apkPath)) { - memcpy(apkPath, path, length); - } - lovrPicoBoot(); -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPicoSetDisplayDimensions(JNIEnv* jni, jobject activity, int width, int height) { - state.displayWidth = width; - state.displayHeight = height; -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPicoUpdateControllerPose(JNIEnv* jni, jobject activity, int hand, bool active, float x, float y, float z, float qx, float qy, float qz, float qw) { - state.controllers[hand].active = active; - vec3_set(state.controllers[hand].position, x, y, z); - quat_set(state.controllers[hand].orientation, -qx, -qy, qz, qw); -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPicoUpdateControllerInput(JNIEnv* jni, jobject activity, int hand, int buttons, float trigger, float thumbstickX, float thumbstickY) { - state.controllers[hand].changed = state.controllers[hand].buttons ^ buttons; - state.controllers[hand].buttons = (uint16_t) buttons; - state.controllers[hand].trigger = trigger; - state.controllers[hand].thumbstick[0] = thumbstickX; - state.controllers[hand].thumbstick[1] = thumbstickY; -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPicoOnFrame(JNIEnv* jni, jobject activity, float x, float y, float z, float qx, float qy, float qz, float qw, float fov, float ipd) { - vec3_set(state.headPosition, x, y, z); - quat_set(state.headOrientation, qx, qy, qz, qw); - state.fov = fov * M_PI / 180.f; - state.ipd = ipd; - - // Haptics - for (uint32_t i = 0; i < 2; i++) { - if (state.controllers[i].hapticStrength > 0.f) { - float strength = state.controllers[i].hapticStrength; - float duration = state.controllers[i].hapticDuration; - jclass class = (*jni)->GetObjectClass(jni, activity); - jmethodID vibrate = (*jni)->GetMethodID(jni, class, "vibrate", "(IFF)V"); - (*jni)->CallObjectMethod(jni, activity, vibrate, i, strength, duration); - state.controllers[i].hapticStrength = 0.f; - } - } - - // Resume the lovr.run coroutine, and if it returns (doesn't yield) then either reboot or exit - if (L && T) { - luax_geterror(T); - luax_clearerror(T); - if (luax_resume(T, 1) != LUA_YIELD) { - bool restart = lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart"); - if (restart) { - luax_checkvariant(T, 2, &cookie); - if (cookie.type == TYPE_OBJECT) { - cookie.type = TYPE_NIL; - memset(&cookie.value, 0, sizeof(cookie.value)); - } - lua_close(L); - lovrPicoBoot(); - } else { - lua_close(L); - L = NULL; - T = NULL; - - // Call 'finish' method on the Activity - jclass class = (*jni)->GetObjectClass(jni, activity); - jmethodID finish = (*jni)->GetMethodID(jni, class, "finish", "()V"); - (*jni)->CallObjectMethod(jni, activity, finish); - } - } - } -} - -JNIEXPORT void JNICALL Java_org_lovr_app_Activity_lovrPicoDrawEye(JNIEnv* jni, jobject object, int eye) { - if (!state.renderCallback) return; - - // Pico modifies a lot of global OpenGL state, including the framebuffer binding, VAO binding, - // buffer bindings, blending, and depth test settings. Since there is no swapchain or texture - // submission API, we have to render into the currently active OpenGL framebuffer, so a cache of - // native Canvas objects is used for that. For the rest of the states, there is a new "nuke all - // OpenGL state" function added to clear any changes made by Pico (lovrGpuResetState) :( - - GLint framebuffer; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer); - - Canvas* canvas = NULL; - for (uint32_t i = 0; i < state.canvases.length; i++) { - if (state.canvases.data[i].id == framebuffer) { - canvas = state.canvases.data[i].instance; - break; - } - } - - if (!canvas) { - CanvasFlags flags = { .depth.enabled = true }; - canvas = lovrCanvasCreateFromHandle(state.displayWidth, state.displayHeight, flags, framebuffer, 0, 0, 1, true); - arr_push(&state.canvases, ((NativeCanvas) { .id = framebuffer, .instance = canvas })); - } - - // start each eye from origin - lovrGraphicsOrigin(); - - for (uint32_t i = 0; i < 2; i++) { - float view[16]; - mat4_identity(view); - mat4_translate(view, state.headPosition[0], state.headPosition[1] + state.offset, state.headPosition[2]); - mat4_rotateQuat(view, state.headOrientation); - mat4_translate(view, state.ipd * (eye == 0 ? -.5f : .5f), 0.f, 0.f); - mat4_invert(view); - lovrGraphicsSetViewMatrix(i, view); - - float projection[16]; - mat4_fov(projection, state.fov, state.fov, state.fov, state.fov, state.clipNear, state.clipFar); - lovrGraphicsSetProjection(i, projection); - } - - lovrGpuResetState(); - lovrGraphicsSetBackbuffer(canvas, false, true); - state.renderCallback(state.renderUserdata); - lovrGraphicsSetBackbuffer(NULL, false, false); -} diff --git a/src/resources/Activity_openxr.java b/src/resources/Activity.java similarity index 100% rename from src/resources/Activity_openxr.java rename to src/resources/Activity.java diff --git a/src/resources/Activity_pico.java b/src/resources/Activity_pico.java deleted file mode 100644 index b7ed72e4..00000000 --- a/src/resources/Activity_pico.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.lovr.app; - -import android.os.Bundle; - -import com.picovr.vractivity.Eye; -import com.picovr.vractivity.HmdState; -import com.picovr.vractivity.RenderInterface; -import com.picovr.vractivity.VRActivity; -import com.picovr.cvclient.ButtonNum; -import com.picovr.cvclient.CVController; -import com.picovr.cvclient.CVControllerListener; -import com.picovr.cvclient.CVControllerManager; -import com.picovr.picovrlib.cvcontrollerclient.ControllerClient; -import com.psmart.vrlib.PicovrSDK; - -public class Activity extends VRActivity implements RenderInterface, CVControllerListener { - CVControllerManager controllerManager; - boolean controllersActive; - - // Activity - - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - - if (ControllerClient.isControllerServiceExisted(this)) { - controllerManager = new CVControllerManager(this); - controllerManager.setListener(this); - } - - lovrPicoOnCreate(getPackageCodePath()); - } - - public void onPause() { - super.onPause(); - if (controllerManager != null) { - controllerManager.unbindService(); - } - } - - public void onResume() { - super.onResume(); - PicovrSDK.SetEyeBufferSize(1920, 1920); - if (controllerManager != null) { - controllerManager.bindService(); - } - } - - // RenderInterface - - public void initGL(int width, int height) { - lovrPicoSetDisplayDimensions(width, height); - } - - public void onFrameBegin(HmdState state) { - if (controllersActive) { - for (int i = 0; i < 2; i++) { - CVController controller = (i == 0) ? - controllerManager.getMainController() : - controllerManager.getSubController(); - - if (controller == null || controller.getConnectState() == 0) { - lovrPicoUpdateControllerPose(i, false, 0, 0, 0, 0, 0, 0, 0); - continue; - } - - float p[] = controller.getPosition(); - float q[] = controller.getOrientation(); - lovrPicoUpdateControllerPose(i, true, p[0], p[1], p[2], q[0], q[1], q[2], q[3]); - - int thumbstick[] = controller.getTouchPad(); - float trigger = (float) controller.getTriggerNum() / 255.f; - float thumbstickX = ((float) thumbstick[1] - 128.f) / (thumbstick[1] > 128 ? 127.f : 128.f); - float thumbstickY = ((float) thumbstick[0] - 128.f) / (thumbstick[0] > 128 ? 127.f : 128.f); - - int buttons = 0; - ButtonNum gripButton = (i == 0) ? ButtonNum.buttonRG : ButtonNum.buttonLG; // Yes I know - buttons |= trigger >= .9f ? (1 << 0) : 0; - buttons |= controller.getButtonState(ButtonNum.click) ? (1 << 1) : 0; - buttons |= controller.getButtonState(gripButton) ? (1 << 2) : 0; - buttons |= controller.getButtonState(ButtonNum.app) ? (1 << 3) : 0; - buttons |= controller.getButtonState(ButtonNum.buttonAX) ? (1 << 4) : 0; - buttons |= controller.getButtonState(ButtonNum.buttonBY) ? (1 << 5) : 0; - lovrPicoUpdateControllerInput(i, buttons, trigger, thumbstickX, thumbstickY); - } - } - - float p[] = state.getPos(); - float q[] = state.getOrientation(); - float fov = state.getFov(); - float ipd = state.getIpd(); - lovrPicoOnFrame(p[0], p[1], p[2], q[0], q[1], q[2], q[3], fov, ipd); - } - - public void onDrawEye(Eye eye) { - lovrPicoDrawEye(eye.getType()); - } - - public void onFrameEnd() { - // - } - - public void onRenderPause() { - // - } - - public void onRenderResume() { - // - } - - public void onRendererShutdown() { - // - } - - public void surfaceChangedCallBack(int width, int height) { - // - } - - public void renderEventCallBack(int i) { - // - } - - public void onTouchEvent() { - // - } - - // CVControllerListener - - public void onBindSuccess() { - // - } - - public void onBindFail() { - controllersActive = false; - } - - public void onThreadStart() { - controllersActive = true; - } - - public void onConnectStateChanged(int serial, int state) { - // - } - - public void onMainControllerChanged(int serial) { - // - } - - public void onChannelChanged(int device, int channel) { - // - } - - // Native - protected native void lovrPicoOnCreate(String apkPath); - protected native void lovrPicoSetDisplayDimensions(int width, int height); - protected native void lovrPicoUpdateControllerPose(int hand, boolean active, float x, float y, float z, float qx, float qy, float qz, float qw); - protected native void lovrPicoUpdateControllerInput(int hand, int buttons, float trigger, float thumbstickX, float thumbstickY); - protected native void lovrPicoOnFrame(float x, float y, float z, float qx, float qy, float qz, float qw, float fov, float ipd); - protected native void lovrPicoDrawEye(int eye); - - public void vibrate(int hand, float strength, float duration) { - if (controllerManager != null) { - int ms = (int) (duration * 1000.f); - ControllerClient.vibrateCV2ControllerStrength(strength, ms, hand); - } - } - - static { - System.loadLibrary("lovr"); - } -} diff --git a/src/resources/AndroidManifest_pico.xml b/src/resources/AndroidManifest_pico.xml index f4dacdc1..e7b82156 100644 --- a/src/resources/AndroidManifest_pico.xml +++ b/src/resources/AndroidManifest_pico.xml @@ -1,12 +1,15 @@ - - - - + + + + + + - + + diff --git a/src/resources/boot.lua b/src/resources/boot.lua index 509be513..d7636c34 100644 --- a/src/resources/boot.lua +++ b/src/resources/boot.lua @@ -121,7 +121,7 @@ function lovr.boot() debug = false }, headset = { - drivers = { 'openxr', 'pico', 'webxr', 'desktop' }, + drivers = { 'openxr', 'webxr', 'desktop' }, supersample = false, offset = 1.7, msaa = 4, diff --git a/src/resources/shaders.c b/src/resources/shaders.c index 2b332971..7c908056 100644 --- a/src/resources/shaders.c +++ b/src/resources/shaders.c @@ -129,7 +129,7 @@ const char* lovrShaderFragmentSuffix = "" " discard; \n" " } \n" "#endif \n" -#if defined(LOVR_WEBGL) || defined(LOVR_USE_PICO) +#if defined(LOVR_WEBGL) " lovrCanvas[0].rgb = pow(lovrCanvas[0].rgb, vec3(.4545)); \n" #endif "#endif \n"