1.openvr_driver.h中接口:
@brief:表示一个追踪设备。在驱动动态库中实现,如vive的driver_lighthouse.dll,运行时被vrserver加载,可用ITrackedDeviceServerProvider对象的Cleanup方法卸载。
vr::IVRServerDriverHost:
@brief:向server发送各类事件通知(Pose或Button按键信息等),提供以下函数功能:
1>.TrackedDeviceAdded:通知server一个追踪设备已经被添加。若返回true,则server将调用该设备的Active函数,若返回false,则该设备不会被active
2>.TrackedDevicePoseUpdated:往server发送pose信息
3>.TrackedDeviceAxisUpdated:往server发送x,y坐标轴信息(Joystick触摸板)
4>.GetRawTrackedDevicePoses:v1.0.7版本添加的函数,为驱动提供设备裸Pose信息
vr::IServerTrackedDeviceProvider:
@brief:用于查询发送追踪设备信息并往server发送,可理解为一个client与server的中间桥梁作用,在驱动dll动态库中实现,运行时被vrserver加载调用。提供以下函数:
1>.HmdError Init( IDriverLog *pDriverLog, vr::IServerDriverHost *pDriverHost, const char *pchUserDriverConfigDir, const char *pchDriverInstallDir ):
@brief:初始化驱动,该函数最先被调用(在任何函数之前)。当且仅当返回HmdError_None时,dll驱动才会被调用。
@pchUserDriverConfigDir:驱动用户配置文件的绝对路径,如:Steam\config
@pchDriverInstallDir:驱动根目录绝对路径,如:Steam\steamapps\common\SteamVR\drivers\lighthouse\bin\win64
@note:在V1.0.6之后,若provider含有HMD,则需在Init返回前,调用TrackedDeviceAdded添加HMD的详细信息。其他设备则可在任何时候调用TrackedDeviceAdded添加。
后续版本被virtual EVRInitError Init( IVRDriverContext *pDriverContext ) = 0所取代。
其中IVRDriverContext在后面分析。
2>.virtual void Cleanup():
@brief: 卸载vr::ITrackedDeviceServerDriver声明的驱动。
3>.virtual const char * const *GetInterfaceVersions() = 0:
@brief:返回驱动使用的vr::ITrackedDeviceServerDriver接口的版本号
4>.virtual void RunFrame() = 0:
@brief:在server的main循环中被调用,通过vr::ITrackedDeviceServerDriver提供的数据接口循环往server发送pose或button或axis等信息
5>.uint32_t GetTrackedDeviceCount():
@brief:返回驱动连接追踪设备的数量。在启动时,可用来初始化驱动的追踪设备链表。
vr::IVRDriverContext:
@brief:获取驱动所需使用到的接口以及获取驱动handle
1>.virtual void *GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError = nullptr ) = 0:
@berief:通过版本号获取接口,以供后续使用。如:
m_pVRServerDriverHost = (IVRServerDriverHost *)VRDriverContext()->GetGenericInterface( IVRServerDriverHost_Version, &eError );
static const char *IVRServerDriverHost_Version = "IVRServerDriverHost_004";
@note:在新版本vr::IServerTrackedDeviceProvider的Init使用到。
比如:
EVRInitError CServerDriver_Sample::Init( vr::IVRDriverContext *pDriverContext )
{
VR_INIT_SERVER_DRIVER_CONTEXT( pDriverContext );
InitDriverLog( vr::VRDriverLog() );
m_pNullHmdLatest = new CSampleDeviceDriver();
vr::VRServerDriverHost()->TrackedDeviceAdded( m_pNullHmdLatest->GetSerialNumber().c_str(), vr::TrackedDeviceClass_HMD, m_pNullHmdLatest );
return VRInitError_None;
}
在VR_INIT_SERVER_DRIVER_CONTEXT( pDriverContext )初始化m_pVRServerDriverHost,m_pVRSettings,
m_propertyHelpers,m_pVRDriverLog,m_pVRDriverManager,m_pVRResources接口,以供后续调用它们的方法。
2>.virtual DriverHandle_t GetDriverHandle() = 0:
@brief:返回该驱动属性容器的句柄
vr::IVRSettings:
@brief:提供接口用来获取类似Steam\steamapps\common\SteamVR\resources\settings\default.vrsettings文件下的属性字段
vr::IVRDriverLog:
@virtual void Log( const char *pchLogMessage ) = 0:
将log输出到log文件,并在log文件名前加驱动名前缀。
vr::IVRProperties:
@brief:对设备的属性进行读写,设备属性有追踪设备系统名,产品型号,产品序列号等。
enum ETrackedDeviceProperty
{
Prop_Invalid = 0,
// general properties that apply to all device classes
Prop_TrackingSystemName_String = 1000,
Prop_ModelNumber_String = 1001,
Prop_SerialNumber_String = 1002,
Prop_RenderModelName_String = 1003,
Prop_WillDriftInYaw_Bool = 1004,
Prop_ManufacturerName_String = 1005,
Prop_TrackingFirmwareVersion_String = 1006,
Prop_HardwareRevision_String = 1007,
Prop_AllWirelessDongleDescriptions_String = 1008,
Prop_ConnectedWirelessDongle_String = 1009,
Prop_DeviceIsWireless_Bool = 1010,
Prop_DeviceIsCharging_Bool = 1011,
Prop_DeviceBatteryPercentage_Float = 1012, // 0 is empty, 1 is full
Prop_StatusDisplayTransform_Matrix34 = 1013,
Prop_Firmware_UpdateAvailable_Bool = 1014,
Prop_Firmware_ManualUpdate_Bool = 1015,
Prop_Firmware_ManualUpdateURL_String = 1016,
Prop_HardwareRevision_Uint64 = 1017,
Prop_FirmwareVersion_Uint64 = 1018,
Prop_FPGAVersion_Uint64 = 1019,
Prop_VRCVersion_Uint64 = 1020,
Prop_RadioVersion_Uint64 = 1021,
Prop_DongleVersion_Uint64 = 1022,
Prop_BlockServerShutdown_Bool = 1023,
Prop_CanUnifyCoordinateSystemWithHmd_Bool = 1024,
Prop_ContainsProximitySensor_Bool = 1025,
Prop_DeviceProvidesBatteryStatus_Bool = 1026,
Prop_DeviceCanPowerOff_Bool = 1027,
Prop_Firmware_ProgrammingTarget_String = 1028,
Prop_DeviceClass_Int32 = 1029,
Prop_HasCamera_Bool = 1030,
Prop_DriverVersion_String = 1031,
Prop_Firmware_ForceUpdateRequired_Bool = 1032,
Prop_ViveSystemButtonFixRequired_Bool = 1033,
Prop_ParentDriver_Uint64 = 1034,
Prop_ResourceRoot_String = 1035,
// Properties that are unique to TrackedDeviceClass_HMD
Prop_ReportsTimeSinceVSync_Bool = 2000,
Prop_SecondsFromVsyncToPhotons_Float = 2001,
Prop_DisplayFrequency_Float = 2002,
Prop_UserIpdMeters_Float = 2003,
Prop_CurrentUniverseId_Uint64 = 2004,
Prop_PreviousUniverseId_Uint64 = 2005,
Prop_DisplayFirmwareVersion_Uint64 = 2006,
Prop_IsOnDesktop_Bool = 2007,
Prop_DisplayMCType_Int32 = 2008,
Prop_DisplayMCOffset_Float = 2009,
Prop_DisplayMCScale_Float = 2010,
Prop_EdidVendorID_Int32 = 2011,
Prop_DisplayMCImageLeft_String = 2012,
Prop_DisplayMCImageRight_String = 2013,
Prop_DisplayGCBlackClamp_Float = 2014,
Prop_EdidProductID_Int32 = 2015,
Prop_CameraToHeadTransform_Matrix34 = 2016,
Prop_DisplayGCType_Int32 = 2017,
Prop_DisplayGCOffset_Float = 2018,
Prop_DisplayGCScale_Float = 2019,
Prop_DisplayGCPrescale_Float = 2020,
Prop_DisplayGCImage_String = 2021,
Prop_LensCenterLeftU_Float = 2022,
Prop_LensCenterLeftV_Float = 2023,
Prop_LensCenterRightU_Float = 2024,
Prop_LensCenterRightV_Float = 2025,
Prop_UserHeadToEyeDepthMeters_Float = 2026,
Prop_CameraFirmwareVersion_Uint64 = 2027,
Prop_CameraFirmwareDescription_String = 2028,
Prop_DisplayFPGAVersion_Uint64 = 2029,
Prop_DisplayBootloaderVersion_Uint64 = 2030,
Prop_DisplayHardwareVersion_Uint64 = 2031,
Prop_AudioFirmwareVersion_Uint64 = 2032,
Prop_CameraCompatibilityMode_Int32 = 2033,
Prop_ScreenshotHorizontalFieldOfViewDegrees_Float = 2034,
Prop_ScreenshotVerticalFieldOfViewDegrees_Float = 2035,
Prop_DisplaySuppressed_Bool = 2036,
Prop_DisplayAllowNightMode_Bool = 2037,
Prop_DisplayMCImageWidth_Int32 = 2038,
Prop_DisplayMCImageHeight_Int32 = 2039,
Prop_DisplayMCImageNumChannels_Int32 = 2040,
Prop_DisplayMCImageData_Binary = 2041,
Prop_SecondsFromPhotonsToVblank_Float = 2042,
// Properties that are unique to TrackedDeviceClass_Controller
Prop_AttachedDeviceId_String = 3000,
Prop_SupportedButtons_Uint64 = 3001,
Prop_Axis0Type_Int32 = 3002, // Return value is of type EVRControllerAxisType
Prop_Axis1Type_Int32 = 3003, // Return value is of type EVRControllerAxisType
Prop_Axis2Type_Int32 = 3004, // Return value is of type EVRControllerAxisType
Prop_Axis3Type_Int32 = 3005, // Return value is of type EVRControllerAxisType
Prop_Axis4Type_Int32 = 3006, // Return value is of type EVRControllerAxisType
Prop_ControllerRoleHint_Int32 = 3007, // Return value is of type ETrackedControllerRole
// Properties that are unique to TrackedDeviceClass_TrackingReference
Prop_FieldOfViewLeftDegrees_Float = 4000,
Prop_FieldOfViewRightDegrees_Float = 4001,
Prop_FieldOfViewTopDegrees_Float = 4002,
Prop_FieldOfViewBottomDegrees_Float = 4003,
Prop_TrackingRangeMinimumMeters_Float = 4004,
Prop_TrackingRangeMaximumMeters_Float = 4005,
Prop_ModeLabel_String = 4006,
// Properties that are used for user interface like icons names
Prop_IconPathName_String = 5000, // usually a directory named "icons"
Prop_NamedIconPathDeviceOff_String = 5001, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceSearching_String = 5002, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceSearchingAlert_String = 5003, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceReady_String = 5004, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceReadyAlert_String = 5005, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceNotReady_String = 5006, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceStandby_String = 5007, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
Prop_NamedIconPathDeviceAlertLow_String = 5008, // PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others
// Properties that are used by helpers, but are opaque to applications
Prop_DisplayHiddenArea_Binary_Start = 5100,
Prop_DisplayHiddenArea_Binary_End = 5150,
// Properties that are unique to drivers
Prop_UserConfigPath_String = 6000,
Prop_InstallPath_String = 6001,
Prop_HasDisplayComponent_Bool = 6002,
Prop_HasControllerComponent_Bool = 6003,
Prop_HasCameraComponent_Bool = 6004,
Prop_HasDriverDirectModeComponent_Bool = 6005,
Prop_HasVirtualDisplayComponent_Bool = 6006,
// Vendors are free to expose private debug data in this reserved region
Prop_VendorSpecific_Reserved_Start = 10000,
Prop_VendorSpecific_Reserved_End = 10999,
}
在vr::ITrackedDeviceServerDriver设备驱动中使用如下:
virtual EVRInitError Activate( vr::TrackedDeviceIndex_t unObjectId )
{
m_unObjectId = unObjectId;
m_ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer( m_unObjectId );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, Prop_ModelNumber_String, m_sModelNumber.c_str() );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, Prop_RenderModelName_String, m_sModelNumber.c_str() );
vr::VRProperties()->SetFloatProperty( m_ulPropertyContainer, Prop_UserIpdMeters_Float, m_flIPD );
vr::VRProperties()->SetFloatProperty( m_ulPropertyContainer, Prop_UserHeadToEyeDepthMeters_Float, 0.f );
vr::VRProperties()->SetFloatProperty( m_ulPropertyContainer, Prop_DisplayFrequency_Float, m_flDisplayFrequency );
vr::VRProperties()->SetFloatProperty( m_ulPropertyContainer, Prop_SecondsFromVsyncToPhotons_Float, m_flSecondsFromVsyncToPhotons );
// return a constant that's not 0 (invalid) or 1 (reserved for Oculus)
vr::VRProperties()->SetUint64Property( m_ulPropertyContainer, Prop_CurrentUniverseId_Uint64, 2 );
// avoid "not fullscreen" warnings from vrmonitor
vr::VRProperties()->SetBoolProperty( m_ulPropertyContainer, Prop_IsOnDesktop_Bool, false );
// Icons can be configured in code or automatically configured by an external file "drivername\resources\driver.vrresources".
// Icon properties NOT configured in code (post Activate) are then auto-configured by the optional presence of a driver's "drivername\resources\driver.vrresources".
// In this manner a driver can configure their icons in a flexible data driven fashion by using an external file.
//
// The structure of the driver.vrresources file allows a driver to specialize their icons based on their HW.
// Keys matching the value in "Prop_ModelNumber_String" are considered first, since the driver may have model specific icons.
// An absence of a matching "Prop_ModelNumber_String" then considers the ETrackedDeviceClass ("HMD", "Controller", "GenericTracker", "TrackingReference")
// since the driver may have specialized icons based on those device class names.
//
// An absence of either then falls back to the "system.vrresources" where generic device class icons are then supplied.
//
// Please refer to "bin\drivers\sample\resources\driver.vrresources" which contains this sample configuration.
//
// "Alias" is a reserved key and specifies chaining to another json block.
//
// In this sample configuration file (overly complex FOR EXAMPLE PURPOSES ONLY)....
//
// "Model-v2.0" chains through the alias to "Model-v1.0" which chains through the alias to "Model-v Defaults".
//
// Keys NOT found in "Model-v2.0" would then chase through the "Alias" to be resolved in "Model-v1.0" and either resolve their or continue through the alias.
// Thus "Prop_NamedIconPathDeviceAlertLow_String" in each model's block represent a specialization specific for that "model".
// Keys in "Model-v Defaults" are an example of mapping to the same states, and here all map to "Prop_NamedIconPathDeviceOff_String".
//
bool bSetupIconUsingExternalResourceFile = true;
if ( !bSetupIconUsingExternalResourceFile )
{
// Setup properties directly in code.
// Path values are of the form {drivername}\icons\some_icon_filename.png
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceOff_String, "{sample}/icons/headset_sample_status_off.png" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceSearching_String, "{sample}/icons/headset_sample_status_searching.gif" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{sample}/icons/headset_sample_status_searching_alert.gif" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceReady_String, "{sample}/icons/headset_sample_status_ready.png" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{sample}/icons/headset_sample_status_ready_alert.png" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceNotReady_String, "{sample}/icons/headset_sample_status_error.png" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceStandby_String, "{sample}/icons/headset_sample_status_standby.png" );
vr::VRProperties()->SetStringProperty( m_ulPropertyContainer, vr::Prop_NamedIconPathDeviceAlertLow_String, "{sample}/icons/headset_sample_status_ready_low.png" );
}
return VRInitError_None;
}
2.自定义类:DeviceHolder类:
1>.addAndActivateDevice():代码如下:
IdReturnValue
addAndActivateDevice(vr::ITrackedDeviceServerDriver *dev) {
/// check to make sure it's not null and not already in there
if (!dev) {
return IdReturnValue::makeError();
}
auto existing = findDevice(dev);//查找
if (existing) {
/// @todo do we return an error or the existing location? This
/// returns the existing location after re-activating.
dev->Activate(existing.value);
return existing;
}
auto newId = static_cast<std::uint32_t>(devices_.size());
devices_.push_back(dev);
dev->Activate(newId);
return IdReturnValue::makeValue(newId);
}
@brief:查找容器devices_中是否存在dev设备驱动,若存在,则active它并返回它的id号;反之,则添加进devices_并返回id号。
2>.addAndActivateDeviceAt():代码如下:
/// Add and activate a device at a reserved id.
IdReturnValue
addAndActivateDeviceAt(vr::ITrackedDeviceServerDriver *dev,
std::uint32_t idx) {
/// check to make sure it's not null and not already in there
if (!dev) {
return IdReturnValue::makeError();
}
auto existing = findDevice(dev);
if (existing && existing.value != idx) {
// if we already found it in there and it's not at the desired
// index...
return IdReturnValue(existing.value, false);
}
if (existing) {
// well, in this case, we might need to just activate it again.
dev->Activate(idx);
return IdReturnValue::makeValue(idx);
}
if (!(idx < devices_.size())) {
// OK, we need to reserve more room.
reserveIds(idx + 1);
}
if (devices_[idx]) {
// there's already somebody else there...
return IdReturnValue::makeError();
}
/// Finally, if we made it through that, it's our turn.
devices_[idx] = dev;
dev->Activate(idx);
return IdReturnValue::makeValue(idx);
}
@brief:激活指定idx的设备。
InterfaceVersionSupport类:
@brief:辅助类,用于管理接口版本号
1>.InterfaceVersionSupport() : supportedInterfaces_(populate()) {}
@brief:接收populate的返回值
2>.populate:
/// Function to create and populate the supported interface
/// container, so the object's data member may be const.
static Container populate() {
Container ret;
// Populate a vector with all the supported interfaces.
for_each_const_string_array(
vr::k_InterfaceVersions,
[&](const char *str) { ret.emplace_back(str); });
// so we can use binary_search
std::sort(ret.begin(), ret.end());
return ret;
}
@brief:将openvr_driver.h中声明的vr::k_InterfaceVersions传入lambda函数,加入vector。之后按序列从小到大排列,
并返回赋给supportedInterfaces_
namespace vr
{
static const char * const k_InterfaceVersions[] =
{
IVRSettings_Version,
ITrackedDeviceServerDriver_Version,
IVRDisplayComponent_Version,
IVRDriverDirectModeComponent_Version,
IVRControllerComponent_Version,
IVRCameraComponent_Version,
IServerTrackedDeviceProvider_Version,
IVRWatchdogProvider_Version,
IVRVirtualDisplay_Version,
IVRDriverManager_Version,
IVRResources_Version,
nullptr
};
...
}
2>.inline bool isInterfaceNameWeCareAbout(std::string const &interfaceName)
@brief:用于检查传入的接口类是否为该驱动所care的接口类
/// A list of just the interface names we actually use.
static const auto interfaceNamesWeCareAbout = {
"ITrackedDeviceServerDriver", "IVRDisplayComponent",
"IVRControllerComponent", //< @todo do we actually use/cast to
// this interface?
"IServerTrackedDeviceProvider", "IVRWatchdogProvider"};
} // namespace detail
inline bool isInterfaceNameWeCareAbout(std::string const &interfaceName) {
return std::find(detail::interfaceNamesWeCareAbout.begin(),
detail::interfaceNamesWeCareAbout.end(),
interfaceName) !=
detail::interfaceNamesWeCareAbout.end();
}
DriverLoader:
@brief:用来加载一个steamVR driver dll驱动,获取主入口点及其函数调用,这个类是驱动运行的必要条件。
1>. /// Factory function to make a driver loader.
static std::unique_ptr<DriverLoader>
make(std::string const &driverRoot, std::string const &driverFile);
@brief:工厂方法生成一个驱动加载对象。
loader_ = DriverLoader::make(locations_.driverRoot,
locations_.driverFile);
驱动根目录(driverRoot)如:Steam\steamapps\common\SteamVR\drivers\lighthouse\bin\win64
驱动文件(driverFile)如:Steam\steamapps\common\SteamVR\drivers\lighthouse\bin\win64\driver_lighthouse.dll
2>.template <typename InterfaceType>
ReturnValue<InterfaceType *, int> getInterface()
@brief:获取模板参数InterfaceType指定的接口类。最初由getProvider<vr::IServerTrackedDeviceProvider>调用。
3>.std::string const &getDriverRoot()
@brief:获取根目录,如:
Steam\steamapps\common\SteamVR\drivers\lighthouse\bin\win64
ChaperoneData:
@brief:一些已知的追踪器空间范围的数据。比如空间界限,游戏空间等。来自于chaperone_info,位于<Steam\config\chaperone_info.vrchap>
1>.struct UniverseData {
CalibrationType type = CalibrationType::Standing;
std::array<double, 3> translation;
double yaw = 0.;
};
@brief:空间数据。类型:站立。数据:位置信息以及偏航角。
2>.ChaperoneData::ChaperoneData(std::string const &steamConfigDir)
: impl_(new Impl), configDir_(steamConfigDir)
@brief:从chaperone_info.vrchap文件中获取字段填充成员变量impl_,结构代码如下:
struct ChaperoneData::Impl {
Json::Value chaperoneInfo;
UniverseDataMap universes;
UniverseBaseSerials baseSerials;
};
3>.UniverseData getDataForUniverse(UniverseId universe) const
@brief:获取外设数据。
ChaperoneData::UniverseData
ChaperoneData::getDataForUniverse(UniverseId universe) const {
auto it = impl_->universes.find(universe);
if (it == end(impl_->universes)) {
return UniverseData();
}
return it->second; // ChaperoneData::UniverseData类型
}
@brief:通过universe号获取对应的键值
using UniverseDataMap =
std::map<std::uint64_t, ChaperoneData::UniverseData>;
4>. UniverseId guessUniverseIdFromBaseStations(BaseStationSerials const &bases);
using BaseStationSerials = std::vector<std::string>;
ChaperoneData::UniverseId ChaperoneData::guessUniverseIdFromBaseStations(
BaseStationSerials const &bases) {
auto providedSize = bases.size();
UniverseId ret = 0;
using UniverseRank = std::pair<float, UniverseId>;
/// Compare function for heap.
auto compare = [](UniverseRank const &a, UniverseRank const &b) {
return a.first < b.first;
};
/// Will contain heap of potential universes and their value (a fraction
/// of their base stations and provided base stations that were included
/// in the base station list provided to the function)
std::vector<UniverseRank> potentialUniverses;
auto push = [&](float value, UniverseId id) {
potentialUniverses.emplace_back(value, id);
std::push_heap(begin(potentialUniverses), end(potentialUniverses),
compare);
};
for (auto &univBaseSerial : impl_->baseSerials) {
auto const &baseSerials = univBaseSerial.second;
std::size_t hits = 0;
auto b = begin(baseSerials);
auto e = end(baseSerials);
/// Count the number of entries that we were given that are also in
/// this universe's list.
auto found = std::count_if(begin(bases), end(bases),
[&](std::string const &needle) {
return std::find(b, e, needle) != e;
});
if (found > 0) {
/// This is meant to combine the influence of "found" in both
/// providedSize and universe size, and the +1 in the
/// denominator is to avoid division by zero.
auto weight = 2.f * static_cast<float>(found) /
(baseSerials.size() + providedSize + 1);
#if 0
std::cout << "Guessing produced weight of " << weight << " for "
<< univBaseSerial.first << std::endl;
#endif
push(weight, univBaseSerial.first);
}
}
if (!potentialUniverses.empty()) {
/// it's a heap, so the best one is always on top.
return potentialUniverses.front().second;
}
return ret;
}
@brief:由基站预测id。
com_osvr_Vive.cpp为OSVR插件一般结构:
OSVR_ReturnCode update(){
if (m_startedInSuccess) {
return OSVR_RETURN_SUCCESS;
}
if (!m_shouldAttemptDetection) {
/// We said we shouldn't and wouldn't try again.
return OSVR_RETURN_FAILURE;
}
/// Hand the Vive object off to the OSVR driver.
auto startResult = finishViveStartup();// 进入
if (startResult) {
/// and it started up the rest of the way just fine!
/// We'll keep the driver around!
m_logger->info("Vive driver finished startup successfully!");
m_startedInSuccess = true;
return OSVR_RETURN_SUCCESS;
}
m_logger->error("Vive driver startup failed.");
if (m_shouldAttemptDetection) {
m_logger->info(" Unloading to perhaps try again later.");
}
return OSVR_RETURN_FAILURE;
}
finishViveStartup()中调用ViveDriverHost中的start方法。
ViveDriverHost类 :继承自vr::IVRServerDriverHost:
ViveDriverHost::StartResult
ViveDriverHost::start(OSVR_PluginRegContext ctx,
osvr::vive::DriverWrapper &&inVive)
{
if (!inVive) {
m_logger->error(
"Called ViveDriverHost::start() with an invalid vive object!");
return StartResult::TemporaryFailure;
}
/// Take ownership of the Vive.
m_vive.reset(new osvr::vive::DriverWrapper(std::move(inVive)));
/// define the lambda to handle the ServerDriverHost::TrackedDeviceAdded
auto handleNewDevice = [&](const char *serialNum,
ETrackedDeviceClass eDeviceClass,
ITrackedDeviceServerDriver *pDriver) {
auto dev = pDriver;
if (!dev) {
m_logger->info("null input device");
return false;
}
auto ret = activateDevice(dev, eDeviceClass);
if (!ret) {
m_logger->error("Device with serial number ")
<< serialNum << " couldn't be added to the devices vector.";
return false;
}
NewDeviceReport out{std::string{serialNum}, ret.value};
{
std::lock_guard<std::mutex> lock(m_mutex);
m_newDevices.submitNew(std::move(out), lock);
}
return true;
};
m_vive->driverHost().onTrackedDeviceAdded = handleNewDevice;
/// Finish setting up the Vive.
try {
if (!m_vive->startServerDeviceProvider()) {
m_logger->error("Could not start the server device provider in "
"the Vive driver. Exiting.");
return StartResult::TemporaryFailure;
}
} catch (CouldNotGetInterface &e) {
m_logger->error("Caught exception trying to start Vive server "
"device provider: ")
<< e.what();
m_logger->error("SteamVR interface version may have changed, may "
"need to be rebuilt against an updated header or "
"use an older SteamVR version. Exiting.");
return StartResult::PermanentFailure;
}
/// Check for interface compatibility
if (DriverWrapper::InterfaceVersionStatus::InterfaceMismatch ==
m_vive->checkServerDeviceProviderInterfaces()) {
m_logger->error(
"SteamVR lighthouse driver requires unavailable/unsupported "
"SteamVR lighthouse driver requires unavailable/unsupported "
"interface versions - either too old or too new for this "
"build. Specifically, the following critical mismaches: ");
for (auto iface : m_vive->getUnsupportedRequestedInterfaces()) {
if (isInterfaceNameWeCareAbout(
detail::getInterfaceName(iface))) {
auto supported =
m_vive->getSupportedInterfaceVersions()
.findSupportedVersionOfInterface(iface);
m_logger->error(" - SteamVR lighthouse: ")
<< iface << "\t\t OSVR-Vive: " << supported;
}
}
m_logger->error("Cannot continue.\n");
return StartResult::PermanentFailure;
}
/// Power the system up.
m_vive->serverDevProvider().LeaveStandby();
/// Reserve ID 0 for the HMD
m_vive->devices().reserveIds(1);
/// Finish setting this up as an OSVR device.
/// Create the initialization options
OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);
osvrDeviceTrackerConfigure(opts, &m_tracker);
osvrDeviceAnalogConfigure(opts, &m_analog, NUM_ANALOGS);
osvrDeviceButtonConfigure(opts, &m_button, NUM_BUTTONS);
/// Because the callbacks may not come from the same thread that
/// calls RunFrame, we need to be careful to not send directly from
/// those callbacks. We can't use an Async device token because the
/// waits are too long and they goof up the SteamVR Lighthouse driver.
m_dev.initSync(ctx, "Vive", opts);
/// Send JSON descriptor
m_dev.sendJsonDescriptor(com_osvr_Vive_json);
/// Register update callback
m_dev.registerUpdateCallback(this);
return StartResult::Success;
}
1>.将参数DriverWrapper类对象inVive赋给成员变量m_vive
2>.将lambda函数指针handleNewDevice赋给onTrackedDeviceAdded(由m_vive控制的driverHost类):
lambda函数截获串号(类似hydra_controller1或hydra_controller2之类的字符串形式),设备类别(类似HMD PUCK等)以及
设备驱动。通过参数传入将指定idx的外设添加到容器std::vector<vr::ITrackedDeviceServerDriver *> devices_中
,并激活它,激活后返回它的idx值赋值给ret.value。之后,将serialNum与ret.value构造一个NewDeviceReport类,将该类存入
队列deque_。
3>.将lambda函数赋给 m_vive->driverHost().onTrackedDeviceAdded。将在ServerDeviceProvider启动后,在Init中被调用。
4>.startServerDeviceProvider:
初始化vr::IVRSettings,vr::IVRDriverLog,vr::IVRProperties类对象,并使用它们构建一个vr::DriverContext类对象,再通过
VR_INIT_SERVER_DRIVER_CONTEXT(context_)本质上是给COpenVRDriverContext类的m_pVRSettings,m_pVRDriverLog,m_pVRProperties
赋值。以供后续如TrackedDeviceAdded使用。
#define VR_INIT_SERVER_DRIVER_CONTEXT( pContext ) \
{ \
vr::EVRInitError eError = vr::InitServerDriverContext( pContext ); \
if( eError != vr::VRInitError_None ) \
return eError; \
}
在将环境准备好之后,serverDeviceProvider_ =
getProvider<vr::IServerTrackedDeviceProvider>(
std::move(loader_), context_);
传入模板参数vr::IServerTrackedDeviceProvider, 随后:
template <typename InterfaceType>
inline ProviderPtr<InterfaceType>
getProvider(std::unique_ptr<DriverLoader> &&loader,
vr::IVRDriverContext *context) {
static_assert(
InterfaceExpectedFromEntryPointTrait<InterfaceType>::value,
"Function only valid for those 'provider' interface types "
"expected to be provided by the driver entry point.");
return detail::getProviderImpl<InterfaceType>(
std::move(loader), context, [](SharedDriverLoader const &) {});
}
传入一个lambda函数指针(不捕获时才可转换为函数指针)。
template <typename InterfaceType, typename F>
inline ProviderPtr<InterfaceType>
getProviderImpl(std::unique_ptr<DriverLoader> &&loader,
vr::IVRDriverContext *context,
F &&driverLoaderFunctor) {
using return_type = ProviderPtr<InterfaceType>;
if (!loader) {
return return_type{};
}
/// Move into local pointer, so if something goes wrong, the driver
/// gets unloaded.
std::unique_ptr<DriverLoader> myLoader(std::move(loader));
auto rawPtr = myLoader->getInterfaceThrowing<InterfaceType>();
auto initResults = rawPtr->Init(context);
if (vr::VRInitError_None != initResults) {
/// Failed, reset the loader pointer to unload the driver.
std::cout << "Got error code " << initResults << std::endl;
myLoader.reset();
return return_type{};
}
/// OK, so this is the interface. Move the loader into a shared
/// pointer, make the loader responsible for cleanup of the
/// interface, and get the shared pointer of the interface pointer
/// returned.
SharedDriverLoader sharedLoader(std::move(myLoader));
sharedLoader->cleanupInterfaceOnDestruction(rawPtr);
/// Call the hook
(std::forward<F>(driverLoaderFunctor))(sharedLoader);
/// Create the return value: another shared pointer.
/// This is the so-called "aliasing" constructor - this pointer will
/// actually keep the DriverLoader alive and do nothing with the
/// lifetime of rawPtr (which is why the loader is responsible for
/// calling Cleanup, see above)
return_type ret(sharedLoader, rawPtr);
return ret;
}
(std::forward<F>(driverLoaderFunctor))(sharedLoader):钩子获取dll中的接口,与driver_lighthouse.dll衔接。
其中sharedLoader最初由loader_ = DriverLoader::make(locations_.driverRoot, locations_.driverFile);构建而成。
std::unique_ptr<DriverLoader>
DriverLoader::make(std::string const &driverRoot,
std::string const &driverFile) {
std::unique_ptr<DriverLoader> ret(
new DriverLoader(driverRoot, driverFile));
return ret;
}
返回一个DriverLoader对象。
DriverLoader::DriverLoader(std::string const &driverRoot,
std::string const &driverFile)
: impl_(new Impl),
logger_(osvr::util::log::make_logger("DriverLoader")) {
/// Set the PATH to include the driver directory so it can
/// find its deps.
SearchPathExtender extender(driverRoot);
f defined(OSVR_WINDOWS)
impl_->driver_ = LoadLibraryA(driverFile.c_str());<strong><span style="font-size:14px;">// 加载steam目录下的driver_lighthouse.dll文件</span></strong>
if (!impl_->driver_) {
reset();
throw CouldNotLoadDriverModule();
}
auto proc = GetProcAddress(impl_->driver_, ENTRY_POINT_FUNCTION_NAME);<strong><span style="font-size:14px;">// 获取dll文件中的HmdDriverFactory接口地址</span></strong>
if (!proc) {
reset();
throw CouldNotLoadEntryPoint();
}
factory_ = reinterpret_cast<DriverFactory>(proc);<strong><span style="font-size:14px;">// 强制转换为void *(*)(const char *, int *)类型</span></strong>
lif defined(OSVR_LINUX) || defined(OSVR_MACOSX)
impl_->driver_ = dlopen(driverFile.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!impl_->driver_) {
reset();
throw CouldNotLoadDriverModule(dlerror());
}
auto proc = dlsym(impl_->driver_, ENTRY_POINT_FUNCTION_NAME);
if (!proc) {
reset();
throw CouldNotLoadEntryPoint(dlerror());
}
factory_ = reinterpret_cast<DriverFactory>(proc);
ndif
}
static const auto ENTRY_POINT_FUNCTION_NAME = "HmdDriverFactory";
using DriverFactory = void *(*)(const char *, int *);
目的是存储一个地址到变量:factory_
在driver_lighthouse.dll中的vr::IServerTrackedDeviceProvider已提供接口如下:
CServerDriver g_ServerTrackedDeviceProvider;
HMD_DLL_EXPORT
void *HmdDriverFactory(const char *pInterfaceName, int *pReturnCode)
{
if (0 == strcmp(IServerTrackedDeviceProvider_Version, pInterfaceName)) {
return &g_ServerTrackedDeviceProvider;
}
if (pReturnCode) {
*pReturnCode = VRInitError_Init_InterfaceNotFound;
}
return NULL;
}
回到ViveDriverHost::start中,checkServerDeviceProviderInterfaces检测驱动需要的接口openvr_driver.h是否支持。
enum class InterfaceVersionStatus {
/// All mentioned interface version strings are handled/described by
/// the header we've built against.
AllInterfacesOK,// 驱动请求使用的所有接口类 openvr_driver.h中都支持
/// Not all mentioned interface version strings are
/// handled/described by the header we've built against, but the
/// interfaces that we use match.
AllUsedInterfacesOK, // 不是所有请求的接口类openvr_driver.h中都支持,但是所使用到的类都支持。
/// At least one of the interfaces that we use doesn't match the
/// version we built against.
InterfaceMismatch //至少一个所使用到的接口类在openvr_driver.h中不支持
};
static const char * const k_InterfaceVersions[] =
{
IVRSettings_Version,
ITrackedDeviceServerDriver_Version,
IVRDisplayComponent_Version,
IVRDriverDirectModeComponent_Version,
IVRControllerComponent_Version,
IVRCameraComponent_Version,
IServerTrackedDeviceProvider_Version,
IVRWatchdogProvider_Version,
IVRVirtualDisplay_Version,
nullptr
};
serverDevProvider().GetInterfaceVersions()返回的是以上openvr_driver.h中的接口类名,并比较是否所有接口类版本都支持。
若一个版本不支持,则将其入队,并将其与本驱动care的版本进行对比,若是必须要使用的但是又不被openvr_driver.h所支持。
则allUsedSupported=false.
/// A list of just the interface names we actually use.
static const auto interfaceNamesWeCareAbout = {
"ITrackedDeviceServerDriver", "IVRDisplayComponent",
"IVRControllerComponent", //< @todo do we actually use/cast to
// this interface?
"IServerTrackedDeviceProvider", "IVRWatchdogProvider"};
关于数据传输部分:
在driver_lighthouse.dll中通过RunFrame()中的vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_unObjectId, GetPose(), sizeof(DriverPose_t));
往vive中的以下接口发:
void ViveDriverHost::TrackedDevicePoseUpdated(uint32_t unWhichDevice,
const DriverPose_t &newPose,
uint32_t unPoseStructSize) {
submitTrackingReport(unWhichDevice, osvr::util::time::getNow(),
newPose);
}
void ViveDriverHost::submitTrackingReport(uint32_t unWhichDevice,
OSVR_TimeValue const &tv,
const DriverPose_t &newPose) {
TrackingReport out;
out.timestamp = tv;
out.sensor = unWhichDevice;
out.report = newPose;
{
std::lock_guard<std::mutex> lock(m_mutex);
m_trackingReports.submitNew(std::move(out), lock);
}
}
因此,整个数据过程为:
通过DriverLoader.cpp加载dll后,再通过m_vive->startServerDeviceProvider()获取dll实现的
vr::IServerTrackedDeviceProvider接口, update中通过调用m_vive->serverDevProvider().RunFrame()结构,通过submitNew往deque_中push,vive osvr端通过grabItems到vector_。最后在update中再通过accessWorkItems获取
vector_。最终通过convertAndSendTracker中的osvrDeviceTrackerSendPoseTimestamped往应用程序传数据。其中chaperone
配置文件中的数据在handleUniverseChange中被处理。
2017-6-27------------------------------------------------------------------------
无论是在直连或者扩展模式上,HTC Vive和它的控制器被OSVR所支持且可以在任何OSVR应用程序上使用,还支持畸变校正,全方位追踪以及输入支持。
通过OSVR,OSVR-Vive插件及其工具访问Vive硬件。
兼容性:
然后改驱动应该兼容所有的Vive家族设备,到目前为止,它只在HTC Vive和Vive PRE(非早期开发版本)上测试过。
OSVR-Vive驱动与为Vive服务的SteamVR驱动交互(driver_lighthouse),因此与SteamVR版本也有关联。
安装流程:
首先确保Vive在SteamVR上工作正常且已经安排了一个房间,最好是进行一个站立状态或房间级别的校准作为游戏空间。然后,下载
与OSVR服务相对应位数的插件二进制包。
在下载的解压包内有以下文件:
1>.一个插件文件。在Windows上,这是一个在bin/osvr-plugins-0目录下的.dll文件。将它放在OSVR server相同目录下。
2>.在bin目录内,一个ViveDisplayExtractor工具---拷贝到OSVR server的bin目录下。
3>.一个起始样例配置文件osvr_server_config(还有一些可以快速替换的预设显示描述符以及网格文件)
首先应该在不需要改变任何SteamVR设置的基础上,能够以直连模式在Vive头显上运行一个OSVR的应用程序。如果Vive在SteamVR上以
直联模式工作正常,但是在OSVR上不能以直连模式打开,则原因可能是在OSVR应用程序启动之前SteamVR没有退出(需要确保在
OSVR应用程序启动之前SteamVR已经退出)。在一些系统上,退出SteamVR之前,在SteamVR菜单内可能需要禁止直连模式。
一次安装:Vive需要一个客户显示描述以及网格畸变数据文件,使用ViveDisplayExtractor工具提取这些文件数据。确保ViveDisplayExtractor与
osvr_server一起运行,也就是Vive与PC连接,SteamVR已经退出,运行ViveDisplayExtractor。
如果成功,将会看到类似以下:
[DisplayExtractor] Writing distortion mesh data file:
C:/Users/Ryan/Desktop/OSVR/bin/displays/HTC_Vive_PRE_meshdata.json
[DisplayExtractor] Writing display descriptor file:
C:/Users/Ryan/Desktop/OSVR/bin/displays/HTC_Vive_PRE.json
[DisplayExtractor] Press enter to quit...
Note:结果配置包含一个到网格数据的绝对路径,因此如果改变了osvr server所在目录,只需要再次运行ViveDisplayExtractor。
以上就是所有的了!现在可以使用样例osvr_server_config.vive.sample.json 文件作为服务配置文件了(将它改为默认名: osvr_server_config.json
),可以在Vive上运行OSVR启动的应用程序了。
如果想要运行SteamVR APP,没问题:只要关闭OSVR Server并且任何在直连模式运行的OSVR Apps即可。