Tentatively completed VR GUI

pull/615/head
Mads Buvik Sandvei 5 years ago
parent 57e48cfc03
commit 6f9c405afd

@ -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)

@ -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))

@ -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())
{

@ -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();

@ -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);

@ -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)

@ -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));
}
}

@ -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

@ -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;
}
}

@ -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

@ -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;

@ -828,16 +828,7 @@ private:
if (node && node->getName() == "VRGUILayer")
{
// Intersected with a GUI layer
// Inject mouse press
//VRGUILayerUserData* userData = static_cast<VRGUILayerUserData*>(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<VRGUILayerUserData*>(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();

@ -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)

@ -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);

@ -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<osg::Geometry> VRAnimation::createPointerGeometry(void)

@ -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 <MyGUI_Widget.h>
@ -28,9 +29,22 @@
#include <MyGUI_WidgetManager.h>
#include <MyGUI_Window.h>
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<osg::Group> geometryRoot,
osg::ref_ptr<osg::Group> cameraRoot,
int width,
int height,
std::string filter,
LayerConfig config,
MWGui::Layout* widget,
VRGUIManager* parent)
: mConfig(config)
, mFilter(filter)
, mWidget(widget)
, mWindow(dynamic_cast<MWGui::WindowBase*>(mWidget))
, mMyGUIWindow(dynamic_cast<MyGUI::Window*>(mWidget->mMainWidget))
, mParent(parent)
, mGeometryRoot(geometryRoot)
, mCameraRoot(cameraRoot)
{
@ -139,14 +146,18 @@ VRGUILayer::VRGUILayer(
osg::ref_ptr<osg::Vec2Array> texCoords{ new osg::Vec2Array(4) };
osg::ref_ptr<osg::Vec3Array> 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<osgMyGUI::RenderManager&>(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<osg::Texture2D> 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<float>(viewSize.width);
float realHeight = static_cast<float>(viewSize.height);
for (auto* widget : mWidgets)
{
auto rect = widget->mMainWidget->getAbsoluteRect();
mRealRect.left = std::min(static_cast<float>(rect.left) / realWidth, mRealRect.left);
mRealRect.top = std::min(static_cast<float>(rect.top) / realHeight, mRealRect.top);
mRealRect.right = std::max(static_cast<float>(rect.right) / realWidth, mRealRect.right);
mRealRect.bottom = std::max(static_cast<float>(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<MyGUI::Window*>(widget->mMainWidget);
auto* windowBase = dynamic_cast<MWGui::WindowBase*>(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<float>(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<osg::Vec2Array> 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<std::string, LayerConfig&> 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<std::string> 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<float>(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<VRGUILayer>(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<VRGUILayer> 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<VRGUILayer>(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<VRGUILayerUserData*>(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);
}
}

@ -4,6 +4,7 @@
#include <map>
#include <set>
#include <regex>
#include <MyGUI_Widget.h>
#include <osg/Geometry>
#include <osg/TexMat>
@ -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<osg::Group> geometryRoot,
osg::ref_ptr<osg::Group> 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<osg::Texture2D> 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<MWGui::Layout*> mWidgets;
osg::ref_ptr<osg::Group> mGeometryRoot;
osg::ref_ptr<osg::Geometry> mGeometry{ new osg::Geometry };
osg::ref_ptr<osg::PositionAttitudeTransform> mTransform{ new osg::PositionAttitudeTransform };
osg::ref_ptr<osg::Group> mCameraRoot;
osg::ref_ptr<osg::StateSet> mStateSet{ new osg::StateSet };
osg::ref_ptr<GUICamera> mGUICamera;
osg::ref_ptr<osg::Camera> 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<VRGUILayer> layer) : mLayer(layer) {};
VRGUILayer* mLayer;
std::weak_ptr<VRGUILayer> 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<osgViewer::Viewer> mOsgViewer{ nullptr };
osg::ref_ptr<osg::Group> mGUIGeometriesRoot{ new osg::Group };
osg::ref_ptr<osg::Group> mGUICamerasRoot{ new osg::Group };
std::map<std::string, std::unique_ptr<VRGUILayer>> mLayers;
std::map<std::string, std::shared_ptr<VRGUILayer>> mLayers;
std::vector<std::shared_ptr<VRGUILayer> > mSideBySideLayers;
int mVisibleMenus{ 0 };
Pose mHeadPose{};
osg::Vec2i mGuiCursor{};
VRGUILayer* mFocusLayer{ nullptr };
};
}

@ -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() &&

@ -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<Drawable> 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<osg::Viewport> 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<osg::Camera> RenderManager::createGUICamera(int order, std::string
{
osg::ref_ptr<GUICamera> camera = new GUICamera(static_cast<osg::Camera::RenderOrder>(order), this, layerFilter);
mGuiCameras.insert(camera);
osg::ref_ptr<osg::Viewport> vp = mViewer->getCamera()->getViewport();
setViewSize(vp->width(), vp->height());
camera->setViewport(0, 0, mViewSize.width, mViewSize.height);
camera->setViewSize(mViewSize);
return camera;
}

@ -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

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Window" layer="InventoryCompanionWindow" position="0 0 600 300" name="_Main">
<Property key="MinSize" value="245 145"/>
<Property key="Visible" value="false"/>
<!-- Items -->
<Widget type="ItemView" skin="MW_ItemView" position="5 5 575 225" name="ItemView" align="Left Top Stretch">
</Widget>
<Widget type="HBox" position="0 235 580 28" align="Right Bottom">
<Widget type="Spacer"/>
<Widget type="AutoSizedButton" skin="MW_Button" name="DisposeCorpseButton" align="Right Bottom">
<Property key="Caption" value="#{sDisposeofCorpse}"/>
<Property key="Visible" value="false"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="TakeButton" align="Right Bottom">
<Property key="Caption" value="#{sTakeAll}"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="CloseButton" align="Right Bottom">
<Property key="Caption" value="#{sClose}"/>
</Widget>
</Widget>
</Widget>
</MyGUI>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Window" layer="DialogueWindow" position="0 0 588 433" name="_Main">
<Property key="MinSize" value="380 230"/>
<Widget type="Widget" skin="MW_Box" position="8 8 381 381" align="Stretch"/>
<Widget type="Widget" position="15 15 364 370" align="Left Top Stretch">
<Widget type="BookPage" skin="MW_BookPage" position="0 0 364 370" name="History" align="Left Top Stretch">
</Widget>
</Widget>
<Widget type="ScrollBar" skin="MW_VScroll" position="370 13 14 371" align="Right VStretch" name="VScroll">
<Property key="Visible" value="false"/>
</Widget>
<!-- The disposition bar-->
<Widget type="ProgressBar" skin="MW_Progress_Blue" position="398 8 166 18"
align="Right Top" name="Disposition">
<Widget type="TextBox" skin="ProgressText" position="0 0 166 14" name="DispositionText" align="Right VCenter">
<Property key="NeedMouse" value="false"/>
</Widget>
</Widget>
<!-- The list of topics -->
<Widget type="MWList" skin="MW_SimpleList" position="398 31 166 328" name="TopicsList" align="Right VStretch">
</Widget>
<!-- The Goodbye button -->
<Widget type="Button" skin="MW_Button" position="398 366 166 23" name="ByeButton" align="Right Bottom">
<Property key="Caption" value="#{sGoodbye}"/>
</Widget>
</Widget>
</MyGUI>

@ -1,15 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Widget" layer="StatusHUD" position="0 0 185 80" name="_Main" align="Default">
<!-- Spell effects box -->
<Widget type="Widget" skin="HUD_Box_Transparent" position="0 15 20 20" align="Left Bottom" name="EffectBox">
</Widget>
<Widget type="Widget" layer="StatusHUD" position="0 0 255 91" name="_Main" align="Default">
<!-- Energy bars -->
<Widget type="ProgressBar" skin="MW_EnergyBar_Yellow" position="0 0 65 12" align="Left Bottom" name="EnemyHealth">
<Widget type="ProgressBar" skin="MW_EnergyBar_Yellow" position="0 34 65 12" align="Left Bottom" name="EnemyHealth">
<Property key="Visible" value="false"/>
</Widget>
<Widget type="Button" skin="" position="0 38 65 12" align="Left Bottom" name="HealthFrame">
<Widget type="Button" skin="" position="0 49 65 12" align="Left Bottom" name="HealthFrame">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/>
<UserString key="ImageTexture_HealthImage" value="icons\k\health.dds"/>
@ -17,7 +14,7 @@
<Property key="NeedMouse" value="false"/>
</Widget>
</Widget>
<Widget type="Button" skin="" position="0 53 65 12" align="Left Bottom" name="MagickaFrame">
<Widget type="Button" skin="" position="0 64 65 12" align="Left Bottom" name="MagickaFrame">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/>
<UserString key="ImageTexture_HealthImage" value="icons\k\magicka.dds"/>
@ -25,7 +22,7 @@
<Property key="NeedMouse" value="false"/>
</Widget>
</Widget>
<Widget type="Button" skin="" position="0 68 65 12" align="Left Bottom" name="FatigueFrame">
<Widget type="Button" skin="" position="0 79 65 12" align="Left Bottom" name="FatigueFrame">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/>
<UserString key="ImageTexture_HealthImage" value="icons\k\fatigue.dds"/>
@ -35,7 +32,7 @@
</Widget>
<!-- Equipped weapon box -->
<Widget type="Button" skin="" position="69 38 36 41" align="Left Bottom" name="WeapBox">
<Widget type="Button" skin="" position="69 49 36 41" align="Left Bottom" name="WeapBox">
<Widget type="Widget" skin="HUD_Box" position="0 0 36 36">
<Property key="NeedMouse" value="false"/>
<Widget type="ItemWidget" skin="MW_ItemIconNoShadow" position="-3 -3 42 42" align="Left Top" name="WeapImage">
@ -48,7 +45,7 @@
</Widget>
<!-- Selected spell box -->
<Widget type="Button" position="109 38 36 41" align="Left Bottom" name="SpellBox">
<Widget type="Button" position="109 49 36 41" align="Left Bottom" name="SpellBox">
<Widget type="Widget" skin="HUD_Box" position="0 0 36 36">
<Widget type="SpellWidget" skin="MW_ItemIconNoShadow" position="-3 -3 42 42" align="Left Top" name="SpellImage"/>
<Property key="NeedMouse" value="false"/>
@ -59,7 +56,7 @@
</Widget>
<!-- Sneak indicator box -->
<Widget type="Button" skin="" position="149 38 36 36" align="Left Bottom" name="SneakBox">
<Widget type="Button" skin="" position="149 49 36 36" align="Left Bottom" name="SneakBox">
<Property key="Visible" value="false"/>
<Widget type="Widget" skin="HUD_Box" position="0 0 36 36">
<Property key="NeedMouse" value="false"/>
@ -70,10 +67,30 @@
</Widget>
</Widget>
</Widget>
<Widget type="Widget" layer="MiniMapHUD" position="0 0 300 92" name="_Main2" align="Stretch">
<!-- Spell effects box -->
<Widget type="Widget" skin="HUD_Box_Transparent" position="0 0 20 20" align="Left Bottom" name="EffectBox">
</Widget>
<!-- Map box -->
<Widget type="Widget" skin="" position="189 20 65 65" name="MiniMapBox" align="Right Bottom">
<Widget type="Widget" skin="HUD_Box" position="0 0 65 65" align="Center">
<Widget type="ScrollView" skin="MW_MapView" position="2 2 61 61" align="Left Bottom" name="MiniMap">
<Widget type="ImageBox" skin="RotatingSkin" position="0 0 32 32" align="Bottom Left" name="Compass">
<Property key="ImageTexture" value="textures\compass.dds"/>
</Widget>
<Widget type="Button" skin="" position_real="0 0 1 1" name="MiniMapButton" align="Stretch">
<Property key="Depth" value="10"/>
</Widget>
</Widget>
</Widget>
</Widget>
<!-- Drowning bar -->
<Widget type="Window" skin="MW_Dialog" position="0 36 230 58" align="Center Top" name="DrowningFrame">
<Widget type="Window" skin="MW_Dialog" position="0 27 230 58" align="Center Top" name="DrowningFrame">
<Property key="Visible" value="false"/>
<Widget type="TextBox" skin="SandText" position="0 3 222 24" name="DrowningTitle" align="Center Top HStretch">
<Property key="Caption" value="#{sBreath}"/>
@ -89,7 +106,7 @@
</Widget>
<!-- Equipped weapon/selected spell name display for a few seconds after it changes -->
<Widget type="TextBox" skin="SandText" position="13 118 270 24" name="WeaponSpellName" align="Left Bottom HStretch">
<Widget type="TextBox" skin="SandText" position="0 20 255 24" name="WeaponSpellName" align="Left Bottom HStretch">
<Property key="Visible" value="false"/>
<Property key="TextAlign" value="Left"/>
<Property key="TextShadow" value="true"/>
@ -98,32 +115,14 @@
</Widget>
<!-- Cell name display when cell changes -->
<Widget type="TextBox" skin="SandText" position="0 89 288 24" name="CellName" align="Left Bottom HStretch">
<Widget type="TextBox" skin="SandText" position="0 20 255 24" name="CellName" align="Left Bottom HStretch">
<Property key="Visible" value="false"/>
<Property key="TextAlign" value="Right"/>
<Property key="TextAlign" value="Left"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
<Property key="NeedMouse" value="false"/>
</Widget>
<!-- Map box -->
<Widget type="Widget" skin="" position="223 15 65 65" name="MiniMapBox" align="Right Bottom">
<Widget type="Widget" skin="HUD_Box" position="0 0 65 65" align="Center">
<Widget type="ScrollView" skin="MW_MapView" position="2 2 61 61" align="Left Bottom" name="MiniMap">
<Widget type="ImageBox" skin="RotatingSkin" position="0 0 32 32" align="Bottom Left" name="Compass">
<Property key="ImageTexture" value="textures\compass.dds"/>
</Widget>
<Widget type="Button" skin="" position_real="0 0 1 1" name="MiniMapButton" align="Stretch">
<Property key="Depth" value="10"/>
</Widget>
</Widget>
</Widget>
</Widget>
<!-- Crosshair -->
<Widget type="ImageBox" skin="HUD_Crosshair" position="0 0 32 32" align="Center Center" name="Crosshair">
</Widget>

@ -5,12 +5,14 @@
<Layer name="Overlay" overlapped="false" pick="false"/>
<Layer name="AdditiveOverlay" type="AdditiveLayer" pick="false"/>
<Layer name="StatusHUD" overlapped="false" pick="true"/>
<Layer name="MiniMapHUD" overlapped="false" pick="true"/>
<Layer name="Menu" overlapped="false" pick="true"/>
<Layer name="InventoryCompanionWindow" overlapped="true" pick="true"/>
<Layer name="InventoryWindow" overlapped="true" pick="true"/>
<Layer name="SpellWindow" overlapped="true" pick="true"/>
<Layer name="MapWindow" overlapped="true" pick="true"/>
<Layer name="StatsWindow" overlapped="true" pick="true"/>
<Layer name="DialogueWindow" overlapped="true" pick="true"/>
<Layer name="ServiceWindow" overlapped="true" pick="true"/>
<Layer name="Windows" overlapped="true" pick="true"/>
<Layer name="JournalBooks" type="ScalingLayer" pick="true">
<Property key="Size" value="600 520"/>

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Window" layer="InventoryCompanionWindow" position="0 0 600 360" name="_Main">
<Property key="Visible" value="false"/>
<Property key="MinSize" value="428 245"/>
<!-- Categories -->
<Widget type="HBox" position="8 8 566 24" align="Left Top HStretch" name="Categories">
<Widget type="AutoSizedButton" skin="MW_Button" name="AllButton">
<Property key="Caption" value="#{sAllTab}"/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="WeaponButton">
<Property key="Caption" value="#{sWeaponTab}"/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="ApparelButton">
<Property key="Caption" value="#{sApparelTab}"/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="MagicButton">
<Property key="Caption" value="#{sMagicTab}"/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="MiscButton">
<Property key="Caption" value="#{sMiscTab}"/>
<Property key="NeedKey" value="false"/>
</Widget>
<!-- Search box-->
<Widget type="EditBox" skin="MW_TextBoxEditWithBorder" position="0 0 0 23" name="FilterEdit">
<UserString key="HStretch" value="true"/>
<UserString key="AcceptTab" value="true"/>
</Widget>
</Widget>
<!-- Items -->
<Widget type="ItemView" skin="MW_ItemView" position="8 38 566 185" name="ItemView" align="Left Top Stretch">
</Widget>
<Widget type="Widget" skin="" position="8 231 566 92" name="BottomPane" align="Left Bottom HStretch">
<Widget type="HBox" position="0 0 566 24" align="Left Top HStretch">
<Widget type="Button" skin="MW_Button" position="0 0 40 24" name="IncreaseButton" align="Left Top">
<Property key="Caption" value="+"/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="48 0 140 24" name="TotalBalanceLabel" align="Left Top">
<Property key="TextAlign" value="Left VCenter"/>
</Widget>
<Widget type="Spacer" />
<Widget type="AutoSizedTextBox" skin="SandText" position="0 0 374 24" name="PlayerGold" align="Right Top">
<Property key="TextAlign" value="Right"/>
</Widget>
</Widget>
<Widget type="HBox" position="0 28 566 28" align="Left Top HStretch">
<Widget type="Button" skin="MW_Button" position="0 0 40 24" name="DecreaseButton" align="Left Top">
<Property key="Caption" value="-"/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="NumericEditBox" skin="MW_TextEdit" position="48 0 140 24" name="TotalBalance" align="Left Top">
<Property key="TextAlign" value="Center"/>
</Widget>
<Widget type="Spacer" />
<Widget type="AutoSizedTextBox" skin="SandText" position="0 0 374 24" name="MerchantGold" align="Right Top">
<Property key="TextAlign" value="Right"/>
</Widget>
</Widget>
<Widget type="HBox" position="0 60 566 28" align="Left Bottom HStretch">
<Widget type="AutoSizedButton" skin="MW_Button" name="MaxSaleButton">
<Property key="Caption" value="#{sMaxSale}"/>
</Widget>
<Widget type="Spacer" />
<Widget type="AutoSizedButton" skin="MW_Button" name="OfferButton">
<Property key="Caption" value="#{sBarterDialog8}"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="CancelButton">
<Property key="Caption" value="#{sCancel}"/>
</Widget>
</Widget>
</Widget>
</Widget>
</MyGUI>
Loading…
Cancel
Save