mirror of https://github.com/bjornbytes/lovr.git
rm pico;
Notes: - We can actually use a single Activity.java file for oculus/pico now - We can unconditionally compile os_android.c on Android - No need for including extra jars in build system - Headset rendering is guaranteed synchronous now, no need to ref L - Add an "android flavor" build setting to differentiate between oculus and pico devices, since they both use OpenXR. - Update the pico manifest to reflect their OpenXR sample - Remove some OpenGL hacks that aren't necessary anymore
This commit is contained in:
parent
f1cc668298
commit
d1dc2f3199
|
@ -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
|
||||
|
|
|
@ -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/<ABI>
|
||||
# - 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
|
||||
|
|
49
Tupfile.lua
49
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',
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 418d31425e3873749bbd6a7cb97641635df6e398
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
// 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 <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
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);
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.lovr.app">
|
||||
<uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<application android:allowBackup="false" android:label="LÖVR" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||
<uses-feature android:name="android.hardware.vr.headtracking" android:required="true"/>
|
||||
<uses-feature android:glEsVersion="0x00030002" android:required="true"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<application android:allowBackup="false" android:label="LÖVR">
|
||||
<meta-data android:name="pvr.app.type" android:value="vr"/>
|
||||
<activity android:name="Activity">
|
||||
<activity android:name="Activity" android:launchMode="singleTask" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
<meta-data android:name="android.app.lib_name" android:value="lovr"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue