// // Created by Bradley Austin Davis on 2018/10/21 // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "RenderThread.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AndroidHelper.h" struct HandController{ ovrInputTrackedRemoteCapabilities caps {}; ovrInputStateTrackedRemote state {}; ovrResult stateResult{ ovrSuccess }; ovrTracking tracking {}; ovrResult trackingResult{ ovrSuccess }; void update(ovrMobile* session, double time = 0.0) { const auto& deviceId = caps.Header.DeviceID; stateResult = vrapi_GetCurrentInputState(session, deviceId, &state.Header); trackingResult = vrapi_GetInputTrackingState(session, deviceId, 0.0, &tracking); } }; std::vector devices; QAndroidJniObject __interfaceActivity; extern "C" { JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void *) { __android_log_write(ANDROID_LOG_WARN, "QQQ", __FUNCTION__); return JNI_VERSION_1_6; } JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnCreate(JNIEnv *env, jobject obj) { __android_log_print(ANDROID_LOG_INFO, "QQQ", __FUNCTION__); __interfaceActivity = QAndroidJniObject(obj); QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() { __interfaceActivity.callMethod("onAppLoadedComplete", "()V"); QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, nullptr, nullptr); }); } JNIEXPORT void Java_io_highfidelity_oculus_OculusMobileActivity_questOnAppAfterLoad(JNIEnv *env, jobject obj) { AndroidHelper::instance().moveToThread(qApp->thread()); } JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnPause(JNIEnv *env, jobject obj) { AndroidHelper::instance().notifyEnterBackground(); } JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnResume(JNIEnv *env, jobject obj) { AndroidHelper::instance().notifyEnterForeground(); } } static const char* FRAME_FILE = "assets:/frames/20190121_1220.json"; static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) { QImage image; QImageReader(filename.c_str()).read(&image); if (layer > 0) { return; } texture->assignStoredMip(0, image.byteCount(), image.constBits()); } void RenderThread::submitFrame(const gpu::FramePointer& frame) { std::unique_lock lock(_frameLock); _pendingFrames.push(frame); } void RenderThread::move(const glm::vec3& v) { std::unique_lock lock(_frameLock); _correction = glm::inverse(glm::translate(mat4(), v)) * _correction; } void RenderThread::initialize() { std::unique_lock lock(_frameLock); setObjectName("RenderThread"); Parent::initialize(); _thread->setObjectName("RenderThread"); } void RenderThread::setup() { // Wait until the context has been moved to this thread { std::unique_lock lock(_frameLock); } ovr::VrHandler::initVr(); // Enable KHR_no_error for this context ovr::VrHandler::setHandler(this, true); makeCurrent(); // GPU library init gpu::Context::init(); _gpuContext = std::make_shared(); _backend = _gpuContext->getBackend(); _gpuContext->beginFrame(); _gpuContext->endFrame(); makeCurrent(); glGenTextures(1, &_externalTexture); glBindTexture(GL_TEXTURE_2D, _externalTexture); static const glm::u8vec4 color{ 0,1,0,0 }; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color); if (QFileInfo(FRAME_FILE).exists()) { auto frame = gpu::readFrame(FRAME_FILE, _externalTexture, &textureLoader); submitFrame(frame); } } void RenderThread::shutdown() { _activeFrame.reset(); while (!_pendingFrames.empty()) { _gpuContext->consumeFrameUpdates(_pendingFrames.front()); _pendingFrames.pop(); } _gpuContext->shutdown(); _gpuContext.reset(); } void RenderThread::handleInput() { static std::once_flag once; std::call_once(once, [&]{ withOvrMobile([&](ovrMobile* session){ int deviceIndex = 0; ovrInputCapabilityHeader capsHeader; while (vrapi_EnumerateInputDevices(session, deviceIndex, &capsHeader) >= 0) { if (capsHeader.Type == ovrControllerType_TrackedRemote) { HandController controller = {}; controller.caps.Header = capsHeader; controller.state.Header.ControllerType = ovrControllerType_TrackedRemote; vrapi_GetInputDeviceCapabilities( session, &controller.caps.Header); devices.push_back(controller); } ++deviceIndex; } }); }); auto readResult = ovr::VrHandler::withOvrMobile([&](ovrMobile *session) { for (auto &controller : devices) { controller.update(session); } }); if (readResult) { for (auto &controller : devices) { const auto &caps = controller.caps; if (controller.stateResult >= 0) { const auto &remote = controller.state; if (remote.Joystick.x != 0.0f || remote.Joystick.y != 0.0f) { glm::vec3 translation; if (caps.ControllerCapabilities & ovrControllerCaps_LeftHand) { translation = glm::vec3{0.0f, -remote.Joystick.y, 0.0f}; } else { translation = glm::vec3{remote.Joystick.x, 0.0f, -remote.Joystick.y}; } float scale = 0.1f + (1.9f * remote.GripTrigger); _correction = glm::translate(glm::mat4(), translation * scale) * _correction; } } } } } void RenderThread::renderFrame() { GLuint finalTexture = 0; glm::uvec2 finalTextureSize; const auto& tracking = beginFrame(); if (_activeFrame) { const auto& frame = _activeFrame; auto& eyeProjections = frame->stereoState._eyeProjections; auto& eyeOffsets = frame->stereoState._eyeViews; // Quest auto frameCorrection = _correction * ovr::toGlm(tracking.HeadPose.Pose); _backend->setCameraCorrection(glm::inverse(frameCorrection), frame->view); ovr::for_each_eye([&](ovrEye eye){ const auto& eyeInfo = tracking.Eye[eye]; eyeProjections[eye] = ovr::toGlm(eyeInfo.ProjectionMatrix); eyeOffsets[eye] = ovr::toGlm(eyeInfo.ViewMatrix); }); _backend->recycle(); _backend->syncCache(); _gpuContext->enableStereo(true); if (frame && !frame->batches.empty()) { _gpuContext->executeFrame(frame); } auto& glbackend = (gpu::gl::GLBackend&)(*_backend); finalTextureSize = { frame->framebuffer->getWidth(), frame->framebuffer->getHeight() }; finalTexture = glbackend.getTextureID(frame->framebuffer->getRenderBuffer(0)); } presentFrame(finalTexture, finalTextureSize, tracking); } bool RenderThread::process() { pollTask(); if (!vrActive()) { QThread::msleep(1); return true; } std::queue pendingFrames; { std::unique_lock lock(_frameLock); pendingFrames.swap(_pendingFrames); } makeCurrent(); while (!pendingFrames.empty()) { _activeFrame = pendingFrames.front(); pendingFrames.pop(); _gpuContext->consumeFrameUpdates(_activeFrame); _activeFrame->stereoState._enable = true; } handleInput(); renderFrame(); return true; }