void LeapMotionPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!_enabled) { return; } const auto frame = _controller.frame(); const auto frameID = frame.id(); if (_lastFrameID >= frameID) { // Leap Motion not connected or duplicate frame. return; } if (!_hasLeapMotionBeenConnected) { emit deviceConnected(getName()); _hasLeapMotionBeenConnected = true; } processFrame(frame); // Updates _joints. auto joints = _joints; auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->withLock([&, this]() { _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints); }); _prevJoints = joints; _lastFrameID = frameID; }
void FboCache::lockTexture(int texture) { withLock(_lock, [&] { Q_ASSERT(_fboMap.count(texture)); if (!_fboLocks.count(texture)) { Q_ASSERT(_readyFboQueue.front()->texture() == (GLuint)texture); _readyFboQueue.pop_front(); _fboLocks[texture] = 1; } else { _fboLocks[texture]++; } }); }
void LeapMotionPlugin::init() { loadSettings(); auto preferences = DependencyManager::get<Preferences>(); static const QString LEAPMOTION_PLUGIN { "Leap Motion" }; { auto getter = [this]()->bool { return _enabled; }; auto setter = [this](bool value) { _enabled = value; saveSettings(); if (!_enabled) { auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->withLock([&, this]() { _inputDevice->clearState(); }); } }; auto preference = new CheckPreference(LEAPMOTION_PLUGIN, "Enabled", getter, setter); preferences->addPreference(preference); } { auto getter = [this]()->QString { return _sensorLocation; }; auto setter = [this](QString value) { _sensorLocation = value; saveSettings(); applySensorLocation(); }; auto preference = new ComboBoxPreference(LEAPMOTION_PLUGIN, "Sensor location", getter, setter); QStringList list = { SENSOR_ON_DESKTOP, SENSOR_ON_HMD }; preference->setItems(list); preferences->addPreference(preference); } { auto getter = [this]()->float { return _desktopHeightOffset; }; auto setter = [this](float value) { _desktopHeightOffset = value; saveSettings(); applyDesktopHeightOffset(); }; auto preference = new SpinnerPreference(LEAPMOTION_PLUGIN, "Desktop height for horizontal forearms", getter, setter); float MIN_VALUE = 0.0f; float MAX_VALUE = 1.0f; float DECIMALS = 2.0f; float STEP = 0.01f; preference->setMin(MIN_VALUE); preference->setMax(MAX_VALUE); preference->setDecimals(DECIMALS); preference->setStep(STEP); preferences->addPreference(preference); } }
void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { std::vector<NeuronJoint> joints; { // lock and copy std::lock_guard<std::mutex> guard(_jointsMutex); joints = _joints; } auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->withLock([&, this]() { _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints); }); _prevJoints = joints; }
void KinectPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!_enabled) { return; } updateBody(); // updates _joints std::vector<KinectJoint> joints = _joints; auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->withLock([&, this]() { _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints); }); _prevJoints = joints; }
QOpenGLFramebufferObject* FboCache::getReadyFbo() { QOpenGLFramebufferObject* result = nullptr; withLock(_lock, [&] { // Delete any FBOs queued for deletion _destroyFboQueue.clear(); if (_readyFboQueue.empty()) { qDebug() << "Building new offscreen FBO number " << _fboMap.size() + 1; result = new QOpenGLFramebufferObject(_size, QOpenGLFramebufferObject::CombinedDepthStencil); _fboMap[result->texture()] = QSharedPointer<QOpenGLFramebufferObject>(result); _readyFboQueue.push_back(result); } else { result = _readyFboQueue.front(); } }); return result; }
void FboCache::releaseTexture(int texture, GLsync readSync) { withLock(_lock, [&] { Q_ASSERT(_fboMap.count(texture)); Q_ASSERT(_fboLocks.count(texture)); int newLockCount = --_fboLocks[texture]; if (!newLockCount) { auto fbo = _fboMap[texture].data(); if (fbo->size() != _size) { // Move the old FBO to the destruction queue. // We can't destroy the FBO here because we might // not be on the right thread or have the context active _destroyFboQueue.push_back(_fboMap[texture]); _fboMap.remove(texture); } else { _readyFboQueue.push_back(fbo); } _fboLocks.remove(texture); } }); }
void runOvrThread() { // Make the shared context current glfwMakeContextCurrent(renderWindow); // Each thread requires it's own glewInit call. glewInit(); // Synchronization to determine when a given eye's render commands have completed GLsync eyeFences[2]{0, 0}; // The index of the current rendering target framebuffer. int backBuffers[2]{0, 0}; // The pose for each rendered framebuffer ovrPosef backPoses[2]; // Offscreen rendering targets. two for each eye. // One is used for rendering while the other is used for distortion gl::FrameBufferWrapper frameBuffers[2][2]; for_each_eye([&](ovrEyeType eye) { glm::uvec2 frameBufferSize = Rift::fromOvr(eyeTextures[0].Header.TextureSize); for (int i = 0; i < 2; ++i) { frameBuffers[i][eye].init(frameBufferSize); } }); while (running) { for (int i = 0; i < 2; ++i) { for_each_eye([&](ovrEyeType eye) { if (0 != eyeFences[eye]) { GLenum result = glClientWaitSync(eyeFences[eye], GL_SYNC_FLUSH_COMMANDS_BIT, 0); switch (result) { case GL_ALREADY_SIGNALED: case GL_CONDITION_SATISFIED: withLock(ovrLock, [&]{ eyeFences[eye] = 0; int bufferIndex = backBuffers[eye]; textureIds[eye] = frameBuffers[bufferIndex][eye].color->texture; backBuffers[eye] = (bufferIndex + 1) % 2; eyePoses[eye] = backPoses[eye]; }); break; } } }); ovrEyeType eye = hmd->EyeRenderOrder[i]; if (0 != eyeFences[eye]) { continue; } gl::MatrixStack & mv = gl::Stacks::modelview(); gl::Stacks::projection().top() = projections[eye]; gl::Stacks::with_push(mv, [&]{ const ovrEyeRenderDesc & erd = eyeRenderDescs[eye]; // We can only acquire an eye pose between beginframe and endframe. // So we've arranged for the lock to be only open at those points. // The main thread will spend most of it's time in the wait. ::withLock(ovrLock, [&]{ if (running) { backPoses[eye] = ovrHmd_GetEyePose(hmd, eye); } }); { // Apply the head pose glm::mat4 m = Rift::fromOvr(backPoses[eye]); mv.preMultiply(glm::inverse(m)); // Apply the per-eye offset glm::vec3 eyeOffset = Rift::fromOvr(erd.ViewAdjust); mv.preMultiply(glm::translate(glm::mat4(), eyeOffset)); } int bufferIndex = backBuffers[eye]; gl::FrameBufferWrapper & frameBuffer = frameBuffers[bufferIndex][eye]; // Render the scene to an offscreen buffer frameBuffer.activate(); renderScene(); frameBuffer.deactivate(); eyeFences[eye] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); }); } } }
void RenderableWebEntityItem::render(RenderArgs* args) { QOpenGLContext * currentContext = QOpenGLContext::currentContext(); QSurface * currentSurface = currentContext->surface(); if (!_webSurface) { _webSurface = new OffscreenQmlSurface(); _webSurface->create(currentContext); _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); _webSurface->load("WebEntity.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _sourceUrl); _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { _webSurface->lockTexture(textureId); assert(!glGetError()); // TODO change to atomic<GLuint>? withLock(_textureLock, [&] { std::swap(_texture, textureId); }); if (textureId) { _webSurface->releaseTexture(textureId); } if (_texture) { _webSurface->makeCurrent(); glBindTexture(GL_TEXTURE_2D, _texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); _webSurface->doneCurrent(); } }); auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) { // Ignore mouse interaction if we're locked if (this->getLocked()) { return; } if (event->button() == Qt::MouseButton::RightButton) { if (event->type() == QEvent::MouseButtonPress) { const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event); _lastPress = toGlm(mouseEvent->pos()); } } if (intersection.entityID == getID()) { if (event->button() == Qt::MouseButton::RightButton) { if (event->type() == QEvent::MouseButtonRelease) { const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event); ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress); if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { AbstractViewStateInterface::instance()->postLambdaEvent([this] { QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); }); } _lastPress = ivec2(INT_MIN); } return; } // FIXME doesn't work... double click events not received if (event->type() == QEvent::MouseButtonDblClick) { AbstractViewStateInterface::instance()->postLambdaEvent([this] { _webSurface->getRootItem()->setProperty("url", _sourceUrl); }); } if (event->button() == Qt::MouseButton::MiddleButton) { if (event->type() == QEvent::MouseButtonRelease) { AbstractViewStateInterface::instance()->postLambdaEvent([this] { _webSurface->getRootItem()->setProperty("url", _sourceUrl); }); } return; } // Map the intersection point to an actual offscreen pixel glm::vec3 point = intersection.intersection; point -= getPosition(); point = glm::inverse(getRotation()) * point; point /= getDimensions(); point += 0.5f; point.y = 1.0f - point.y; point *= getDimensions() * METERS_TO_INCHES * DPI; // Forward the mouse event. QMouseEvent mappedEvent(event->type(), QPoint((int)point.x, (int)point.y), event->screenPos(), event->button(), event->buttons(), event->modifiers()); QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); } }; EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer); QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent); QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent); QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent); } glm::vec2 dims = glm::vec2(getDimensions()); dims *= METERS_TO_INCHES * DPI; // The offscreen surface is idempotent for resizes (bails early // if it's a no-op), so it's safe to just call resize every frame // without worrying about excessive overhead. _webSurface->resize(QSize(dims.x, dims.y)); currentContext->makeCurrent(currentSurface); PerformanceTimer perfTimer("RenderableWebEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Web); static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f); Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setModelTransform(getTransformToCenter()); bool textured = false, culled = false, emissive = false; if (_texture) { batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); textured = emissive = true; } DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, textured, culled, emissive); DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f)); }