1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-20 07:53:51 +00:00

Tentatively completed VR GUI

This commit is contained in:
Mads Buvik Sandvei 2020-05-12 22:13:01 +02:00
parent 57e48cfc03
commit 6f9c405afd
25 changed files with 883 additions and 388 deletions

View file

@ -30,7 +30,11 @@ namespace MWGui
{ {
ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop)
#ifdef USE_OPENXR
: WindowBase("openmw_container_window_vr.layout")
#else
: WindowBase("openmw_container_window.layout") : WindowBase("openmw_container_window.layout")
#endif
, mDragAndDrop(dragAndDrop) , mDragAndDrop(dragAndDrop)
, mSortModel(nullptr) , mSortModel(nullptr)
, mModel(nullptr) , mModel(nullptr)

View file

@ -266,7 +266,11 @@ namespace MWGui
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
DialogueWindow::DialogueWindow() DialogueWindow::DialogueWindow()
#ifdef USE_OPENXR
: WindowBase("openmw_dialogue_window_vr.layout")
#else
: WindowBase("openmw_dialogue_window.layout") : WindowBase("openmw_dialogue_window.layout")
#endif
, mIsCompanion(false) , mIsCompanion(false)
, mGoodbye(false) , mGoodbye(false)
, mPersuasionDialog(new ResponseCallback(this)) , mPersuasionDialog(new ResponseCallback(this))

View file

@ -103,10 +103,13 @@ namespace MWGui
, mIsDrowning(false) , mIsDrowning(false)
, mDrowningFlashTheta(0.f) , mDrowningFlashTheta(0.f)
{ {
#ifndef USE_OPENXR #ifdef USE_OPENXR
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
#endif
mMainWidgetBaseSize = mMainWidget->getSize(); mMainWidgetBaseSize = mMainWidget->getSize();
mMainWidget->setSize(mMainWidgetBaseSize);
#else
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
mMainWidgetBaseSize = mMainWidget->getSize();
#endif
// Energy bars // Energy bars
getWidget(mHealthFrame, "HealthFrame"); getWidget(mHealthFrame, "HealthFrame");
@ -381,9 +384,6 @@ namespace MWGui
mDrowningFlashTheta += dt * osg::PI*2; mDrowningFlashTheta += dt * osg::PI*2;
mSpellIcons->updateWidgets(mEffectBox, true); 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()) if (mEnemyActorId != -1 && mEnemyHealth->getVisible())
{ {

View file

@ -117,6 +117,8 @@ namespace MWGui
messageBox->update(height); messageBox->update(height);
height += messageBox->getHeight(); height += messageBox->getHeight();
} }
box->setVisible(true);
} }
void MessageBoxManager::removeStaticMessageBox () void MessageBoxManager::removeStaticMessageBox ()
@ -189,6 +191,11 @@ namespace MWGui
mMessageWidget->setCaptionWithReplacing(mMessage); mMessageWidget->setCaptionWithReplacing(mMessage);
} }
MessageBox::~MessageBox()
{
setVisible(false);
}
void MessageBox::update (int height) void MessageBox::update (int height)
{ {
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();

View file

@ -59,6 +59,7 @@ namespace MWGui
{ {
public: public:
MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message); MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);
~MessageBox();
void setMessage (const std::string& message); void setMessage (const std::string& message);
int getHeight (); int getHeight ();
void update (int height); void update (int height);

View file

@ -46,7 +46,11 @@ namespace
namespace MWGui namespace MWGui
{ {
TradeWindow::TradeWindow() TradeWindow::TradeWindow()
#ifdef USE_OPENXR
: WindowBase("openmw_trade_window_vr.layout")
#else
: WindowBase("openmw_trade_window.layout") : WindowBase("openmw_trade_window.layout")
#endif
, mSortModel(nullptr) , mSortModel(nullptr)
, mTradeModel(nullptr) , mTradeModel(nullptr)
, mItemToSell(-1) , mItemToSell(-1)

View file

@ -53,7 +53,6 @@ void WindowBase::onDoubleClick(MyGUI::Widget *_sender)
void WindowBase::setVisible(bool visible) void WindowBase::setVisible(bool visible)
{ {
Log(Debug::Verbose) << mLayoutName << ".setVisible: " << visible;
bool wasVisible = mMainWidget->getVisible(); bool wasVisible = mMainWidget->getVisible();
mMainWidget->setVisible(visible); mMainWidget->setVisible(visible);
@ -123,26 +122,42 @@ void NoDrop::onFrame(float dt)
MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition(); 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) if (mDrag->mIsOnDragAndDrop)
{ {
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();
while (focus && focus != mWidget) while (focus && focus != mWidget)
{
focus = focus->getParent(); focus = focus->getParent();
}
if (focus == mWidget) if (focus == mWidget)
{
mTransparent = true; mTransparent = true;
} }
}
if (!mWidget->getAbsoluteCoord().inside(mousePos)) if (!mWidget->getAbsoluteCoord().inside(mousePos))
mTransparent = false; mTransparent = false;
if (mTransparent) 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 mWidget->setNeedMouseFocus(false); // Allow click-through
#endif
setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5)); setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5));
} }
else else
{ {
mWidget->setNeedMouseFocus(true); #ifndef USE_OPENXR
mWidget->setNeedMouseFocus(true); // Allow click-through
#endif
setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5)); setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5));
} }
} }

View file

