diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index f0e025a33..40c5b3c9d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -30,7 +30,11 @@ namespace MWGui { ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) +#ifdef USE_OPENXR + : WindowBase("openmw_container_window_vr.layout") +#else : WindowBase("openmw_container_window.layout") +#endif , mDragAndDrop(dragAndDrop) , mSortModel(nullptr) , mModel(nullptr) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 6b400c172..70ef700ca 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -266,7 +266,11 @@ namespace MWGui // -------------------------------------------------------------------------------------------------- DialogueWindow::DialogueWindow() +#ifdef USE_OPENXR + : WindowBase("openmw_dialogue_window_vr.layout") +#else : WindowBase("openmw_dialogue_window.layout") +#endif , mIsCompanion(false) , mGoodbye(false) , mPersuasionDialog(new ResponseCallback(this)) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index c93f7472b..cce199f1d 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -103,10 +103,13 @@ namespace MWGui , mIsDrowning(false) , mDrowningFlashTheta(0.f) { -#ifndef USE_OPENXR +#ifdef USE_OPENXR + mMainWidgetBaseSize = mMainWidget->getSize(); + mMainWidget->setSize(mMainWidgetBaseSize); +#else mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); -#endif mMainWidgetBaseSize = mMainWidget->getSize(); +#endif // Energy bars getWidget(mHealthFrame, "HealthFrame"); @@ -381,9 +384,6 @@ namespace MWGui mDrowningFlashTheta += dt * osg::PI*2; mSpellIcons->updateWidgets(mEffectBox, true); - Log(Debug::Verbose) << "Size: " << mMainWidget->getSize(); - Log(Debug::Verbose) << "width: " << mMainWidget->getWidth(); - Log(Debug::Verbose) << "height: " << mMainWidget->getHeight(); if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) { diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index d64ec9c37..682b778c5 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -117,6 +117,8 @@ namespace MWGui messageBox->update(height); height += messageBox->getHeight(); } + + box->setVisible(true); } void MessageBoxManager::removeStaticMessageBox () @@ -189,6 +191,11 @@ namespace MWGui mMessageWidget->setCaptionWithReplacing(mMessage); } + MessageBox::~MessageBox() + { + setVisible(false); + } + void MessageBox::update (int height) { MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index e4e4b743c..a6f2e3f00 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -59,6 +59,7 @@ namespace MWGui { public: MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message); + ~MessageBox(); void setMessage (const std::string& message); int getHeight (); void update (int height); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index b102b13ce..75daef5b6 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -46,7 +46,11 @@ namespace namespace MWGui { TradeWindow::TradeWindow() +#ifdef USE_OPENXR + : WindowBase("openmw_trade_window_vr.layout") +#else : WindowBase("openmw_trade_window.layout") +#endif , mSortModel(nullptr) , mTradeModel(nullptr) , mItemToSell(-1) diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index dc184475c..bd859b86b 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -53,7 +53,6 @@ void WindowBase::onDoubleClick(MyGUI::Widget *_sender) void WindowBase::setVisible(bool visible) { - Log(Debug::Verbose) << mLayoutName << ".setVisible: " << visible; bool wasVisible = mMainWidget->getVisible(); mMainWidget->setVisible(visible); @@ -123,26 +122,42 @@ void NoDrop::onFrame(float dt) MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition(); +#ifdef USE_OPENXR + // Since VR mode stretches some windows to full screen, the usual outside condition + // won't work + mTransparent = false; +#endif if (mDrag->mIsOnDragAndDrop) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); while (focus && focus != mWidget) + { focus = focus->getParent(); + } if (focus == mWidget) + { mTransparent = true; + } } if (!mWidget->getAbsoluteCoord().inside(mousePos)) mTransparent = false; if (mTransparent) { +#ifndef USE_OPENXR + // These makes focus null, which messes up the logic for VR + // since i reset mTransparent to false every update. + // TODO: Is there a cleaner way? mWidget->setNeedMouseFocus(false); // Allow click-through +#endif setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5)); } else { - mWidget->setNeedMouseFocus(true); +#ifndef USE_OPENXR + mWidget->setNeedMouseFocus(true); // Allow click-through +#endif setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5)); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9b115cde2..818dd5c99 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -204,6 +204,10 @@ namespace MWGui mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale); mGuiPlatform->initialise(resourcePath, logpath); +#ifdef USE_OPENXR + mGuiPlatform->getRenderManagerPtr()->setViewSize(1024, 1024); +#endif + mGui = new MyGUI::Gui; mGui->initialise(""); @@ -1246,6 +1250,9 @@ namespace MWGui void WindowManager::windowResized(int x, int y) { +#ifdef USE_OPENXR + return; +#endif mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y); // scaled size diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index f02c6fd95..60bb55e2b 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -297,7 +297,10 @@ namespace MWPhysics auto* session = MWVR::Environment::get().getSession(); if (session) { - float yaw = session->movementYaw(); + float pitch = 0.f; + float yaw = 0.f; + session->movementAngles(yaw, pitch); + refpos.rot[0] += pitch; refpos.rot[2] += yaw; } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ff2ff2bb7..2b4579308 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -898,6 +898,7 @@ namespace MWRender const bool isPlayer = (mPtr == MWMechanics::getPlayer()); + if (isPlayer) { Log(Debug::Verbose) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName(); @@ -1068,7 +1069,8 @@ namespace MWRender if (groupname.compare(0, 4, "jump")) if (groupname.compare(0, 4, "walk")) if (groupname.compare(0, 3, "run")) - return false; + if (groupname.compare(0, 4, "swim")) + return false; #else (void)groupname; #endif diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 2315cd69b..185c0c461 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -361,10 +361,12 @@ namespace MWRender if(mPreviewMode) limit /= 2; +#ifndef USE_OPENXR if(angle > limit) angle = limit; else if(angle < -limit) angle = -limit; +#endif if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index faaac6448..e1019b171 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -828,16 +828,7 @@ private: if (node && node->getName() == "VRGUILayer") { - // Intersected with a GUI layer - // Inject mouse press - //VRGUILayerUserData* userData = static_cast(node->getUserData()); - //userData->mLayer->injectMouseClick(SDL_BUTTON_LEFT, onPress); injectMousePress(SDL_BUTTON_LEFT, onPress); - //SDL_MouseButtonEvent arg; - //if (onPress) - // mousePressed(arg, SDL_BUTTON_LEFT); - //else - // mouseReleased(arg, SDL_BUTTON_LEFT); } else if (onPress) { @@ -919,35 +910,11 @@ private: { mXRInput->updateControls(); - auto* world = MWBase::Environment::get().getWorld(); - auto* anim = MWVR::Environment::get().getPlayerAnimation(); - if (world && anim && anim->mPointerTarget.mHit) - { - auto* node = anim->mPointerTarget.mHitNode; - auto* vrGuiManager = Environment::get().getGUIManager(); - if (node && node->getName() == "VRGUILayer") - { - int w, h; - SDL_GetWindowSize(mWindow, &w, &h); - - osg::Vec3 local = anim->mPointerTarget.mHitPointLocal; - local.x() = (local.x() + 1.f) / 2.f; - local.z() = 1.f - (local.z() + 1.f) / 2.f; - - mGuiCursorX = mInvUiScalingFactor * (local.x() * w); - mGuiCursorY = mInvUiScalingFactor * (local.z() * h); - - VRGUILayerUserData* userData = static_cast(node->getUserData()); - vrGuiManager->setFocusLayer(userData->mLayer); - MyGUI::InputManager::getInstance().injectMouseMove((int)mGuiCursorX, (int)mGuiCursorY, 0); - } - else - { - vrGuiManager->setFocusLayer(nullptr); - } - } - - + auto* vrGuiManager = Environment::get().getGUIManager(); + vrGuiManager->updateFocus(); + auto guiCursor = vrGuiManager->guiCursor(); + mGuiCursorX = guiCursor.x(); + mGuiCursorY = guiCursor.y(); OpenXRActionEvent event{}; while (mXRInput->nextActionEvent(event)) @@ -988,7 +955,8 @@ private: toggleMainMenu(); // Explicitly request position update here so that the player can move the menu // using the menu key when the menu can't be toggled. - xrGUIManager->updatePose(); + // TODO: This should respond to a menu HODL instead + // xrGUIManager->updateTracking(); break; case A_Screenshot: screenshot(); diff --git a/apps/openmw/mwvr/openxrsession.cpp b/apps/openmw/mwvr/openxrsession.cpp index 30e09448d..fad4a7f19 100644 --- a/apps/openmw/mwvr/openxrsession.cpp +++ b/apps/openmw/mwvr/openxrsession.cpp @@ -131,14 +131,12 @@ namespace MWVR roll = angle_y; } - float OpenXRSession::movementYaw(void) + void OpenXRSession::movementAngles(float& yaw, float& pitch) { auto lhandquat = predictedPoses(PredictionSlice::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation; - float yaw = 0.f; - float pitch = 0.f; float roll = 0.f; getEulerAngles(lhandquat, yaw, pitch, roll); - return yaw; + } void OpenXRSession::advanceFrame(void) diff --git a/apps/openmw/mwvr/openxrsession.hpp b/apps/openmw/mwvr/openxrsession.hpp index 9bdcc68c0..3c4cef5c1 100644 --- a/apps/openmw/mwvr/openxrsession.hpp +++ b/apps/openmw/mwvr/openxrsession.hpp @@ -47,8 +47,8 @@ public: //! Update predictions void predictNext(int extraPeriods); - //! Yaw angle to be used for offsetting movement direction - float movementYaw(void); + //! Angles to be used for overriding movement direction + void movementAngles(float& yaw, float& pitch); void advanceFrame(void); diff --git a/apps/openmw/mwvr/vranimation.cpp b/apps/openmw/mwvr/vranimation.cpp index 9b2e9268d..97f03c772 100644 --- a/apps/openmw/mwvr/vranimation.cpp +++ b/apps/openmw/mwvr/vranimation.cpp @@ -465,7 +465,7 @@ VRAnimation::VRAnimation( ); mWeaponPointerTransform->setName("Weapon Pointer"); mWeaponPointerTransform->setUpdateCallback(new WeaponPointerController); - mWeaponDirectionTransform->addChild(mWeaponPointerTransform); + //mWeaponDirectionTransform->addChild(mWeaponPointerTransform); } VRAnimation::~VRAnimation() {}; @@ -538,7 +538,13 @@ void VRAnimation::setPointForward(bool enabled) mPointerTransform->removeChild(mPointerRescale); if (enabled) + { mPointerTransform->addChild(mPointerRescale); + } + else + { + mPointerTarget = MWRender::RayResult{}; + } } osg::ref_ptr VRAnimation::createPointerGeometry(void) diff --git a/apps/openmw/mwvr/vrgui.cpp b/apps/openmw/mwvr/vrgui.cpp index ad7502a9d..e671d4685 100644 --- a/apps/openmw/mwvr/vrgui.cpp +++ b/apps/openmw/mwvr/vrgui.cpp @@ -20,6 +20,7 @@ #include "../mwrender/camera.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwgui/windowbase.hpp" #include @@ -28,9 +29,22 @@ #include #include +namespace osg +{ +// Convenience +const double PI_8 = osg::PI_4 / 2.; +} + namespace MWVR { +// When making a circle of a given radius of equally wide planes separated by a given angle, what is the width +static osg::Vec2 radiusAngleWidth(float radius, float angleRadian) +{ + const float width = std::fabs( 2.f * radius * std::tanf(angleRadian / 2.f) ); + return osg::Vec2(width, width); +} + /// RTT camera used to draw the osg GUI to a texture class GUICamera : public osg::Camera { @@ -120,18 +134,11 @@ private: VRGUILayer::VRGUILayer( osg::ref_ptr geometryRoot, osg::ref_ptr cameraRoot, - int width, - int height, std::string filter, LayerConfig config, - MWGui::Layout* widget, VRGUIManager* parent) : mConfig(config) , mFilter(filter) - , mWidget(widget) - , mWindow(dynamic_cast(mWidget)) - , mMyGUIWindow(dynamic_cast(mWidget->mMainWidget)) - , mParent(parent) , mGeometryRoot(geometryRoot) , mCameraRoot(cameraRoot) { @@ -139,14 +146,18 @@ VRGUILayer::VRGUILayer( osg::ref_ptr texCoords{ new osg::Vec2Array(4) }; osg::ref_ptr normals{ new osg::Vec3Array(1) }; - // Units are divided by 2 because geometry has an extent of 2 (-1 to 1) - auto extent_units = config.extent * Environment::get().unitsPerMeter() / 2.f; + auto extent_units = config.extent * Environment::get().unitsPerMeter(); + + float left = mConfig.center.x() - 0.5; + float right = left + 1.f; + float top = 0.5f + mConfig.center.y(); + float bottom = top - 1.f; // Define the menu quad - osg::Vec3 top_left (-1, 1, 1); - osg::Vec3 bottom_left(-1, 1, -1); - osg::Vec3 bottom_right(1, 1, -1); - osg::Vec3 top_right (1, 1, 1); + osg::Vec3 top_left (left, 1, top); + osg::Vec3 bottom_left(left, 1, bottom); + osg::Vec3 bottom_right(right , 1, bottom); + osg::Vec3 top_right (right, 1, top); (*vertices)[0] = top_left; (*vertices)[1] = bottom_left; (*vertices)[2] = bottom_right; @@ -163,20 +174,23 @@ VRGUILayer::VRGUILayer( mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->setSupportsDisplayList(false); mGeometry->setName("VRGUILayer"); - mGeometry->setUserData(new VRGUILayerUserData(this)); // Create the camera that will render the menu texture - mGUICamera = new GUICamera(width, height, config.backgroundColor); + mGUICamera = new GUICamera(config.pixelResolution.x(), config.pixelResolution.y(), config.backgroundColor); osgMyGUI::RenderManager& renderManager = static_cast(MyGUI::RenderManager::getInstance()); - mGUICamera->setScene(renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter)); + mMyGUICamera = renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter); + //myGUICamera->setViewport(0, 0, 256, 256); + //mMyGUICamera->setProjectionMatrixAsOrtho2D(-1, 1, -1, 1); + mGUICamera->setScene(mMyGUICamera); // Define state set that allows rendering with transparency - mStateSet->setTextureAttributeAndModes(0, menuTexture(), osg::StateAttribute::ON); - mStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON); - mStateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - mStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - mGeometry->setStateSet(mStateSet); + osg::StateSet* stateSet = mGeometry->getOrCreateStateSet(); + stateSet->setTextureAttributeAndModes(0, menuTexture(), osg::StateAttribute::ON); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + mGeometry->setStateSet(stateSet); // Position in the game world mTransform->setScale(osg::Vec3(extent_units.x(), 1.f, extent_units.y())); @@ -186,6 +200,10 @@ VRGUILayer::VRGUILayer( mGeometryRoot->addChild(mTransform); mCameraRoot->addChild(mGUICamera); + // Edit offset to account for priority + if(!mConfig.sideBySide) + mConfig.offset.y() -= 0.001f * mConfig.priority; + mTransform->addUpdateCallback(new LayerUpdateCallback(this)); } @@ -206,68 +224,174 @@ osg::ref_ptr VRGUILayer::menuTexture() return nullptr; } -void VRGUILayer::updatePose() +void VRGUILayer::setAngle(float angle) { - osg::Vec3 eye{}; - osg::Vec3 center{}; - osg::Vec3 up{}; - - // Get head pose by reading the camera view matrix to place the GUI in the world. - Pose headPose{}; - auto* world = MWBase::Environment::get().getWorld(); - if (!world) - return; - auto* camera = world->getRenderingManager().getCamera()->getOsgCamera(); - if (!camera) - return; - camera->getViewMatrixAsLookAt(eye, center, up); - headPose.position = eye; - headPose.orientation = camera->getViewMatrix().getRotate(); + mRotation = osg::Quat{ angle, osg::Z_AXIS }; + updatePose(); +} - if (mConfig.trackedLimb == TrackedLimb::HEAD) +void VRGUILayer::updateTracking(const Pose& headPose) +{ + if (mConfig.trackingMode == TrackingMode::Menu) { mTrackedPose = headPose; - mTrackedPose.orientation = mTrackedPose.orientation.inverse(); } else { - // If it's not head, it's one of the hands, so i don't bother checking - auto* session = MWVR::Environment::get().getSession(); - auto& poses = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw); - mTrackedPose = poses.hands[(int)TrackedSpace::STAGE][(int)mConfig.trackedLimb]; - // World position is the head, so must add difference between head and hand in tracking space to world pose - mTrackedPose.position = mTrackedPose.position * MWVR::Environment::get().unitsPerMeter() - poses.head[(int)TrackedSpace::STAGE].position * MWVR::Environment::get().unitsPerMeter() + headPose.position; + auto* anim = MWVR::Environment::get().getPlayerAnimation(); + if (anim) + { + const osg::Node* hand = nullptr; + if (mConfig.trackingMode == TrackingMode::HudLeftHand) + hand = anim->getNode("bip01 l hand"); + else + hand = anim->getNode("bip01 r hand"); + if (hand) + { + auto world = osg::computeLocalToWorld(hand->getParentalNodePaths()[0]); + mTrackedPose.position = world.getTrans(); + mTrackedPose.orientation = world.getRotate(); + if (mConfig.trackingMode == TrackingMode::HudRightHand) + mTrackedPose.orientation = osg::Quat(osg::PI, osg::Vec3(1, 0, 0)) * mTrackedPose.orientation; + mTrackedPose.orientation = osg::Quat(osg::PI_2, osg::Vec3(0, 0, 1)) * mTrackedPose.orientation; + mTrackedPose.orientation = osg::Quat(osg::PI, osg::Vec3(1, 0, 0)) * mTrackedPose.orientation; + } + } } - mLayerPose.orientation = mConfig.rotation * mTrackedPose.orientation; + updatePose(); +} - if (mConfig.vertical) +void VRGUILayer::updatePose() +{ + + auto orientation = mRotation * mTrackedPose.orientation; + + if (mConfig.trackingMode == TrackingMode::Menu) { - // Force layer to be vertical + // Force menu layers to be vertical auto axis = osg::Z_AXIS; osg::Quat vertical; - auto local = mLayerPose.orientation * axis; + auto local = orientation * axis; vertical.makeRotate(local, axis); - mLayerPose.orientation = mLayerPose.orientation * vertical; + orientation = orientation * vertical; } // Orient the offset and move the layer - mLayerPose.position = mTrackedPose.position + mLayerPose.orientation * mConfig.offset * MWVR::Environment::get().unitsPerMeter(); + auto position = mTrackedPose.position + orientation * mConfig.offset * MWVR::Environment::get().unitsPerMeter(); - mTransform->setAttitude(mLayerPose.orientation); - mTransform->setPosition(mLayerPose.position); + mTransform->setAttitude(orientation); + mTransform->setPosition(position); +} + +void VRGUILayer::updateRect() +{ + auto viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + mRealRect.left = 1.f; + mRealRect.top = 1.f; + mRealRect.right = 0.f; + mRealRect.bottom = 0.f; + float realWidth = static_cast(viewSize.width); + float realHeight = static_cast(viewSize.height); + for (auto* widget : mWidgets) + { + auto rect = widget->mMainWidget->getAbsoluteRect(); + mRealRect.left = std::min(static_cast(rect.left) / realWidth, mRealRect.left); + mRealRect.top = std::min(static_cast(rect.top) / realHeight, mRealRect.top); + mRealRect.right = std::max(static_cast(rect.right) / realWidth, mRealRect.right); + mRealRect.bottom = std::max(static_cast(rect.bottom) / realHeight, mRealRect.bottom); + } + + // Some widgets don't capture the full visual + if (mFilter == "JournalBooks" || mFilter == "MessageBox" ) + { + mRealRect.left = 0.f; + mRealRect.top = 0.f; + mRealRect.right = 1.f; + mRealRect.bottom = 1.f; + } + + if (mFilter == "Notification") + { + // The latest widget for notification is always the top one + // So we just have to stretch the rectangle to the bottom + // TODO: This might get deprecated with this new system? + mRealRect.bottom = 1.f; + } } void VRGUILayer::update() { - if (mConfig.trackingMode == TrackingMode::Auto) - updatePose(); + if (mConfig.trackingMode != TrackingMode::Menu) + updateTracking(); + + if (mConfig.sideBySide) + { + // The side-by-side windows are also the resizable windows. + // Stretch according to config + // This genre of layer should only ever have 1 widget as it will cover the full layer + auto* widget = mWidgets.front(); + auto* myGUIWindow = dynamic_cast(widget->mMainWidget); + auto* windowBase = dynamic_cast(widget); + if (windowBase && myGUIWindow) + { + auto w = mConfig.myGUIViewSize.x(); + auto h = mConfig.myGUIViewSize.y(); + windowBase->setCoordf(0.f, 0.f, w, h); + windowBase->onWindowResize(myGUIWindow); + } + } + updateRect(); + + float w = 0.f; + float h = 0.f; + for (auto* widget : mWidgets) + { + w = std::max(w, (float)widget->mMainWidget->getWidth()); + h = std::max(h, (float)widget->mMainWidget->getHeight()); + } + + // Pixels per unit + float res = static_cast(mConfig.spatialResolution) / Environment::get().unitsPerMeter(); + + if (mConfig.sizingMode == SizingMode::Auto) + { + mTransform->setScale(osg::Vec3(w / res, 1.f, h / res)); + } + if (mFilter == "Notification") + { + auto viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + h = (1.f - mRealRect.top) * viewSize.height; + mTransform->setScale(osg::Vec3(w / res, 1.f, h / res)); + } + + osg::ref_ptr texCoords{ new osg::Vec2Array(4) }; + (*texCoords)[0].set(mRealRect.left, 1.f - mRealRect.top); + (*texCoords)[1].set(mRealRect.left, 1.f - mRealRect.bottom); + (*texCoords)[2].set(mRealRect.right, 1.f - mRealRect.bottom); + (*texCoords)[3].set(mRealRect.right, 1.f - mRealRect.top); + mGeometry->setTexCoordArray(0, texCoords); +} + +void +VRGUILayer::insertWidget( + MWGui::Layout* widget) +{ + for (auto* w : mWidgets) + if (w == widget) + return; + mWidgets.push_back(widget); +} - if (mConfig.stretch) +void +VRGUILayer::removeWidget( + MWGui::Layout* widget) +{ + for (auto it = mWidgets.begin(); it != mWidgets.end(); it++) { - if (mWindow && mMyGUIWindow) + if (*it == widget) { - mWindow->setCoordf(0.f, 0.f, 1.f, 1.f); - mWindow->onWindowResize(mMyGUIWindow); + mWidgets.erase(it); + return; } } } @@ -301,148 +425,255 @@ void VRGUIManager::showGUIs(bool show) { } -LayerConfig gDefaultConfig = LayerConfig -{ - true, // stretch - osg::Vec4{0.f,0.f,0.f,.75f}, // background - osg::Quat{}, // rotation - osg::Vec3(0.f,1.f,0.f), // offset - osg::Vec2(1.f, 1.f), // extent (meters) - osg::Vec2i(2024,2024), // resolution (pixels) - TrackedLimb::HEAD, - TrackingMode::Manual, - true // vertical +static const LayerConfig createDefaultConfig(int priority) +{ + return LayerConfig{ + 1, + false, // side-by-side + osg::Vec4{0.f,0.f,0.f,.75f}, // background + osg::Vec3(0.f,0.66f,-.25f), // offset + osg::Vec2(0.f,0.f), // center (model space) + osg::Vec2(1.f, 1.f), // extent (meters) + 1024, // Spatial resolution (pixels per meter) + osg::Vec2i(2048,2048), // Texture resolution + osg::Vec2(1,1), + SizingMode::Auto, + TrackingMode::Menu + }; +} +LayerConfig gDefaultConfig = createDefaultConfig(1); +LayerConfig gJournalBooksConfig = LayerConfig +{ + 2, + gDefaultConfig.sideBySide, + osg::Vec4{}, // background + gDefaultConfig.offset, + gDefaultConfig.center, + gDefaultConfig.extent, + gDefaultConfig.spatialResolution, + gDefaultConfig.pixelResolution, + gDefaultConfig.myGUIViewSize, + SizingMode::Fixed, + gDefaultConfig.trackingMode }; +LayerConfig gDefaultWindowsConfig = createDefaultConfig(3); +LayerConfig gMessageBoxConfig = gJournalBooksConfig; +LayerConfig gNotificationConfig = gJournalBooksConfig; -LayerConfig gStatusHUDConfig = LayerConfig +static const float sSideBySideRadius = 1.f; +static const float sSideBySideAzimuthInterval = -osg::PI_4; +static const LayerConfig createSideBySideConfig(int priority) { - false, // stretch - osg::Vec4{0.f,0.f,0.f,0.f}, // background - osg::Quat{}, // rotation - osg::Vec3(0.f,.0f,.2f), // offset (meters) - osg::Vec2(.2f, .2f), // extent (meters) - osg::Vec2i(1024,512), // resolution (pixels) - TrackedLimb::RIGHT_HAND, - TrackingMode::Auto, - false // vertical + return LayerConfig{ + priority, + true, // side-by-side + gDefaultConfig.backgroundColor, + osg::Vec3(0.f,sSideBySideRadius,-.25f), // offset + gDefaultConfig.center, + radiusAngleWidth(sSideBySideRadius, sSideBySideAzimuthInterval), // extent (meters) + gDefaultConfig.spatialResolution, + gDefaultConfig.pixelResolution, + osg::Vec2(0.70f, 0.70f), + SizingMode::Fixed, + gDefaultConfig.trackingMode + }; }; -LayerConfig gMinimapHUDConfig = LayerConfig +LayerConfig gStatsWindowConfig = createSideBySideConfig(0); +LayerConfig gInventoryWindowConfig = createSideBySideConfig(1); +LayerConfig gSpellWindowConfig = createSideBySideConfig(2); +LayerConfig gMapWindowConfig = createSideBySideConfig(3); +LayerConfig gInventoryCompanionWindowConfig = createSideBySideConfig(4); +LayerConfig gDialogueWindowConfig = createSideBySideConfig(5); + +LayerConfig gStatusHUDConfig = LayerConfig { - false, // stretch - osg::Vec4{0.f,0.f,0.f,0.f}, // background - osg::Quat{}, // rotation - osg::Vec3(0.f,.0f,.2f), // offset (meters) - osg::Vec2(.2f, .2f), // extent (meters) - osg::Vec2i(1024,512), // resolution (pixels) - TrackedLimb::RIGHT_HAND, - TrackingMode::Auto, - false // vertical + 0, + false, // side-by-side + osg::Vec4{}, // background + osg::Vec3(0.025f,.025f,.066f), // offset (meters) + osg::Vec2(0.f,0.5f), // center (model space) + osg::Vec2(.1f, .1f), // extent (meters) + 1024, // resolution (pixels per meter) + osg::Vec2i(1024,1024), + gDefaultConfig.myGUIViewSize, + SizingMode::Auto, + TrackingMode::HudLeftHand, }; LayerConfig gPopupConfig = LayerConfig { - false, // stretch + 0, + false, // side-by-side osg::Vec4{0.f,0.f,0.f,0.f}, // background - osg::Quat{}, // rotation - osg::Vec3(0.f,0.f,.2f), // offset - osg::Vec2(.2f, .2f), // extent (meters) - osg::Vec2i(1024,1024), - TrackedLimb::RIGHT_HAND, - TrackingMode::Auto, - false // vertical + osg::Vec3(-0.025f,.025f,.066f), // offset (meters) + osg::Vec2(0.f,0.5f), // center (model space) + osg::Vec2(.1f, .1f), // extent (meters) + 1024, // resolution (pixels per meter) + osg::Vec2i(2048,2048), + gDefaultConfig.myGUIViewSize, + SizingMode::Auto, + TrackingMode::HudRightHand, }; -LayerConfig gWindowsConfig = gDefaultConfig; -LayerConfig gJournalBooksConfig = LayerConfig -{ - true, // stretch - gDefaultConfig.backgroundColor, - osg::Quat{}, // rotation - gDefaultConfig.offset, - gDefaultConfig.extent, - gDefaultConfig.resolution, - TrackedLimb::HEAD, - TrackingMode::Manual, - true // vertical -}; -LayerConfig gSpellWindowConfig = LayerConfig -{ - true, // stretch - gDefaultConfig.backgroundColor, - osg::Quat{-osg::PI_2, osg::Z_AXIS}, // rotation - gDefaultConfig.offset, - gDefaultConfig.extent, - gDefaultConfig.resolution, - TrackedLimb::HEAD, - TrackingMode::Manual, - true // vertical -}; -LayerConfig gInventoryWindowConfig = LayerConfig -{ - true, // stretch - gDefaultConfig.backgroundColor, - osg::Quat{}, // rotation - gDefaultConfig.offset, - gDefaultConfig.extent, - gDefaultConfig.resolution, - TrackedLimb::HEAD, - TrackingMode::Manual, - true // vertical -}; -LayerConfig gMapWindowConfig = LayerConfig -{ - true, // stretch - gDefaultConfig.backgroundColor, - osg::Quat{osg::PI, osg::Z_AXIS}, // rotation - gDefaultConfig.offset, - gDefaultConfig.extent, - gDefaultConfig.resolution, - TrackedLimb::HEAD, - TrackingMode::Manual, - true // vertical -}; -LayerConfig gStatsWindowConfig = LayerConfig -{ - true, // stretch - gDefaultConfig.backgroundColor, - osg::Quat{osg::PI_2, osg::Z_AXIS}, // rotation - gDefaultConfig.offset, - gDefaultConfig.extent, - gDefaultConfig.resolution, - TrackedLimb::HEAD, - TrackingMode::Manual, - true // vertical -}; static std::map gLayerConfigs = { {"StatusHUD", gStatusHUDConfig}, - {"MinimapHUD", gMinimapHUDConfig}, + //{"MinimapHUD", gMinimapHUDConfig}, {"Popup", gPopupConfig}, - {"Windows", gWindowsConfig}, {"JournalBooks", gJournalBooksConfig}, - {"SpellWindow", gSpellWindowConfig}, + {"InventoryCompanionWindow", gInventoryCompanionWindowConfig}, {"InventoryWindow", gInventoryWindowConfig}, + {"SpellWindow", gSpellWindowConfig}, {"MapWindow", gMapWindowConfig}, {"StatsWindow", gStatsWindowConfig}, - {"Default", gDefaultConfig}, + {"DialogueWindow", gDialogueWindowConfig}, + {"MessageBox", gMessageBoxConfig}, + {"Windows", gDefaultWindowsConfig}, + {"Notification", gNotificationConfig} }; static std::set layerBlacklist = { - "Overlay" + "Overlay", + "AdditiveOverlay" }; +void VRGUIManager::updateSideBySideLayers() +{ + // Nothing to update + if (mSideBySideLayers.size() == 0) + return; + + std::sort(mSideBySideLayers.begin(), mSideBySideLayers.end(), [](const auto& lhs, const auto& rhs) { return *lhs < *rhs; }); + + int n = mSideBySideLayers.size(); + + float span = sSideBySideAzimuthInterval * (n - 1); // zero index, places lone layers straight ahead + float low = -span / 2; + + for (int i = 0; i < mSideBySideLayers.size(); i++) + mSideBySideLayers[i]->setAngle(low + static_cast(i) * sSideBySideAzimuthInterval); +} + +void VRGUIManager::insertLayer(const std::string& name) +{ + LayerConfig config = gDefaultConfig; + auto configIt = gLayerConfigs.find(name); + if (configIt != gLayerConfigs.end()) + { + config = configIt->second; + } + else + { + Log(Debug::Warning) << "Layer " << name << " has no configuration, using default"; + } + + auto layer = std::shared_ptr(new VRGUILayer( + mGUIGeometriesRoot, + mGUICamerasRoot, + name, + config, + this + )); + mLayers[name] = layer; + + layer->mGeometry->setUserData(new VRGUILayerUserData(mLayers[name])); + + // Default new layer's pick to false + // TODO: re-add widget->setLayerPick(false) somewhere; + + if (config.sideBySide) + { + mSideBySideLayers.push_back(layer); + updateSideBySideLayers(); + } + + if (config.trackingMode == TrackingMode::Menu) + { + // Update tracking when a menu is opened + // But don't automatically update it again until all menus have been closed + if (mVisibleMenus == 0) + updateTracking(); + else + layer->updateTracking(mHeadPose); + mVisibleMenus++; + } +} + +void VRGUIManager::insertWidget(MWGui::Layout* widget) +{ + auto* layer = widget->mMainWidget->getLayer(); + auto name = layer->getName(); + + auto it = mLayers.find(name); + if (it == mLayers.end()) + { + insertLayer(name); + it = mLayers.find(name); + if (it == mLayers.end()) + { + Log(Debug::Error) << "Failed to insert layer " << name; + return; + } + } + + it->second->insertWidget(widget); + + if (it->second.get() != mFocusLayer) + widget->setLayerPick(false); +} + +void VRGUIManager::removeLayer(const std::string& name) +{ + auto it = mLayers.find(name); + if (it == mLayers.end()) + return; + + auto layer = it->second; + + for (auto it2 = mSideBySideLayers.begin(); it2 < mSideBySideLayers.end(); it2++) + { + if (*it2 == layer) + { + mSideBySideLayers.erase(it2); + updateSideBySideLayers(); + } + } + + if (it->second.get() == mFocusLayer) + setFocusLayer(nullptr); + + if (it->second->mConfig.trackingMode == TrackingMode::Menu) + mVisibleMenus--; + + mLayers.erase(it); +} + +void VRGUIManager::removeWidget(MWGui::Layout* widget) +{ + auto* layer = widget->mMainWidget->getLayer(); + auto name = layer->getName(); + + auto it = mLayers.find(name); + if (it == mLayers.end()) + { + Log(Debug::Warning) << "Tried to remove widget from nonexistent layer " << name; + return; + } + + it->second->removeWidget(widget); + if (it->second->widgetCount() == 0) + { + removeLayer(name); + } +} + void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible) { auto* layer = widget->mMainWidget->getLayer(); - //if (!layer) - //{ - // Log(Debug::Warning) << "Hark! MyGUI has betrayed us. The widget " << widget->mMainWidget->getName() << " has no layer"; - // return; - //} auto name = layer->getName(); Log(Debug::Verbose) << "setVisible (" << name << "): " << visible; @@ -453,60 +684,103 @@ void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible) widget->setLayerPick(false); return; } + if (visible) + insertWidget(widget); + else + removeWidget(widget); +} + +void VRGUIManager::updateTracking(void) +{ + // Get head pose by reading the camera view matrix to place the GUI in the world. + osg::Vec3 eye{}; + osg::Vec3 center{}; + osg::Vec3 up{}; + Pose headPose{}; + auto* world = MWBase::Environment::get().getWorld(); + if (!world) + return; + auto* camera = world->getRenderingManager().getCamera()->getOsgCamera(); + if (!camera) + return; + camera->getViewMatrixAsLookAt(eye, center, up); + headPose.position = eye; + headPose.orientation = camera->getViewMatrix().getRotate(); + headPose.orientation = headPose.orientation.inverse(); + + mHeadPose = headPose; + + for (auto& layer : mLayers) + layer.second->updateTracking(mHeadPose); +} + +void VRGUIManager::updateFocus() +{ + auto* anim = MWVR::Environment::get().getPlayerAnimation(); + if (anim && anim->mPointerTarget.mHit) { - if (mLayers.find(name) == mLayers.end()) + std::shared_ptr newFocusLayer = nullptr; + auto* node = anim->mPointerTarget.mHitNode; + if (node->getName() == "VRGUILayer") { - LayerConfig config = gDefaultConfig; - auto configIt = gLayerConfigs.find(name); - if (configIt != gLayerConfigs.end()) - config = configIt->second; - - mLayers[name] = std::unique_ptr(new VRGUILayer( - mGUIGeometriesRoot, - mGUICamerasRoot, - 2048, - 2048, - name, - config, - widget, - this - )); - - // Default new layer's pick to false - widget->setLayerPick(false); - - Log(Debug::Verbose) << "Created GUI layer " << name; + VRGUILayerUserData* userData = static_cast(node->getUserData()); + newFocusLayer = userData->mLayer.lock(); } - updatePose(); - } - else - { - auto it = mLayers.find(name); - if (it != mLayers.end()) + + if (newFocusLayer && newFocusLayer->mFilter != "Notification") { - if (it->second.get() == mFocusLayer) - setFocusLayer(nullptr); - mLayers.erase(it); - Log(Debug::Verbose) << "Erased GUI layer " << name; + setFocusLayer(newFocusLayer.get()); + computeGuiCursor(anim->mPointerTarget.mHitPointLocal); } } } -void VRGUIManager::updatePose(void) -{ - for (auto& layer : mLayers) - layer.second->updatePose(); -} - void VRGUIManager::setFocusLayer(VRGUILayer* layer) { + if (layer == mFocusLayer) + return; + if (mFocusLayer) - mFocusLayer->mWidget->setLayerPick(false); + { + mFocusLayer->mWidgets.front()->setLayerPick(false); + } mFocusLayer = layer; if (mFocusLayer) - mFocusLayer->mWidget->setLayerPick(true); - + { + Log(Debug::Verbose) << "Set focus layer to " << mFocusLayer->mWidgets.front()->mMainWidget->getLayer()->getName(); + mFocusLayer->mWidgets.front()->setLayerPick(true); + } + else + { + Log(Debug::Verbose) << "Set focus layer to null"; + } +} + +void VRGUIManager::computeGuiCursor(osg::Vec3 hitPoint) +{ + float x = 0; + float y = 0; + if (mFocusLayer) + { + osg::Vec2 bottomLeft = mFocusLayer->mConfig.center - osg::Vec2(0.5f, 0.5f); + x = hitPoint.x() - bottomLeft.x(); + y = hitPoint.z() - bottomLeft.y(); + auto rect = mFocusLayer->mRealRect; + auto viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + auto width = viewSize.width * rect.width(); + auto height = viewSize.height * rect.height(); + auto left = viewSize.width * rect.left; + auto bottom = viewSize.height * rect.bottom; + x = width * x + left; + y = bottom - height * y; + } + + mGuiCursor.x() = (int)x; + mGuiCursor.y() = (int)y; + + MyGUI::InputManager::getInstance().injectMouseMove((int)x, (int)y, 0); + MWBase::Environment::get().getWindowManager()->setCursorActive(true); } } diff --git a/apps/openmw/mwvr/vrgui.hpp b/apps/openmw/mwvr/vrgui.hpp index c46f12971..c53e522b3 100644 --- a/apps/openmw/mwvr/vrgui.hpp +++ b/apps/openmw/mwvr/vrgui.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -34,21 +35,34 @@ namespace MWVR enum class TrackingMode { - Auto, //!< Update tracking every frame - Manual //!< Update tracking only on user request or when GUI visibility changes. + Menu, //!< Menu quads with fixed position based on head tracking. + HudLeftHand, //!< Hud quads tracking the left hand every frame + HudRightHand, //!< Hud quads tracking the right hand every frame + }; + + // Some UI elements should occupy predefined geometries + // Others should grow/shrink freely + enum class SizingMode + { + Auto, + Fixed }; struct LayerConfig { - bool stretch; //!< Resize layer window to occupy full quad + int priority; //!< Higher priority shows over lower priority windows. + bool sideBySide; //!< Resize layer window to occupy full quad osg::Vec4 backgroundColor; //!< Background color of layer - osg::Quat rotation; //!< Rotation relative to the tracking node osg::Vec3 offset; //!< Offset from tracked node in meters - osg::Vec2 extent; //!< Spatial extent of the layer in meters - osg::Vec2i resolution; //!< Pixel resolution of the texture - TrackedLimb trackedLimb; //!< Which limb to track + osg::Vec2 center; //!< Model space centerpoint of menu geometry. All menu geometries have model space lengths of 1 in each dimension. Use this to affect how geometries grow with changing size. + osg::Vec2 extent; //!< Spatial extent of the layer in meters when using Fixed sizing mode + int spatialResolution; //!< Pixels when using the Auto sizing mode. \note Meters per pixel of the GUI viewport, not the RTT texture. + osg::Vec2i pixelResolution; //!< Pixel resolution of the RTT texture + osg::Vec2 myGUIViewSize; //!< Resizable elements are resized to this (fraction of full view) + SizingMode sizingMode; //!< How to size the layer TrackingMode trackingMode; //!< Tracking mode - bool vertical; //!< Make layer vertical regardless of tracking orientation + + bool operator<(const LayerConfig& rhs) const { return priority < rhs.priority; } }; class VRGUILayer @@ -57,11 +71,8 @@ namespace MWVR VRGUILayer( osg::ref_ptr geometryRoot, osg::ref_ptr cameraRoot, - int width, - int height, std::string filter, LayerConfig config, - MWGui::Layout* widget, VRGUIManager* parent); ~VRGUILayer(); @@ -69,33 +80,39 @@ namespace MWVR osg::ref_ptr menuTexture(); + void setAngle(float angle); + void updateTracking(const Pose& headPose = {}); void updatePose(); + void updateRect(); void update(); + void insertWidget(MWGui::Layout* widget); + void removeWidget(MWGui::Layout* widget); + int widgetCount() { return mWidgets.size(); } + + bool operator<(const VRGUILayer& rhs) const { return mConfig.priority < rhs.mConfig.priority; } + public: Pose mTrackedPose{}; - Pose mLayerPose{}; LayerConfig mConfig; std::string mFilter; - MWGui::Layout* mWidget; - MWGui::WindowBase* mWindow; - MyGUI::Window* mMyGUIWindow; - VRGUIManager* mParent; + std::vector mWidgets; osg::ref_ptr mGeometryRoot; osg::ref_ptr mGeometry{ new osg::Geometry }; osg::ref_ptr mTransform{ new osg::PositionAttitudeTransform }; - osg::ref_ptr mCameraRoot; - osg::ref_ptr mStateSet{ new osg::StateSet }; osg::ref_ptr mGUICamera; + osg::ref_ptr mMyGUICamera{ nullptr }; + MyGUI::FloatRect mRealRect{}; + osg::Quat mRotation{ 0,0,0,1 }; }; class VRGUILayerUserData : public osg::Referenced { public: - VRGUILayerUserData(VRGUILayer* layer) : mLayer(layer) {}; + VRGUILayerUserData(std::shared_ptr layer) : mLayer(layer) {}; - VRGUILayer* mLayer; + std::weak_ptr mLayer; }; class VRGUIManager @@ -110,18 +127,38 @@ namespace MWVR void setVisible(MWGui::Layout*, bool visible); - void updatePose(void); + void updateSideBySideLayers(); + + void insertLayer(const std::string& name); + + void insertWidget(MWGui::Layout* widget); + + void removeLayer(const std::string& name); + + void removeWidget(MWGui::Layout* widget); + + void updateTracking(void); + + void updateFocus(); void setFocusLayer(VRGUILayer* layer); + osg::Vec2i guiCursor() { return mGuiCursor; }; + private: + void computeGuiCursor(osg::Vec3 hitPoint); + osg::ref_ptr mOsgViewer{ nullptr }; osg::ref_ptr mGUIGeometriesRoot{ new osg::Group }; osg::ref_ptr mGUICamerasRoot{ new osg::Group }; - std::map> mLayers; + std::map> mLayers; + std::vector > mSideBySideLayers; + int mVisibleMenus{ 0 }; + Pose mHeadPose{}; + osg::Vec2i mGuiCursor{}; VRGUILayer* mFocusLayer{ nullptr }; }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9c7c0e76b..bffb6e9bd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1100,6 +1100,15 @@ namespace MWWorld MWWorld::Ptr World::getFacedObject() { +#ifdef USE_OPENXR + + auto* anim = MWVR::Environment::get().getPlayerAnimation(); + if (anim && anim->mPointerTarget.mHit) + return anim->mPointerTarget.mHitObject; + else + return MWWorld::Ptr(); +#endif + MWWorld::Ptr facedObject; if (MWBase::Environment::get().getWindowManager()->isGuiMode() && diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 4e5f12520..930d58f7d 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -364,6 +364,7 @@ public: GUICamera(osg::Camera::RenderOrder order, RenderManager* parent, std::string filter) : mParent(parent) , mUpdate(false) + , mFilter(filter) { setReferenceFrame(osg::Transform::ABSOLUTE_RF); setProjectionResizePolicy(osg::Camera::FIXED); @@ -405,6 +406,7 @@ public: osg::ref_ptr mDrawable; MyGUI::RenderTargetInfo mInfo; bool mUpdate; + std::string mFilter; }; @@ -436,6 +438,10 @@ RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, R { if (scalingFactor != 0.f) mInvScalingFactor = 1.f / scalingFactor; + + + osg::ref_ptr vp = mViewer->getCamera()->getViewport(); + setViewSize(vp->width(), vp->height()); } RenderManager::~RenderManager() @@ -552,21 +558,13 @@ void GUICamera::collectDrawCalls(std::string filter) MyGUI::LayerManager* myGUILayers = MyGUI::LayerManager::getInstancePtr(); if (myGUILayers != nullptr) { - std::regex layerRegex{ filter, std::regex_constants::icase }; for (unsigned i = 0; i < myGUILayers->getLayerCount(); i++) { auto layer = myGUILayers->getLayer(i); - auto name = layer->getName(); - if (std::regex_search(name, layerRegex)) - { - //std::cout << "Including Layer: " << layer->getName() << std::endl; + if (name == filter) layer->renderToTarget(this, mUpdate); - } - else { - //std::cout << "Excluding Layer: " << layer->getName() << std::endl; - } } } end(); @@ -590,6 +588,11 @@ void RenderManager::setViewSize(int width, int height) if(width < 1) width = 1; if(height < 1) height = 1; +////#ifdef USE_OPENXR +// width = 1024; +// height = 1024; +////#endif + mViewSize.set(width * mInvScalingFactor, height * mInvScalingFactor); for (auto* camera : mGuiCameras) @@ -605,8 +608,8 @@ osg::ref_ptr RenderManager::createGUICamera(int order, std::string { osg::ref_ptr camera = new GUICamera(static_cast(order), this, layerFilter); mGuiCameras.insert(camera); - osg::ref_ptr vp = mViewer->getCamera()->getViewport(); - setViewSize(vp->width(), vp->height()); + camera->setViewport(0, 0, mViewSize.width, mViewSize.height); + camera->setViewSize(mViewSize); return camera; } diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index e909a11d2..22890f190 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -16,8 +16,8 @@ set(MYGUI_FILES openmw_box.skin.xml openmw_button.skin.xml openmw_chargen_birth.layout - openmw_chargen_class_description.layout openmw_chargen_class.layout + openmw_chargen_class_description.layout openmw_chargen_create_class.layout openmw_chargen_generate_class_result.layout openmw_chargen_race.layout @@ -25,78 +25,80 @@ set(MYGUI_FILES openmw_chargen_select_attribute.layout openmw_chargen_select_skill.layout openmw_chargen_select_specialization.layout + openmw_companion_window.layout openmw_confirmation_dialog.layout openmw_console.layout openmw_console.skin.xml openmw_container_window.layout + openmw_container_window_vr.layout openmw_count_window.layout + openmw_debug_window.layout + openmw_debug_window.skin.xml openmw_dialogue_window.layout + openmw_dialogue_window_vr.layout openmw_dialogue_window.skin.xml openmw_edit.skin.xml + openmw_edit_effect.layout + openmw_edit_note.layout + openmw_enchanting_dialog.layout openmw_font.xml + openmw_hud.layout openmw_hud_box.skin.xml openmw_hud_energybar.skin.xml - openmw_hud.layout openmw_hud_vr.layout openmw_infobox.layout openmw_interactive_messagebox.layout openmw_interactive_messagebox_notransp.layout openmw_inventory_window.layout openmw_inventory_window_vr.layout + openmw_itemselection_dialog.layout + openmw_jail_screen.layout openmw_journal.layout openmw_journal.skin.xml openmw_layers.xml openmw_layers_vr.xml + openmw_levelup_dialog.layout openmw_list.skin.xml + openmw_loading_screen.layout + openmw_magicselection_dialog.layout openmw_mainmenu.layout openmw_mainmenu.skin.xml openmw_map_window.layout - openmw_map_window_vr.layout openmw_map_window.skin.xml + openmw_map_window_vr.layout + openmw_merchantrepair.layout openmw_messagebox.layout + openmw_persuasion_dialog.layout openmw_pointer.xml openmw_progress.skin.xml + openmw_quickkeys_menu.layout + openmw_quickkeys_menu_assign.layout + openmw_recharge_dialog.layout + openmw_repair.layout openmw_resources.xml + openmw_savegame_dialog.layout + openmw_screen_fader.layout + openmw_screen_fader_hit.layout openmw_scroll.layout openmw_scroll.skin.xml - openmw_settings_window.layout openmw_settings.xml + openmw_settings_window.layout + openmw_spell_buying_window.layout openmw_spell_window.layout openmw_spell_window_vr.layout + openmw_spellcreation_dialog.layout openmw_stats_window.layout openmw_stats_window_vr.layout - openmw_text_input.layout openmw_text.skin.xml + openmw_text_input.layout openmw_tooltips.layout openmw_trade_window.layout - openmw_spell_buying_window.layout - openmw_windows.skin.xml - openmw_quickkeys_menu.layout - openmw_quickkeys_menu_assign.layout - openmw_itemselection_dialog.layout - openmw_magicselection_dialog.layout - openmw_spell_buying_window.layout - openmw_loading_screen.layout - openmw_levelup_dialog.layout - openmw_wait_dialog.layout - openmw_wait_dialog_progressbar.layout - openmw_spellcreation_dialog.layout - openmw_edit_effect.layout - openmw_enchanting_dialog.layout + openmw_trade_window_vr.layout openmw_trainingwindow.layout openmw_travel_window.layout - openmw_persuasion_dialog.layout - openmw_merchantrepair.layout - openmw_repair.layout - openmw_companion_window.layout - openmw_savegame_dialog.layout - openmw_recharge_dialog.layout - openmw_screen_fader.layout - openmw_screen_fader_hit.layout - openmw_edit_note.layout - openmw_debug_window.layout - openmw_debug_window.skin.xml - openmw_jail_screen.layout + openmw_wait_dialog.layout + openmw_wait_dialog_progressbar.layout + openmw_windows.skin.xml DejaVuLGCSansMono.ttf ../launcher/images/openmw.png OpenMWResourcePlugin.xml diff --git a/files/mygui/openmw_container_window_vr.layout b/files/mygui/openmw_container_window_vr.layout new file mode 100644 index 000000000..fb1fda805 --- /dev/null +++ b/files/mygui/openmw_container_window_vr.layout @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_dialogue_window_vr.layout b/files/mygui/openmw_dialogue_window_vr.layout new file mode 100644 index 000000000..d165dc7b6 --- /dev/null +++ b/files/mygui/openmw_dialogue_window_vr.layout @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_hud_vr.layout b/files/mygui/openmw_hud_vr.layout index 9369dff41..f3a2bcb1d 100644 --- a/files/mygui/openmw_hud_vr.layout +++ b/files/mygui/openmw_hud_vr.layout @@ -1,15 +1,12 @@ - - - - + - + - + @@ -17,7 +14,7 @@ - + @@ -25,7 +22,7 @@ - + @@ -35,7 +32,7 @@ - + @@ -48,7 +45,7 @@ - + @@ -59,7 +56,7 @@ - + @@ -70,10 +67,30 @@ - - + + + + + + + + + + + + + + + + + + + + + + - + @@ -89,7 +106,7 @@ - + @@ -98,32 +115,14 @@ - + - + - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_layers_vr.xml b/files/mygui/openmw_layers_vr.xml index cae9d22ab..8b314d67f 100644 --- a/files/mygui/openmw_layers_vr.xml +++ b/files/mygui/openmw_layers_vr.xml @@ -5,12 +5,14 @@ - + + + diff --git a/files/mygui/openmw_trade_window_vr.layout b/files/mygui/openmw_trade_window_vr.layout new file mode 100644 index 000000000..2947dba29 --- /dev/null +++ b/files/mygui/openmw_trade_window_vr.layout @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +