mirror of https://github.com/bjornbytes/lovr.git
Pico: Draw the rest of the owl;
Add entrypoints, headset backend code, fill in the Activity, and add various special cases to account for the asynchronous render loop, lack of sRGB support, and OpenGL state resets.
This commit is contained in:
parent
58ab637465
commit
ac58a1aeba
|
@ -353,6 +353,10 @@ set(LOVR_SRC
|
|||
src/api/l_lovr.c
|
||||
)
|
||||
|
||||
if(LOVR_USE_PICO)
|
||||
list(REMOVE_ITEM LOVR_SRC src/main.c)
|
||||
endif()
|
||||
|
||||
if(LOVR_BUILD_SHARED)
|
||||
add_library(lovr SHARED ${LOVR_SRC})
|
||||
elseif(LOVR_BUILD_EXE)
|
||||
|
|
|
@ -87,7 +87,7 @@ static HeadsetRenderData headsetRenderData;
|
|||
static void renderHelper(void* userdata) {
|
||||
HeadsetRenderData* renderData = userdata;
|
||||
lua_State* L = renderData->L;
|
||||
#ifdef EMSCRIPTEN
|
||||
#if defined(EMSCRIPTEN) || defined(LOVR_USE_PICO)
|
||||
luax_geterror(L);
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pushcfunction(L, luax_getstack);
|
||||
|
@ -579,7 +579,7 @@ static int l_lovrHeadsetRenderTo(lua_State* L) {
|
|||
lua_settop(L, 1);
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#if defined(EMSCRIPTEN) || defined(LOVR_USE_PICO)
|
||||
if (headsetRenderData.ref != LUA_NOREF) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, headsetRenderData.ref);
|
||||
}
|
||||
|
|
|
@ -310,7 +310,7 @@ void lovrGraphicsSetCamera(Camera* camera, bool clear) {
|
|||
|
||||
if (!state.camera.canvas) {
|
||||
state.camera.canvas = state.defaultCanvas;
|
||||
lovrCanvasSetStereo(state.camera.canvas, camera->stereo);
|
||||
lovrCanvasSetStereo(state.camera.canvas, camera->stereo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +364,7 @@ Color lovrGraphicsGetBackgroundColor() {
|
|||
|
||||
void lovrGraphicsSetBackgroundColor(Color color) {
|
||||
state.backgroundColor = state.linearBackgroundColor = color;
|
||||
#ifndef LOVR_WEBGL
|
||||
#if !defined(LOVR_WEBGL) && !defined(LOVR_USE_PICO)
|
||||
gammaCorrect(&state.linearBackgroundColor);
|
||||
#endif
|
||||
}
|
||||
|
@ -789,7 +789,7 @@ void lovrGraphicsFlushMesh(Mesh* mesh) {
|
|||
}
|
||||
|
||||
void lovrGraphicsClear(Color* color, float* depth, int* stencil) {
|
||||
#ifndef LOVR_WEBGL
|
||||
#if !defined(LOVR_WEBGL) && !defined(LOVR_USE_PICO)
|
||||
if (color) gammaCorrect(color);
|
||||
#endif
|
||||
if (color || depth || stencil) lovrGraphicsFlush();
|
||||
|
|
|
@ -228,6 +228,7 @@ 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);
|
||||
|
|
|
@ -1495,6 +1495,32 @@ 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);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_BUFFER_TYPES; i++) {
|
||||
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) {
|
||||
#ifndef LOVR_WEBGL
|
||||
lovrAssert(state.activeTimer == ~0u, "Attempt to start a new GPU timer while one is already active!");
|
||||
|
|
|
@ -33,6 +33,9 @@ bool lovrHeadsetInit(HeadsetDriver* drivers, size_t count, float offset, uint32_
|
|||
#ifdef LOVR_USE_VRAPI
|
||||
case DRIVER_VRAPI: interface = &lovrHeadsetVrApiDriver; break;
|
||||
#endif
|
||||
#ifdef LOVR_USE_PICO
|
||||
case DRIVER_PICO: interface = &lovrHeadsetPicoDriver; break;
|
||||
#endif
|
||||
#ifdef LOVR_USE_WEBVR
|
||||
case DRIVER_WEBVR: interface = &lovrHeadsetWebVRDriver; break;
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#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/arr.h"
|
||||
#include "core/maf.h"
|
||||
#include "core/os.h"
|
||||
#include "core/util.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
@ -8,6 +16,7 @@
|
|||
#include <EGL/egl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
// Platform
|
||||
|
||||
|
@ -177,34 +186,70 @@ bool lovrPlatformIsKeyDown(KeyCode key) {
|
|||
|
||||
// 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 offset, uint32_t msaa) {
|
||||
state.offset = offset;
|
||||
state.clipNear = .1f;
|
||||
state.clipFar = 100.f;
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
return false;
|
||||
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 0.;
|
||||
return lovrPlatformGetTime();
|
||||
}
|
||||
|
||||
static void pico_getDisplayDimensions(uint32_t* width, uint32_t* height) {
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
*width = state.displayWidth;
|
||||
*height = state.displayHeight;
|
||||
}
|
||||
|
||||
static const float* pico_getDisplayMask(uint32_t* count) {
|
||||
|
@ -217,22 +262,28 @@ static uint32_t pico_getViewCount(void) {
|
|||
}
|
||||
|
||||
static bool pico_getViewPose(uint32_t view, float* position, float* orientation) {
|
||||
return false;
|
||||
// TODO use HmdState pose info, offset view by half ipd
|
||||
quat_init(orientation, state.headOrientation);
|
||||
return view < 2;
|
||||
}
|
||||
|
||||
static bool pico_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down) {
|
||||
return false;
|
||||
*left = *right = *up = *down = state.fov;
|
||||
return view < 2;
|
||||
}
|
||||
|
||||
static void pico_getClipDistance(float* clipNear, float* clipFar) {
|
||||
*clipNear = 0.f;
|
||||
*clipFar = 0.f;
|
||||
*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;
|
||||
}
|
||||
|
@ -243,15 +294,56 @@ static const float* pico_getBoundsGeometry(uint32_t* count) {
|
|||
}
|
||||
|
||||
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);
|
||||
return state.controllers[index].active;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pico_getVelocity(Device device, float* velocity, float* angularVelocity) {
|
||||
return false;
|
||||
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) {
|
||||
return false;
|
||||
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) {
|
||||
|
@ -259,11 +351,39 @@ static bool pico_isTouched(Device device, DeviceButton button, bool* touched) {
|
|||
}
|
||||
|
||||
static bool pico_getAxis(Device device, DeviceAxis axis, float* value) {
|
||||
return false;
|
||||
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) {
|
||||
return false;
|
||||
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) {
|
||||
|
@ -271,7 +391,8 @@ static struct ModelData* pico_newModelData(Device device) {
|
|||
}
|
||||
|
||||
static void pico_renderTo(void (*callback)(void*), void* userdata) {
|
||||
//
|
||||
state.renderCallback = callback;
|
||||
state.renderUserdata = userdata;
|
||||
}
|
||||
|
||||
static void pico_update(float dt) {
|
||||
|
@ -304,3 +425,155 @@ HeadsetInterface lovrHeadsetPicoDriver = {
|
|||
.renderTo = pico_renderTo,
|
||||
.update = pico_update
|
||||
};
|
||||
|
||||
// Activity callbacks
|
||||
|
||||
static lua_State* L;
|
||||
static lua_State* T;
|
||||
static Variant cookie;
|
||||
|
||||
static void lovrPicoBoot(void) {
|
||||
lovrAssert(lovrPlatformInit(), "Failed to initialize platform");
|
||||
lovrPlatformSetTime(0.);
|
||||
|
||||
L = luaL_newstate();
|
||||
luax_setmainthread(L);
|
||||
luaL_openlibs(L);
|
||||
|
||||
lua_getglobal(L, "package");
|
||||
lua_getfield(L, -1, "preload");
|
||||
luaL_register(L, NULL, lovrModules);
|
||||
lua_pop(L, 2);
|
||||
|
||||
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 (lua_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 }));
|
||||
}
|
||||
|
||||
Camera camera;
|
||||
camera.stereo = false;
|
||||
camera.canvas = canvas;
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
float fov = tanf(state.fov);
|
||||
mat4_fov(camera.projection[i], -fov, fov, fov, -fov, state.clipNear, state.clipFar);
|
||||
mat4_identity(camera.viewMatrix[i]);
|
||||
mat4_translate(camera.viewMatrix[i], state.headPosition[0], state.headPosition[1] + state.offset, state.headPosition[2]);
|
||||
mat4_rotateQuat(camera.viewMatrix[i], state.headOrientation);
|
||||
mat4_translate(camera.viewMatrix[i], state.ipd * (eye == 0 ? -.5f : .5f), 0.f, 0.f);
|
||||
mat4_invert(camera.viewMatrix[i]);
|
||||
}
|
||||
|
||||
lovrGpuResetState();
|
||||
lovrGraphicsSetCamera(&camera, true);
|
||||
state.renderCallback(state.renderUserdata);
|
||||
lovrGraphicsSetCamera(NULL, false);
|
||||
}
|
||||
|
|
|
@ -6,35 +6,93 @@ 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 {
|
||||
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() {
|
||||
|
@ -65,6 +123,47 @@ public class Activity extends VRActivity implements RenderInterface {
|
|||
//
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ function lovr.boot()
|
|||
timer = true
|
||||
},
|
||||
headset = {
|
||||
drivers = { 'leap', 'openxr', 'oculus', 'vrapi', 'openvr', 'webxr', 'webvr', 'desktop' },
|
||||
drivers = { 'leap', 'openxr', 'oculus', 'vrapi', 'pico', 'openvr', 'webxr', 'webvr', 'desktop' },
|
||||
offset = 1.7,
|
||||
msaa = 4
|
||||
},
|
||||
|
|
|
@ -126,7 +126,7 @@ const char* lovrShaderFragmentSuffix = ""
|
|||
" discard; \n"
|
||||
" } \n"
|
||||
"#endif \n"
|
||||
#ifdef LOVR_WEBGL
|
||||
#if defined(LOVR_WEBGL) || defined(LOVR_USE_PICO)
|
||||
" lovrCanvas[0].rgb = pow(lovrCanvas[0].rgb, vec3(.4545)); \n"
|
||||
#endif
|
||||
"#endif \n"
|
||||
|
|
Loading…
Reference in New Issue