@ -204,6 +204,10 @@ namespace MWGui
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale); mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
mGuiPlatform->initialise(resourcePath, logpath); mGuiPlatform->initialise(resourcePath, logpath);
#ifdef USE_OPENXR
mGuiPlatform->getRenderManagerPtr()->setViewSize(1024, 1024);
#endif
mGui = new MyGUI::Gui; mGui = new MyGUI::Gui;
mGui->initialise(""); mGui->initialise("");
@ -1246,6 +1250,9 @@ namespace MWGui
void WindowManager::windowResized(int x, int y) void WindowManager::windowResized(int x, int y)
{ {
#ifdef USE_OPENXR
return;
#endif
mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y); mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y);
// scaled size // scaled size

View file

@ -297,7 +297,10 @@ namespace MWPhysics
auto* session = MWVR::Environment::get().getSession(); auto* session = MWVR::Environment::get().getSession();
if (session) 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; refpos.rot[2] += yaw;
} }
} }

View file

@ -898,6 +898,7 @@ namespace MWRender
const bool isPlayer = (mPtr == MWMechanics::getPlayer()); const bool isPlayer = (mPtr == MWMechanics::getPlayer());
if (isPlayer) if (isPlayer)
{ {
Log(Debug::Verbose) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName(); Log(Debug::Verbose) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
@ -1068,6 +1069,7 @@ namespace MWRender
if (groupname.compare(0, 4, "jump")) if (groupname.compare(0, 4, "jump"))
if (groupname.compare(0, 4, "walk")) if (groupname.compare(0, 4, "walk"))
if (groupname.compare(0, 3, "run")) if (groupname.compare(0, 3, "run"))
if (groupname.compare(0, 4, "swim"))
return false; return false;
#else #else
(void)groupname; (void)groupname;

View file

@ -361,10 +361,12 @@ namespace MWRender
if(mPreviewMode) if(mPreviewMode)
limit /= 2; limit /= 2;
#ifndef USE_OPENXR
if(angle > limit) if(angle > limit)
angle = limit; angle = limit;
else if(angle < -limit) else if(angle < -limit)
angle = -limit; angle = -limit;
#endif
if (mVanity.enabled || mPreviewMode) { if (mVanity.enabled || mPreviewMode) {
mPreviewCam.pitch = angle; mPreviewCam.pitch = angle;

View file

@ -828,16 +828,7 @@ private:
if (node && node->getName() == "VRGUILayer") 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); injectMousePress(SDL_BUTTON_LEFT, onPress);
//SDL_MouseButtonEvent arg;
//if (onPress)
// mousePressed(arg, SDL_BUTTON_LEFT);
//else
// mouseReleased(arg, SDL_BUTTON_LEFT);
} }
else if (onPress) else if (onPress)
{ {
@ -919,35 +910,11 @@ private:
{ {
mXRInput->updateControls(); 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(); auto* vrGuiManager = Environment::get().getGUIManager();
if (node && node->getName() == "VRGUILayer") vrGuiManager->updateFocus();
{ auto guiCursor = vrGuiManager->guiCursor();
int w, h; mGuiCursorX = guiCursor.x();
SDL_GetWindowSize(mWindow, &w, &h); mGuiCursorY = guiCursor.y();
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);
}
}
OpenXRActionEvent event{}; OpenXRActionEvent event{};
while (mXRInput->nextActionEvent(event)) while (mXRInput->nextActionEvent(event))
@ -988,7 +955,8 @@ private:
toggleMainMenu(); toggleMainMenu();
// Explicitly request position update here so that the player can move the menu // Explicitly request position update here so that the player can move the menu
// using the menu key when the menu can't be toggled. // 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; break;
case A_Screenshot: case A_Screenshot:
screenshot(); screenshot();

View file

@ -131,14 +131,12 @@ namespace MWVR
roll = angle_y; 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; 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; float roll = 0.f;
getEulerAngles(lhandquat, yaw, pitch, roll); getEulerAngles(lhandquat, yaw, pitch, roll);
return yaw;
} }
void OpenXRSession::advanceFrame(void) void OpenXRSession::advanceFrame(void)

View file

@ -47,8 +47,8 @@ public:
//! Update predictions //! Update predictions
void predictNext(int extraPeriods); void predictNext(int extraPeriods);
//! Yaw angle to be used for offsetting movement direction //! Angles to be used for overriding movement direction
float movementYaw(void); void movementAngles(float& yaw, float& pitch);
void advanceFrame(void); void advanceFrame(void);

View file

@ -465,7 +465,7 @@ VRAnimation::VRAnimation(
); );
mWeaponPointerTransform->setName("Weapon Pointer"); mWeaponPointerTransform->setName("Weapon Pointer");
mWeaponPointerTransform->setUpdateCallback(new WeaponPointerController); mWeaponPointerTransform->setUpdateCallback(new WeaponPointerController);
mWeaponDirectionTransform->addChild(mWeaponPointerTransform); //mWeaponDirectionTransform->addChild(mWeaponPointerTransform);
} }
VRAnimation::~VRAnimation() {}; VRAnimation::~VRAnimation() {};
@ -538,7 +538,13 @@ void VRAnimation::setPointForward(bool enabled)
mPointerTransform->removeChild(mPointerRescale); mPointerTransform->removeChild(mPointerRescale);
if (enabled) if (enabled)
{
mPointerTransform->addChild(mPointerRescale); mPointerTransform->addChild(mPointerRescale);
}
else
{
mPointerTarget = MWRender::RayResult{};
}
} }
osg::ref_ptr<osg::Geometry> VRAnimation::createPointerGeometry(void) osg::ref_ptr<osg::Geometry> VRAnimation::createPointerGeometry(void)

View file

