SceneSwitcher/plugins/openvr/macro-condition-openvr.cpp
2025-06-20 22:29:35 +02:00

304 lines
8.1 KiB
C++

#include "macro-condition-openvr.hpp"
#include <layout-helpers.hpp>
#include <macro-condition-edit.hpp>
#include <openvr.h>
namespace advss {
const std::string MacroConditionOpenVR::id = "openvr";
bool MacroConditionOpenVR::_registered = MacroConditionFactory::Register(
MacroConditionOpenVR::id,
{MacroConditionOpenVR::Create, MacroConditionOpenVREdit::Create,
"AdvSceneSwitcher.condition.openvr"});
static vr::IVRSystem *openvrSystem;
static std::mutex openvrMutex;
static std::condition_variable openvrCV;
static void processOpenVREvents()
{
std::unique_lock<std::mutex> lock(openvrMutex);
vr::VREvent_t event;
while (true) {
if (openvrSystem->PollNextEvent(&event, sizeof(event)) &&
event.eventType == vr::VREvent_Quit) {
openvrSystem->AcknowledgeQuit_Exiting();
vr::VR_Shutdown();
openvrSystem = nullptr;
break;
}
openvrCV.wait_for(lock, std::chrono::milliseconds(100));
}
blog(LOG_INFO, "stop handling openVR events");
}
static void initOpenVR(vr::EVRInitError &err)
{
openvrSystem = vr::VR_Init(&err, vr::VRApplication_Background);
if (openvrSystem) {
// Don't kill OBS if SteamVR is exiting
std::thread t(processOpenVREvents);
t.detach();
}
}
struct TrackingData {
float x, y, z;
bool valid = false;
};
static TrackingData getOpenVRPos(vr::EVRInitError &err)
{
TrackingData data;
std::unique_lock<std::mutex> lock(openvrMutex);
if (!openvrSystem) {
initOpenVR(err);
}
if (openvrSystem && vr::VRCompositor() &&
openvrSystem->IsTrackedDeviceConnected(0)) {
vr::TrackedDevicePose_t poses[vr::k_unMaxTrackedDeviceCount];
openvrSystem->GetDeviceToAbsoluteTrackingPose(
vr::TrackingUniverseStanding, 0.0, poses,
vr::k_unMaxTrackedDeviceCount);
auto hmdPose = poses[vr::k_unTrackedDeviceIndex_Hmd];
auto mat = hmdPose.mDeviceToAbsoluteTracking.m;
data.x = mat[0][3];
data.y = mat[1][3];
data.z = mat[2][3];
data.valid = true;
}
return data;
}
bool MacroConditionOpenVR::CheckCondition()
{
vr::EVRInitError err;
TrackingData data = getOpenVRPos(err);
if (!data.valid) {
return false;
}
return data.x >= _minX && data.y >= _minY && data.z >= _minZ &&
data.x <= _maxX && data.y <= _maxY && data.z <= _maxZ;
}
bool MacroConditionOpenVR::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
_minX.Save(obj, "minX");
_minY.Save(obj, "minY");
_minZ.Save(obj, "minZ");
_maxX.Save(obj, "maxX");
_maxY.Save(obj, "maxY");
_maxZ.Save(obj, "maxZ");
obs_data_set_int(obj, "version", 1);
return true;
}
bool MacroConditionOpenVR::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
if (!obs_data_has_user_value(obj, "version")) {
_minX = obs_data_get_double(obj, "minX");
_minY = obs_data_get_double(obj, "minY");
_minZ = obs_data_get_double(obj, "minZ");
_maxX = obs_data_get_double(obj, "maxX");
_maxY = obs_data_get_double(obj, "maxY");
_maxZ = obs_data_get_double(obj, "maxZ");
} else {
_minX.Load(obj, "minX");
_minY.Load(obj, "minY");
_minZ.Load(obj, "minZ");
_maxX.Load(obj, "maxX");
_maxY.Load(obj, "maxY");
_maxZ.Load(obj, "maxZ");
}
return true;
}
MacroConditionOpenVREdit::MacroConditionOpenVREdit(
QWidget *parent, std::shared_ptr<MacroConditionOpenVR> entryData)
: QWidget(parent),
_minX(new VariableDoubleSpinBox()),
_minY(new VariableDoubleSpinBox()),
_minZ(new VariableDoubleSpinBox()),
_maxX(new VariableDoubleSpinBox()),
_maxY(new VariableDoubleSpinBox()),
_maxZ(new VariableDoubleSpinBox()),
_xPos(new QLabel("-")),
_yPos(new QLabel("-")),
_zPos(new QLabel("-")),
_errLabel(new QLabel())
{
_errLabel->setVisible(false);
_minX->setPrefix("Min X: ");
_minY->setPrefix("Min Y: ");
_minZ->setPrefix("Min Z: ");
_maxX->setPrefix("Max X: ");
_maxY->setPrefix("Max Y: ");
_maxZ->setPrefix("Max Z: ");
_minX->setMinimum(-99);
_minY->setMinimum(-99);
_minZ->setMinimum(-99);
_maxX->setMinimum(-99);
_maxY->setMinimum(-99);
_maxZ->setMinimum(-99);
_minX->setMaximum(99);
_minY->setMaximum(99);
_minZ->setMaximum(99);
_maxX->setMaximum(99);
_maxY->setMaximum(99);
_maxZ->setMaximum(99);
QWidget::connect(
_minX,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(MinXChanged(const NumberVariable<double> &)));
QWidget::connect(
_minY,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(MinYChanged(const NumberVariable<double> &)));
QWidget::connect(
_minZ,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(MinZChanged(const NumberVariable<double> &)));
QWidget::connect(
_maxX,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(MaxXChanged(const NumberVariable<double> &)));
QWidget::connect(
_maxY,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(MaxYChanged(const NumberVariable<double> &)));
QWidget::connect(
_maxZ,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(MaxZChanged(const NumberVariable<double> &)));
QGridLayout *controlsLayout = new QGridLayout;
controlsLayout->addWidget(_minX, 0, 0);
controlsLayout->addWidget(_minY, 0, 1);
controlsLayout->addWidget(_minZ, 0, 2);
controlsLayout->addWidget(_maxX, 1, 0);
controlsLayout->addWidget(_maxY, 1, 1);
controlsLayout->addWidget(_maxZ, 1, 2);
QWidget *controls = new QWidget;
controls->setObjectName("openVRControls");
controls->setStyleSheet(
"#openVRControls { background-color: rgba(0,0,0,0); }");
controls->setLayout(controlsLayout);
controls->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{controls}}", controls},
{"{{xPos}}", _xPos},
{"{{yPos}}", _yPos},
{"{{zPos}}", _zPos},
};
QHBoxLayout *line1 = new QHBoxLayout;
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.openvr.entry.line1"),
line1, widgetPlaceholders);
QHBoxLayout *line2 = new QHBoxLayout;
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.openvr.entry.line2"),
line2, widgetPlaceholders);
QHBoxLayout *line3 = new QHBoxLayout;
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.openvr.entry.line3"),
line3, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1);
mainLayout->addLayout(line2);
mainLayout->addLayout(line3);
mainLayout->addWidget(_errLabel);
setLayout(mainLayout);
connect(&_timer, &QTimer::timeout, this,
&MacroConditionOpenVREdit::UpdateOpenVRPos);
_timer.start(1000);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionOpenVREdit::MinXChanged(const NumberVariable<double> &pos)
{
GUARD_LOADING_AND_LOCK();
_entryData->_minX = pos;
}
void MacroConditionOpenVREdit::MinYChanged(const NumberVariable<double> &pos)
{
GUARD_LOADING_AND_LOCK();
_entryData->_minY = pos;
}
void MacroConditionOpenVREdit::MinZChanged(const NumberVariable<double> &pos)
{
GUARD_LOADING_AND_LOCK();
_entryData->_minZ = pos;
}
void MacroConditionOpenVREdit::MaxXChanged(const NumberVariable<double> &pos)
{
GUARD_LOADING_AND_LOCK();
_entryData->_maxX = pos;
}
void MacroConditionOpenVREdit::MaxYChanged(const NumberVariable<double> &pos)
{
GUARD_LOADING_AND_LOCK();
_entryData->_maxY = pos;
}
void MacroConditionOpenVREdit::MaxZChanged(const NumberVariable<double> &pos)
{
GUARD_LOADING_AND_LOCK();
_entryData->_maxZ = pos;
}
void MacroConditionOpenVREdit::UpdateOpenVRPos()
{
vr::EVRInitError err;
TrackingData data = getOpenVRPos(err);
if (data.valid) {
_xPos->setText(QString::number(data.x));
_yPos->setText(QString::number(data.y));
_zPos->setText(QString::number(data.z));
} else {
_xPos->setText("-");
_yPos->setText("-");
_zPos->setText("-");
_errLabel->setText(
QString(obs_module_text(
"AdvSceneSwitcher.condition.openvr.errorStatus")) +
QString(vr::VR_GetVRInitErrorAsEnglishDescription(err)));
}
_errLabel->setVisible(!data.valid);
adjustSize();
}
void MacroConditionOpenVREdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_minX->SetValue(_entryData->_minX);
_minY->SetValue(_entryData->_minY);
_minZ->SetValue(_entryData->_minZ);
_maxX->SetValue(_entryData->_maxX);
_maxY->SetValue(_entryData->_maxY);
_maxZ->SetValue(_entryData->_maxZ);
}
} // namespace advss