@ -20,6 +20,7 @@
#include "../mwrender/camera.hpp" #include "../mwrender/camera.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwgui/windowbase.hpp" #include "../mwgui/windowbase.hpp"
#include <MyGUI_Widget.h> #include <MyGUI_Widget.h>
@ -28,9 +29,22 @@
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
#include <MyGUI_Window.h> #include <MyGUI_Window.h>
namespace osg
{
// Convenience
const double PI_8 = osg::PI_4 / 2.;
}
namespace MWVR 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 /// RTT camera used to draw the osg GUI to a texture
class GUICamera : public osg::Camera class GUICamera : public osg::Camera
{ {
@ -120,18 +134,11 @@ private:
VRGUILayer::VRGUILayer( VRGUILayer::VRGUILayer(
osg::ref_ptr<osg::Group> geometryRoot, osg::ref_ptr<osg::Group> geometryRoot,
osg::ref_ptr<osg::Group> cameraRoot, osg::ref_ptr<osg::Group> cameraRoot,
int width,
int height,
std::string filter, std::string filter,
LayerConfig config, LayerConfig config,
MWGui::Layout* widget,
VRGUIManager* parent) VRGUIManager* parent)
: mConfig(config) : mConfig(config)
, mFilter(filter) , mFilter(filter)
, mWidget(widget)
, mWindow(dynamic_cast<MWGui::WindowBase*>(mWidget))
, mMyGUIWindow(dynamic_cast<MyGUI::Window*>(mWidget->mMainWidget))
, mParent(parent)
, mGeometryRoot(geometryRoot) , mGeometryRoot(geometryRoot)
, mCameraRoot(cameraRoot) , mCameraRoot(cameraRoot)
{ {
@ -139,14 +146,18 @@ VRGUILayer::VRGUILayer(
osg::ref_ptr<osg::Vec2Array> texCoords{ new osg::Vec2Array(4) }; osg::ref_ptr<osg::Vec2Array> texCoords{ new osg::Vec2Array(4) };
osg::ref_ptr<osg::Vec3Array> normals{ new osg::Vec3Array(1) }; 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();
auto extent_units = config.extent * Environment::get().unitsPerMeter() / 2.f;
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 // Define the menu quad
osg::Vec3 top_left (-1, 1, 1); osg::Vec3 top_left (left, 1, top);
osg::Vec3 bottom_left(-1, 1, -1); osg::Vec3 bottom_left(left, 1, bottom);
osg::Vec3 bottom_right(1, 1, -1); osg::Vec3 bottom_right(right , 1, bottom);
osg::Vec3 top_right (1, 1, 1); osg::Vec3 top_right (right, 1, top);
(*vertices)[0] = top_left; (*vertices)[0] = top_left;
(*vertices)[1] = bottom_left; (*vertices)[1] = bottom_left;
(*vertices)[2] = bottom_right; (*vertices)[2] = bottom_right;
@ -163,20 +174,23 @@ VRGUILayer::VRGUILayer(
mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->setDataVariance(osg::Object::DYNAMIC);
mGeometry->setSupportsDisplayList(false); mGeometry->setSupportsDisplayList(false);
mGeometry->setName("VRGUILayer"); mGeometry->setName("VRGUILayer");
mGeometry->setUserData(new VRGUILayerUserData(this));
// Create the camera that will render the menu texture // 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()); 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 // Define state set that allows rendering with transparency
mStateSet->setTextureAttributeAndModes(0, menuTexture(), osg::StateAttribute::ON); osg::StateSet* stateSet = mGeometry->getOrCreateStateSet();
mStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); stateSet->setTextureAttributeAndModes(0, menuTexture(), osg::StateAttribute::ON);
mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON); stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
mStateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
mStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
mGeometry->setStateSet(mStateSet); stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
mGeometry->setStateSet(stateSet);
// Position in the game world // Position in the game world
mTransform->setScale(osg::Vec3(extent_units.x(), 1.f, extent_units.y())); mTransform->setScale(osg::Vec3(extent_units.x(), 1.f, extent_units.y()));
@ -186,6 +200,10 @@ VRGUILayer::VRGUILayer(
mGeometryRoot->addChild(mTransform); mGeometryRoot->addChild(mTransform);
mCameraRoot->addChild(mGUICamera); mCameraRoot->addChild(mGUICamera);
// Edit offset to account for priority
if(!mConfig.sideBySide)
mConfig.offset.y() -= 0.001f * mConfig.priority;
mTransform->addUpdateCallback(new LayerUpdateCallback(this)); mTransform->addUpdateCallback(new LayerUpdateCallback(this));
} }
@ -206,68 +224,174 @@ osg::ref_ptr<osg::Texture2D> VRGUILayer::menuTexture()
return nullptr; return nullptr;
} }
void VRGUILayer::updatePose() void VRGUILayer::setAngle(float angle)
{ {
osg::Vec3 eye{}; mRotation = osg::Quat{ angle, osg::Z_AXIS };
osg::Vec3 center{}; updatePose();
osg::Vec3 up{}; }
// Get head pose by reading the camera view matrix to place the GUI in the world. void VRGUILayer::updateTracking(const Pose& headPose)
Pose headPose{}; {
auto* world = MWBase::Environment::get().getWorld(); if (mConfig.trackingMode == TrackingMode::Menu)
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();
if (mConfig.trackedLimb == TrackedLimb::HEAD)
{ {
mTrackedPose = headPose; mTrackedPose = headPose;
mTrackedPose.orientation = mTrackedPose.orientation.inverse();
} }
else else
{ {
// If it's not head, it's one of the hands, so i don't bother checking auto* anim = MWVR::Environment::get().getPlayerAnimation();
auto* session = MWVR::Environment::get().getSession(); if (anim)
auto& poses = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw); {
mTrackedPose = poses.hands[(int)TrackedSpace::STAGE][(int)mConfig.trackedLimb]; const osg::Node* hand = nullptr;
// World position is the head, so must add difference between head and hand in tracking space to world pose if (mConfig.trackingMode == TrackingMode::HudLeftHand)
mTrackedPose.position = mTrackedPose.position * MWVR::Environment::get().unitsPerMeter() - poses.head[(int)TrackedSpace::STAGE].position * MWVR::Environment::get().unitsPerMeter() + headPose.position; 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; auto axis = osg::Z_AXIS;
osg::Quat vertical; osg::Quat vertical;
auto local = mLayerPose.orientation * axis; auto local = orientation * axis;
vertical.makeRotate(local, axis); vertical.makeRotate(local, axis);
mLayerPose.orientation = mLayerPose.orientation * vertical; orientation = orientation * vertical;
} }
// Orient the offset and move the layer // 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->setAttitude(orientation);
mTransform->setPosition(mLayerPose.position); 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() void VRGUILayer::update()
{ {
if (mConfig.trackingMode == TrackingMode::Auto) if (mConfig.trackingMode != TrackingMode::Menu)
updatePose(); updateTracking();
if (mConfig.stretch) if (mConfig.sideBySide)
{ {
if (mWindow && mMyGUIWindow) // 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)
{ {
mWindow->setCoordf(0.f, 0.f, 1.f, 1.f); auto w = mConfig.myGUIViewSize.x();
mWindow->onWindowResize(mMyGUIWindow); 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);
}
void
VRGUILayer::removeWidget(
MWGui::Layout* widget)
{
for (auto it = mWidgets.begin(); it != mWidgets.end(); it++)
{
if (*it == widget)
{
mWidgets.erase(it);
return;
} }
} }
} }
@ -301,148 +425,255 @@ void VRGUIManager::showGUIs(bool show)
{ {
} }
LayerConfig gDefaultConfig = LayerConfig static const LayerConfig createDefaultConfig(int priority)
{ {
true, // stretch return LayerConfig{
1,
false, // side-by-side
osg::Vec4{0.f,0.f,0.f,.75f}, // background osg::Vec4{0.f,0.f,0.f,.75f}, // background
osg::Quat{}, // rotation osg::Vec3(0.f,0.66f,-.25f), // offset
osg::Vec3(0.f,1.f,0.f), // offset osg::Vec2(0.f,0.f), // center (model space)
osg::Vec2(1.f, 1.f), // extent (meters) osg::Vec2(1.f, 1.f), // extent (meters)
osg::Vec2i(2024,2024), // resolution (pixels) 1024, // Spatial resolution (pixels per meter)
TrackedLimb::HEAD, osg::Vec2i(2048,2048), // Texture resolution
TrackingMode::Manual, osg::Vec2(1,1),
true // vertical 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;
static const float sSideBySideRadius = 1.f;
static const float sSideBySideAzimuthInterval = -osg::PI_4;
static const LayerConfig createSideBySideConfig(int priority)
{
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 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 LayerConfig gStatusHUDConfig = LayerConfig
{ {
false, // stretch 0,
osg::Vec4{0.f,0.f,0.f,0.f}, // background false, // side-by-side
osg::Quat{}, // rotation osg::Vec4{}, // background
osg::Vec3(0.f,.0f,.2f), // offset (meters) osg::Vec3(0.025f,.025f,.066f), // offset (meters)
osg::Vec2(.2f, .2f), // extent (meters) osg::Vec2(0.f,0.5f), // center (model space)
osg::Vec2i(1024,512), // resolution (pixels) osg::Vec2(.1f, .1f), // extent (meters)
TrackedLimb::RIGHT_HAND, 1024, // resolution (pixels per meter)
TrackingMode::Auto, osg::Vec2i(1024,1024),
false // vertical gDefaultConfig.myGUIViewSize,
}; SizingMode::Auto,
TrackingMode::HudLeftHand,
LayerConfig gMinimapHUDConfig = 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
}; };
LayerConfig gPopupConfig = LayerConfig LayerConfig gPopupConfig = LayerConfig
{ {
false, // stretch 0,
false, // side-by-side
osg::Vec4{0.f,0.f,0.f,0.f}, // background osg::Vec4{0.f,0.f,0.f,0.f}, // background
osg::Quat{}, // rotation osg::Vec3(-0.025f,.025f,.066f), // offset (meters)
osg::Vec3(0.f,0.f,.2f), // offset osg::Vec2(0.f,0.5f), // center (model space)
osg::Vec2(.2f, .2f), // extent (meters) osg::Vec2(.1f, .1f), // extent (meters)
osg::Vec2i(1024,1024), 1024, // resolution (pixels per meter)
TrackedLimb::RIGHT_HAND, osg::Vec2i(2048,2048),
TrackingMode::Auto, gDefaultConfig.myGUIViewSize,
false // vertical 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 = static std::map<std::string, LayerConfig&> gLayerConfigs =
{ {
{"StatusHUD", gStatusHUDConfig}, {"StatusHUD", gStatusHUDConfig},
{"MinimapHUD", gMinimapHUDConfig}, //{"MinimapHUD", gMinimapHUDConfig},
{"Popup", gPopupConfig}, {"Popup", gPopupConfig},
{"Windows", gWindowsConfig},
{"JournalBooks", gJournalBooksConfig}, {"JournalBooks", gJournalBooksConfig},
{"SpellWindow", gSpellWindowConfig}, {"InventoryCompanionWindow", gInventoryCompanionWindowConfig},
{"InventoryWindow", gInventoryWindowConfig}, {"InventoryWindow", gInventoryWindowConfig},
{"SpellWindow", gSpellWindowConfig},
{"MapWindow", gMapWindowConfig}, {"MapWindow", gMapWindowConfig},
{"StatsWindow", gStatsWindowConfig}, {"StatsWindow", gStatsWindowConfig},
{"Default", gDefaultConfig}, {"DialogueWindow", gDialogueWindowConfig},
{"MessageBox", gMessageBoxConfig},
{"Windows", gDefaultWindowsConfig},
{"Notification", gNotificationConfig}
}; };
static std::set<std::string> layerBlacklist = 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) void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible)
{ {
auto* layer = widget->mMainWidget->getLayer(); 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(); auto name = layer->getName();
Log(Debug::Verbose) << "setVisible (" << name << "): " << visible; Log(Debug::Verbose) << "setVisible (" << name << "): " << visible;
@ -453,60 +684,103 @@ void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible)
widget->setLayerPick(false); widget->setLayerPick(false);
return; return;
} }
if (visible) if (visible)
{ insertWidget(widget);
if (mLayers.find(name) == mLayers.end())
{
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;
}
updatePose();
}
else else
{ removeWidget(widget);
auto it = mLayers.find(name);
if (it != mLayers.end())
{
if (it->second.get() == mFocusLayer)
setFocusLayer(nullptr);
mLayers.erase(it);
Log(Debug::Verbose) << "Erased GUI layer " << name;
}
}
} }
void VRGUIManager::updatePose(void) 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) for (auto& layer : mLayers)
layer.second->updatePose(); layer.second->updateTracking(mHeadPose);
}
void VRGUIManager::updateFocus()
{
auto* anim = MWVR::Environment::get().getPlayerAnimation();
if (anim && anim->mPointerTarget.mHit)
{
std::shared_ptr<VRGUILayer> newFocusLayer = nullptr;
auto* node = anim->mPointerTarget.mHitNode;
if (node->getName() == "VRGUILayer")
{
VRGUILayerUserData* userData = static_cast<VRGUILayerUserData*>(node->getUserData());
newFocusLayer = userData->mLayer.lock();
}
if (newFocusLayer && newFocusLayer->mFilter != "Notification")
{
setFocusLayer(newFocusLayer.get());
computeGuiCursor(anim->mPointerTarget.mHitPointLocal);
}
}
} }
void VRGUIManager::setFocusLayer(VRGUILayer* layer) void VRGUIManager::setFocusLayer(VRGUILayer* layer)
{ {
if (layer == mFocusLayer)
return;
if (mFocusLayer) if (mFocusLayer)
mFocusLayer->mWidget->setLayerPick(false); {
mFocusLayer->mWidgets.front()->setLayerPick(false);
}
mFocusLayer = layer; mFocusLayer = layer;
if (mFocusLayer) 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);
} }
} }

View file

@ -4,6 +4,7 @@
#include <map> #include <map>
#include <set> #include <set>
#include <regex> #include <regex>
#include <MyGUI_Widget.h>
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/TexMat> #include <osg/TexMat>
@ -34,21 +35,34 @@ namespace MWVR
enum class TrackingMode enum class TrackingMode
{ {
Auto, //!< Update tracking every frame Menu, //!< Menu quads with fixed position based on head tracking.
Manual //!< Update tracking only on user request or when GUI visibility changes. 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 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::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::Vec3 offset; //!< Offset from tracked node in meters
osg::Vec2 extent; //!< Spatial extent of the layer in meters 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::Vec2i resolution; //!< Pixel resolution of the texture osg::Vec2 extent; //!< Spatial extent of the layer in meters when using Fixed sizing mode
TrackedLimb trackedLimb; //!< Which limb to track 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 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 class VRGUILayer
@ -57,11 +71,8 @@ namespace MWVR
VRGUILayer( VRGUILayer(
osg::ref_ptr<osg::Group> geometryRoot, osg::ref_ptr<osg::Group> geometryRoot,
osg::ref_ptr<osg::Group> cameraRoot, osg::ref_ptr<osg::Group> cameraRoot,
int width,
int height,
std::string filter, std::string filter,
LayerConfig config, LayerConfig config,
MWGui::Layout* widget,
VRGUIManager* parent); VRGUIManager* parent);
~VRGUILayer(); ~VRGUILayer();
@ -69,33 +80,39 @@ namespace MWVR
osg::ref_ptr<osg::Texture2D> menuTexture(); osg::ref_ptr<osg::Texture2D> menuTexture();
void setAngle(float angle);
void updateTracking(const Pose& headPose = {});
void updatePose(); void updatePose();
void updateRect();
void update(); 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: public:
Pose mTrackedPose{}; Pose mTrackedPose{};
Pose mLayerPose{};
LayerConfig mConfig; LayerConfig mConfig;
std::string mFilter; std::string mFilter;
MWGui::Layout* mWidget; std::vector<MWGui::Layout*> mWidgets;
MWGui::WindowBase* mWindow;
MyGUI::Window* mMyGUIWindow;
VRGUIManager* mParent;
osg::ref_ptr<osg::Group> mGeometryRoot; osg::ref_ptr<osg::Group> mGeometryRoot;
osg::ref_ptr<osg::Geometry> mGeometry{ new osg::Geometry }; osg::ref_ptr<osg::Geometry> mGeometry{ new osg::Geometry };
osg::ref_ptr<osg::PositionAttitudeTransform> mTransform{ new osg::PositionAttitudeTransform }; osg::ref_ptr<osg::PositionAttitudeTransform> mTransform{ new osg::PositionAttitudeTransform };
osg::ref_ptr<osg::Group> mCameraRoot; osg::ref_ptr<osg::Group> mCameraRoot;
osg::ref_ptr<osg::StateSet> mStateSet{ new osg::StateSet };
osg::ref_ptr<GUICamera> mGUICamera; 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 class VRGUILayerUserData : public osg::Referenced
{ {
public: public:
VRGUILayerUserData(VRGUILayer* layer) : mLayer(layer) {}; VRGUILayerUserData(std::shared_ptr<VRGUILayer> layer) : mLayer(layer) {};
VRGUILayer* mLayer; std::weak_ptr<VRGUILayer> mLayer;
}; };
class VRGUIManager class VRGUIManager
@ -110,18 +127,38 @@ namespace MWVR
void setVisible(MWGui::Layout*, bool visible); 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); void setFocusLayer(VRGUILayer* layer);
osg::Vec2i guiCursor() { return mGuiCursor; };
private: private:
void computeGuiCursor(osg::Vec3 hitPoint);
osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr }; osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr };
osg::ref_ptr<osg::Group> mGUIGeometriesRoot{ new osg::Group }; osg::ref_ptr<osg::Group> mGUIGeometriesRoot{ new osg::Group };
osg::ref_ptr<osg::Group> mGUICamerasRoot{ 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 }; VRGUILayer* mFocusLayer{ nullptr };
}; };
} }

View file

@ -1100,6 +1100,15 @@ namespace MWWorld
MWWorld::Ptr World::getFacedObject() 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; MWWorld::Ptr facedObject;
if (MWBase::Environment::get().getWindowManager()->isGuiMode() && if (MWBase::Environment::get().getWindowManager()->isGuiMode() &&

View file

@ -364,6 +364,7 @@ public:
GUICamera(osg::Camera::RenderOrder order, RenderManager* parent, std::string filter) GUICamera(osg::Camera::RenderOrder order, RenderManager* parent, std::string filter)
: mParent(parent) : mParent(parent)
, mUpdate(false) , mUpdate(false)
, mFilter(filter)
{ {
setReferenceFrame(osg::Transform::ABSOLUTE_RF); setReferenceFrame(osg::Transform::ABSOLUTE_RF);
setProjectionResizePolicy(osg::Camera::FIXED); setProjectionResizePolicy(osg::Camera::FIXED);
@ -405,6 +406,7 @@ public:
osg::ref_ptr<Drawable> mDrawable; osg::ref_ptr<Drawable> mDrawable;
MyGUI::RenderTargetInfo mInfo; MyGUI::RenderTargetInfo mInfo;
bool mUpdate; bool mUpdate;
std::string mFilter;
}; };
@ -436,6 +438,10 @@ RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, R
{ {
if (scalingFactor != 0.f) if (scalingFactor != 0.f)
mInvScalingFactor = 1.f / scalingFactor; mInvScalingFactor = 1.f / scalingFactor;
osg::ref_ptr<osg::Viewport> vp = mViewer->getCamera()->getViewport();
setViewSize(vp->width(), vp->height());
} }
RenderManager::~RenderManager() RenderManager::~RenderManager()
@ -552,22 +558,14 @@ void GUICamera::collectDrawCalls(std::string filter)
MyGUI::LayerManager* myGUILayers = MyGUI::LayerManager::getInstancePtr(); MyGUI::LayerManager* myGUILayers = MyGUI::LayerManager::getInstancePtr();
if (myGUILayers != nullptr) if (myGUILayers != nullptr)
{ {
std::regex layerRegex{ filter, std::regex_constants::icase };
for (unsigned i = 0; i < myGUILayers->getLayerCount(); i++) for (unsigned i = 0; i < myGUILayers->getLayerCount(); i++)
{ {
auto layer = myGUILayers->getLayer(i); auto layer = myGUILayers->getLayer(i);
auto name = layer->getName(); auto name = layer->getName();
if (std::regex_search(name, layerRegex)) if (name == filter)
{
//std::cout << "Including Layer: " << layer->getName() << std::endl;
layer->renderToTarget(this, mUpdate); layer->renderToTarget(this, mUpdate);
} }
else {
//std::cout << "Excluding Layer: " << layer->getName() << std::endl;
}
}
} }
end(); end();
@ -590,6 +588,11 @@ void RenderManager::setViewSize(int width, int height)
if(width < 1) width = 1; if(width < 1) width = 1;
if(height < 1) height = 1; if(height < 1) height = 1;
////#ifdef USE_OPENXR
// width = 1024;
// height = 1024;
////#endif
mViewSize.set(width * mInvScalingFactor, height * mInvScalingFactor); mViewSize.set(width * mInvScalingFactor, height * mInvScalingFactor);
for (auto* camera : mGuiCameras) 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); osg::ref_ptr<GUICamera> camera = new GUICamera(static_cast<osg::Camera::RenderOrder>(order), this, layerFilter);
mGuiCameras.insert(camera); mGuiCameras.insert(camera);
osg::ref_ptr<osg::Viewport> vp = mViewer->getCamera()->getViewport(); camera->setViewport(0, 0, mViewSize.width, mViewSize.height);
setViewSize(vp->width(), vp->height()); camera->setViewSize(mViewSize);
return camera; return camera;
} }

View file

@ -16,8 +16,8 @@ set(MYGUI_FILES
openmw_box.skin.xml openmw_box.skin.xml
openmw_button.skin.xml openmw_button.skin.xml
openmw_chargen_birth.layout openmw_chargen_birth.layout
openmw_chargen_class_description.layout
openmw_chargen_class.layout openmw_chargen_class.layout
openmw_chargen_class_description.layout
openmw_chargen_create_class.layout openmw_chargen_create_class.layout
openmw_chargen_generate_class_result.layout openmw_chargen_generate_class_result.layout
openmw_chargen_race.layout openmw_chargen_race.layout
@ -25,78 +25,80 @@ set(MYGUI_FILES
openmw_chargen_select_attribute.layout openmw_chargen_select_attribute.layout
openmw_chargen_select_skill.layout openmw_chargen_select_skill.layout
openmw_chargen_select_specialization.layout openmw_chargen_select_specialization.layout
openmw_companion_window.layout
openmw_confirmation_dialog.layout openmw_confirmation_dialog.layout
openmw_console.layout openmw_console.layout
openmw_console.skin.xml openmw_console.skin.xml
openmw_container_window.layout openmw_container_window.layout
openmw_container_window_vr.layout
openmw_count_window.layout openmw_count_window.layout
openmw_debug_window.layout
openmw_debug_window.skin.xml
openmw_dialogue_window.layout openmw_dialogue_window.layout
openmw_dialogue_window_vr.layout
openmw_dialogue_window.skin.xml openmw_dialogue_window.skin.xml
openmw_edit.skin.xml openmw_edit.skin.xml
openmw_edit_effect.layout
openmw_edit_note.layout
openmw_enchanting_dialog.layout
openmw_font.xml openmw_font.xml
openmw_hud.layout
openmw_hud_box.skin.xml openmw_hud_box.skin.xml
openmw_hud_energybar.skin.xml openmw_hud_energybar.skin.xml
openmw_hud.layout
openmw_hud_vr.layout openmw_hud_vr.layout
openmw_infobox.layout openmw_infobox.layout
openmw_interactive_messagebox.layout openmw_interactive_messagebox.layout
openmw_interactive_messagebox_notransp.layout openmw_interactive_messagebox_notransp.layout
openmw_inventory_window.layout openmw_inventory_window.layout
openmw_inventory_window_vr.layout openmw_inventory_window_vr.layout
openmw_itemselection_dialog.layout
openmw_jail_screen.layout
openmw_journal.layout openmw_journal.layout
openmw_journal.skin.xml openmw_journal.skin.xml
openmw_layers.xml openmw_layers.xml
openmw_layers_vr.xml openmw_layers_vr.xml
openmw_levelup_dialog.layout
openmw_list.skin.xml openmw_list.skin.xml
openmw_loading_screen.layout
openmw_magicselection_dialog.layout
openmw_mainmenu.layout openmw_mainmenu.layout
openmw_mainmenu.skin.xml openmw_mainmenu.skin.xml
openmw_map_window.layout openmw_map_window.layout
openmw_map_window_vr.layout
openmw_map_window.skin.xml openmw_map_window.skin.xml
openmw_map_window_vr.layout
openmw_merchantrepair.layout
openmw_messagebox.layout openmw_messagebox.layout
openmw_persuasion_dialog.layout
openmw_pointer.xml openmw_pointer.xml
openmw_progress.skin.xml openmw_progress.skin.xml
openmw_resources.xml
openmw_scroll.layout
openmw_scroll.skin.xml
openmw_settings_window.layout
openmw_settings.xml
openmw_spell_window.layout
openmw_spell_window_vr.layout
openmw_stats_window.layout
openmw_stats_window_vr.layout
openmw_text_input.layout
openmw_text.skin.xml
openmw_tooltips.layout
openmw_trade_window.layout
openmw_spell_buying_window.layout
openmw_windows.skin.xml
openmw_quickkeys_menu.layout openmw_quickkeys_menu.layout
openmw_quickkeys_menu_assign.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_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_recharge_dialog.layout
openmw_repair.layout
openmw_resources.xml
openmw_savegame_dialog.layout
openmw_screen_fader.layout openmw_screen_fader.layout
openmw_screen_fader_hit.layout openmw_screen_fader_hit.layout
openmw_edit_note.layout openmw_scroll.layout
openmw_debug_window.layout openmw_scroll.skin.xml
openmw_debug_window.skin.xml openmw_settings.xml
openmw_jail_screen.layout 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.skin.xml
openmw_text_input.layout
openmw_tooltips.layout
openmw_trade_window.layout
openmw_trade_window_vr.layout
openmw_trainingwindow.layout
openmw_travel_window.layout
openmw_wait_dialog.layout
openmw_wait_dialog_progressbar.layout
openmw_windows.skin.xml
DejaVuLGCSansMono.ttf DejaVuLGCSansMono.ttf
../launcher/images/openmw.png ../launcher/images/openmw.png
OpenMWResourcePlugin.xml OpenMWResourcePlugin.xml

View file

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

View file

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

View file

@ -1,15 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout"> <MyGUI type="Layout">
<Widget type="Widget" layer="StatusHUD" position="0 0 185 80" name="_Main" align="Default"> <Widget type="Widget" layer="StatusHUD" position="0 0 255 91" 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>
<!-- Energy bars --> <!-- 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"/> <Property key="Visible" value="false"/>
</Widget> </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="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/> <UserString key="ToolTipLayout" value="HealthToolTip"/>
<UserString key="ImageTexture_HealthImage" value="icons\k\health.dds"/> <UserString key="ImageTexture_HealthImage" value="icons\k\health.dds"/>
@ -17,7 +14,7 @@
<Property key="NeedMouse" value="false"/> <Property key="NeedMouse" value="false"/>
</Widget> </Widget>
</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="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/> <UserString key="ToolTipLayout" value="HealthToolTip"/>
<UserString key="ImageTexture_HealthImage" value="icons\k\magicka.dds"/> <UserString key="ImageTexture_HealthImage" value="icons\k\magicka.dds"/>
@ -25,7 +22,7 @@
<Property key="NeedMouse" value="false"/> <Property key="NeedMouse" value="false"/>
</Widget> </Widget>
</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="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/> <UserString key="ToolTipLayout" value="HealthToolTip"/>
<UserString key="ImageTexture_HealthImage" value="icons\k\fatigue.dds"/> <UserString key="ImageTexture_HealthImage" value="icons\k\fatigue.dds"/>
@ -35,7 +32,7 @@
</Widget> </Widget>
<!-- Equipped weapon box --> <!-- 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"> <Widget type="Widget" skin="HUD_Box" position="0 0 36 36">
<Property key="NeedMouse" value="false"/> <Property key="NeedMouse" value="false"/>
<Widget type="ItemWidget" skin="MW_ItemIconNoShadow" position="-3 -3 42 42" align="Left Top" name="WeapImage"> <Widget type="ItemWidget" skin="MW_ItemIconNoShadow" position="-3 -3 42 42" align="Left Top" name="WeapImage">
@ -48,7 +45,7 @@
</Widget> </Widget>
<!-- Selected spell box --> <!-- 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="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"/> <Widget type="SpellWidget" skin="MW_ItemIconNoShadow" position="-3 -3 42 42" align="Left Top" name="SpellImage"/>
<Property key="NeedMouse" value="false"/> <Property key="NeedMouse" value="false"/>
@ -59,7 +56,7 @@
</Widget> </Widget>
<!-- Sneak indicator box --> <!-- 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"/> <Property key="Visible" value="false"/>
<Widget type="Widget" skin="HUD_Box" position="0 0 36 36"> <Widget type="Widget" skin="HUD_Box" position="0 0 36 36">
<Property key="NeedMouse" value="false"/> <Property key="NeedMouse" value="false"/>
@ -70,44 +67,12 @@
</Widget> </Widget>
</Widget> </Widget>
</Widget> <!-- Spell effects box -->
<Widget type="Widget" layer="MiniMapHUD" position="0 0 300 92" name="_Main2" align="Stretch"> <Widget type="Widget" skin="HUD_Box_Transparent" position="0 0 20 20" align="Left Bottom" name="EffectBox">
<!-- Drowning bar -->
<Widget type="Window" skin="MW_Dialog" position="0 36 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}"/>
<Property key="TextAlign" value="Center"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
</Widget>
<Widget type="Widget" skin="MW_Box" position="11 29 200 10" align="Stretch" name="BoundingBox"/>
<Widget type="ProgressBar" skin="MW_Progress_Drowning_Full" position="13 31 196 6" align="Center Top" name="Drowning">
<Property key="NeedMouse" value="false"/>
</Widget>
<Widget type="Widget" skin="MW_Progress_Drowning_Small" position="15 33 192 2" align="Center Top" name="Flash"/>
</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">
<Property key="Visible" value="false"/>
<Property key="TextAlign" value="Left"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
<Property key="NeedMouse" value="false"/>
</Widget>
<!-- Cell name display when cell changes -->
<Widget type="TextBox" skin="SandText" position="0 89 288 24" name="CellName" align="Left Bottom HStretch">
<Property key="Visible" value="false"/>
<Property key="TextAlign" value="Right"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
<Property key="NeedMouse" value="false"/>
</Widget> </Widget>
<!-- Map box --> <!-- Map box -->
<Widget type="Widget" skin="" position="223 15 65 65" name="MiniMapBox" align="Right Bottom"> <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="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="ScrollView" skin="MW_MapView" position="2 2 61 61" align="Left Bottom" name="MiniMap">
@ -124,6 +89,40 @@
</Widget> </Widget>
</Widget> </Widget>
<!-- Drowning bar -->
<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}"/>
<Property key="TextAlign" value="Center"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
</Widget>
<Widget type="Widget" skin="MW_Box" position="11 29 200 10" align="Stretch" name="BoundingBox"/>
<Widget type="ProgressBar" skin="MW_Progress_Drowning_Full" position="13 31 196 6" align="Center Top" name="Drowning">
<Property key="NeedMouse" value="false"/>
</Widget>
<Widget type="Widget" skin="MW_Progress_Drowning_Small" position="15 33 192 2" align="Center Top" name="Flash"/>
</Widget>
<!-- Equipped weapon/selected spell name display for a few seconds after it changes -->
<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"/>
<Property key="TextShadowColour" value="0 0 0"/>
<Property key="NeedMouse" value="false"/>
</Widget>
<!-- Cell name display when cell changes -->
<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="Left"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
<Property key="NeedMouse" value="false"/>
</Widget>
<!-- Crosshair --> <!-- Crosshair -->
<Widget type="ImageBox" skin="HUD_Crosshair" position="0 0 32 32" align="Center Center" name="Crosshair"> <Widget type="ImageBox" skin="HUD_Crosshair" position="0 0 32 32" align="Center Center" name="Crosshair">
</Widget> </Widget>

View file

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

View file

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