精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
服务方向
联系方式
锐英源承接关于本文的任何培训、开发和技术服务需求,欢迎联系QQ396806883,微信ryysoft。
有点像MVC架构,HMD数据是数据层,CSampleControllerDriver(用于控制器数据)是控制器, CServerDriver_Sample像逻辑层,这个MVC比喻只是让大家好记,并不是真是。 另外篇幅介绍了SteamVR的设备接口。
First thing first, I didn't want to upload this project, because of its complexity, coded in C++ (and I don't like this language), and this complexity involved the impossibility to find how to send finger and body tracking data to games (despite the documentation given by Valve on github). 首先,我不想上载此项目,因为它的C++编码的复杂性(而且我不喜欢这种语言),而且这种复杂性涉及无法找到如何将手指和身体跟踪数据发送到的项目。游戏(尽管Valve在github上提供了文档)。
But a project is still a project, so maybe it will be helpful for other programmers. 但是一个项目仍然是一个项目,因此也许对其他程序员会有所帮助。
This project is based on https://github.com/r57zone/OpenVR-driver-for-DIY. 该项目基于https://github.com/r57zone/OpenVR-driver-for-DIY。
Else for this project to be fully completed, it needs to have a communication bridge between VR device and PC, but since OpenVR has been coded in C++, my USB sniffer solution coded in C# won't work: https://www.codeproject.com/Articles/1244702/How-to-Communicate-with-its-USB-Devices-using-HID 要使该项目完全完成,还需要在VR设备和PC之间建立通信桥,但是由于OpenVR已使用C ++进行编码,所以我的C#编码的USB嗅探器解决方案将无法使用:https://www.codeproject .com / Articles / 1244702 /如何与使用HID的USB设备进行通信
Designing its own VR solution is a real need at this time, VR is not as far as different from a keyboard, a mouse, or whatever PC devices. 目前,设计自己的VR解决方案是真正需要,VR与键盘,鼠标或任何PC设备没有什么不同。
The problem is there, how many manufacturers we have for keyboard/mouse, a ton, but not for VR and it's a problem, VR has been popularized in 90s, but it's ended with a flop after 96s, too expensive, hardware issues, full VR immersion not reached. 问题就在那里,我们有多少家键盘/鼠标制造商,一吨,但没有VR,这是一个问题,VR在90年代就很流行了,但是到了96年代就以失败告终,太贵了,硬件问题,全部无法达到VR沉浸感。
And commercials repeat this past mistake since 2014 with Facebook, thinking VR technology is mature enough to be commercialized right now. 广告商自2014年以来在Facebook重复了过去的错误,认为VR技术已经足够成熟,可以立即商业化。
So here we have, a way to develop our own VR solution with OpenVR: 因此,在这里,我们有了一种使用OpenVR开发自己的VR解决方案的方法:
Why focus on SteamVR? It's because game developers have decided to begin the VR game development on it, not on other solutions. 为什么要专注于SteamVR?这是因为游戏开发人员决定开始在其上进行VR游戏开发,而不是在其他解决方案上进行。
The truth is also that other solutions don't offer the same dev kit like OpenXR or WMR, we can develop device driver and VR games with. 事实是,其他解决方案没有提供像OpenXR或WMR一样的开发套件,我们可以用它开发设备驱动程序和VR游戏。
First, before explaining how the code works, let's explain how to setup a hardware driver to make it work with SteamVR, it's pretty simple as you will see: 首先,在解释代码的工作方式之前,让我们解释如何设置硬件驱动程序以使其与SteamVR一起使用,这非常简单,如您所见:
It's that simple, yes. 就这么简单,是的。
Ok, let's begin the code explanation, since the code is in C++, I wouldn't be able to comment with precision, what Valve coded. 好的,让我们开始代码说明,因为代码是C ++,所以我无法精确地注释Valve编码的内容。
Here are three classes that are the minimum required to make a device driver: 以下是制作设备驱动程序所需的最低三个类:
And the entry of this driver is: 该驱动程序的条目是:
HMD_DLL_EXPORT void *HmdDriverFactory(const char* pInterfaceName, int32_t* pReturnCode)
{
static CServerDriver_Sample g_serverDriverNull;return &g_serverDriverNull;
}
Let's dig into this class: 让我们深入探讨该类:
class CServerDriver_Sample : public IServerTrackedDeviceProvider
{
public: CSampleDeviceDriver* m_pNullHmdLatest;
CSampleControllerDriver* m_pController;
CSampleControllerDriver* m_pController2; const char* const* GetInterfaceVersions() { return k_InterfaceVersions; }
bool ShouldBlockStandbyMode() { return false; }
void EnterStandby() {}
void LeaveStandby() {} EVRInitError Init(IVRDriverContext* pDriverContext)
{
VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext); m_pNullHmdLatest = new CSampleDeviceDriver();
VRServerDriverHost()->TrackedDeviceAdded(m_pNullHmdLatest->GetSerialNumber().c_str(),
TrackedDeviceClass_HMD, m_pNullHmdLatest); m_pController = new CSampleControllerDriver();
m_pController->SetControllerIndex(1);
VRServerDriverHost()->TrackedDeviceAdded(m_pController->GetSerialNumber().c_str(),
TrackedDeviceClass_Controller, m_pController); m_pController2 = new CSampleControllerDriver();
m_pController2->SetControllerIndex(2);
VRServerDriverHost()->TrackedDeviceAdded(m_pController2->GetSerialNumber().c_str(),
TrackedDeviceClass_Controller, m_pController2); return VRInitError_None;
} void Cleanup()
{
delete m_pNullHmdLatest;
m_pNullHmdLatest = NULL; delete m_pController;
m_pController = NULL; delete m_pController2;
m_pController2 = NULL;
} void RunFrame()
{
m_pNullHmdLatest->RunFrame();
m_pController->RunFrame();
m_pController2->RunFrame(); VREvent_t vrEvent;
while (VRServerDriverHost()->PollNextEvent(&vrEvent, sizeof(vrEvent)))
{
m_pController->ProcessEvent(vrEvent);
m_pController2->ProcessEvent(vrEvent);
}
}
};
Init() is the first function called and it initializes three classes, one for HDM data, one for one VR controller and another one for a 2nd VR controller. Init()是第一个调用的函数,它初始化三个类,一个用于HDM数据,一个用于一个VR控制器,另一个用于第二个VR控制器。
The VR controller we are talking about is HTC Vive Controllers, I haven't found a way to create Vive knuckles or a HTC Vive body tracker, unfortunately. 我们正在谈论的VR控制器是HTC Vive控制器,不幸的是,我还没有找到创建Vive指关节或HTC Vive身体追踪器的方法。
So, we are already limited to a device scheme, and that's not a good thing, but better than nothing, and all VR games control are based on this minimalist VR control scheme. 因此,我们已经仅限于一种设备方案,这不是一件好事,但总比没有好。所有VR游戏控制都基于这种简约的VR控制方案。
But it doesn't change, I am a bit upset, I hope you will find a way, because I am giving up, like the author of OpenVR-driver-for-DIY, VR is a cursed project ^^. 但这并没有改变,我有点沮丧,希望您能找到一种方法,因为我正在放弃,就像OpenVR-driver-for-DIY的作者一样,VR是一个被诅咒的项目^^。
So let's back in the code explanation, shall we. 现在,让我们回到代码说明中。
void Cleanup()
{
delete m_pNullHmdLatest;
m_pNullHmdLatest = NULL; delete m_pController;
m_pController = NULL; delete m_pController2;
m_pController2 = NULL;
} void RunFrame()
{
m_pNullHmdLatest->RunFrame();
m_pController->RunFrame();
m_pController2->RunFrame(); VREvent_t vrEvent;
while (VRServerDriverHost()->PollNextEvent(&vrEvent, sizeof(vrEvent)))
{
m_pController->ProcessEvent(vrEvent);
m_pController2->ProcessEvent(vrEvent);
}
}
Cleanup() looks like a free malloc function. Cleanup()看起来像一个免费的malloc函数。
RunFrame() is the core of this function, it's the 2nd entry gate of this driver, it calls HMD and controller tracking function. RunFrame()是此函数的核心,它是此驱动程序的第二个入口,它调用HMD和控制器跟踪功能。
For the next, I don't think it's useful to explain all that's going on with the two classes left, CSampleDeviceDriver and CSampleControllerDriver since we just want to know how we send VR device data in SteamVR games. 接下来,我认为解释剩下的两个类CSampleDeviceDriver和CSampleControllerDriver没什么用,因为我们只想知道我们如何在SteamVR游戏中发送VR设备数据。
To make it fast, HMD data control is in GetPose() in CSampleDeviceDriver class: 为了快速实现,HMD数据控件位于CSampleDeviceDriver类的GetPose()中:
// Headset Control
DriverPose_t GetPose()
{
DriverPose_t pose = { 0 };pose.poseIsValid = true;
pose.result = TrackingResult_Running_OK;
pose.deviceIsConnected = true; pose.qWorldFromDriverRotation.w = 1;
pose.qWorldFromDriverRotation.x = 0;
pose.qWorldFromDriverRotation.y = 0;
pose.qWorldFromDriverRotation.z = 0; pose.qDriverFromHeadRotation.w = 1;
pose.qDriverFromHeadRotation.x = 0;
pose.qDriverFromHeadRotation.y = 0;
pose.qDriverFromHeadRotation.z = 0; // Headset
// Move Forward/Backward
if ((GetAsyncKeyState(VK_UP) & 0x8000) != 0) HMD.Z -= StepPos;
if ((GetAsyncKeyState(VK_DOWN) & 0x8000) != 0) HMD.Z += StepPos; // Move Right/Left
if ((GetAsyncKeyState(VK_LEFT) & 0x8000) != 0) HMD.X -= StepPos;
if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) != 0) HMD.X += StepPos; // Move Up/Down
if ((GetAsyncKeyState(VK_NUMPAD0) & 0x8000) != 0) HMD.Y += StepPos;
if ((GetAsyncKeyState(VK_DECIMAL) & 0x8000) != 0) HMD.Y -= StepPos; // Rotate Right/Left
if ((GetAsyncKeyState(VK_NUMPAD1) & 0x8000) != 0) HMD.Pitch += StepRot;
if ((GetAsyncKeyState(VK_NUMPAD3) & 0x8000) != 0) HMD.Pitch -= StepRot; // Rotate Up/Down
if ((GetAsyncKeyState(VK_NUMPAD5) & 0x8000) != 0) HMD.Roll += StepRot;
if ((GetAsyncKeyState(VK_NUMPAD2) & 0x8000) != 0) HMD.Roll -= StepRot; // Roll Left/Right
if ((GetAsyncKeyState(VK_NUMPAD4) & 0x8000) != 0) HMD.Yaw += StepRot;
if ((GetAsyncKeyState(VK_NUMPAD6) & 0x8000) != 0) HMD.Yaw -= StepRot; HMD.Yaw = OffsetYPR(HMD.Yaw);
HMD.Pitch = OffsetYPR(HMD.Pitch);
HMD.Roll = OffsetYPR(HMD.Roll); //Convert yaw, pitch, roll to quaternion
double t0 = cos(DegToRad(HMD.Yaw));
double t1 = sin(DegToRad(HMD.Yaw));
double t2 = cos(DegToRad(HMD.Roll));
double t3 = sin(DegToRad(HMD.Roll));
double t4 = cos(DegToRad(HMD.Pitch));
double t5 = sin(DegToRad(HMD.Pitch)); //Set head tracking rotation
pose.qRotation.w = t0 * t2 * t4 + t1 * t3 * t5;
pose.qRotation.x = t0 * t3 * t4 - t1 * t2 * t5;
pose.qRotation.y = t0 * t2 * t5 + t1 * t3 * t4;
pose.qRotation.z = t1 * t2 * t4 - t0 * t3 * t5; pose.vecPosition[0] = HMD.X;
pose.vecPosition[1] = HMD.Y;
pose.vecPosition[2] = HMD.Z; return pose;
}
pose is the structure that is in charge of holding HMD data and the HMD structure is the one I created to keep a trace during the driver execution: 姿势是负责保存HMD数据的结构,而HMD结构是我在驱动程序执行期间创建的用于跟踪的结构:
typedef struct _HMDData
{
double X, Y, Z;
double Yaw, Pitch, Roll;
} THMD, *PHMD;
And controller data is located in GetPose() in CSampleControllerDriver class: 控制器数据位于CSampleControllerDriver类的GetPose()中:
// Controller Control
DriverPose_t GetPose()
{
DriverPose_t pose = { 0 };pose.poseIsValid = true;
pose.result = TrackingResult_Running_OK;
pose.deviceIsConnected = true; pose.qWorldFromDriverRotation.w = 1;
pose.qWorldFromDriverRotation.x = 0;
pose.qWorldFromDriverRotation.y = 0;
pose.qWorldFromDriverRotation.z = 0; pose.qDriverFromHeadRotation.w = 1;
pose.qDriverFromHeadRotation.x = 0;
pose.qDriverFromHeadRotation.y = 0;
pose.qDriverFromHeadRotation.z = 0; if (ControllerIndex == 1)
{
// FirstController
// Move Forward/Backward
if ((GetAsyncKeyState('W') & 0x8000) != 0)
FirstController.Z -= StepCtrlPos;
if ((GetAsyncKeyState(' ') & 0x8000) != 0)
FirstController.Z += StepCtrlPos; // Move Right/Left
if ((GetAsyncKeyState('A') & 0x8000) != 0)
FirstController.X -= StepCtrlPos;
if ((GetAsyncKeyState('D') & 0x8000) != 0)
FirstController.X += StepCtrlPos; // Move Up/Down
if ((GetAsyncKeyState('Q') & 0x8000) != 0)
FirstController.Y += StepCtrlPos;
if ((GetAsyncKeyState('E') & 0x8000) != 0)
FirstController.Y -= StepCtrlPos; // Rotate Up/Down
if ((GetAsyncKeyState('I') & 0x8000) != 0) FirstController.Yaw += StepRot;
if ((GetAsyncKeyState('K') & 0x8000) != 0) FirstController.Yaw -= StepRot; // Rotate Right/Left
if ((GetAsyncKeyState('J') & 0x8000) != 0) FirstController.Roll += StepRot;
if ((GetAsyncKeyState('L') & 0x8000) != 0) FirstController.Roll -= StepRot; // Roll Right/Left
if ((GetAsyncKeyState('U') & 0x8000) != 0) FirstController.Pitch += StepRot;
if ((GetAsyncKeyState('O') & 0x8000) != 0) FirstController.Pitch -= StepRot; FirstController.Yaw = OffsetYPR(FirstController.Yaw);
FirstController.Pitch = OffsetYPR(FirstController.Pitch);
FirstController.Roll = OffsetYPR(FirstController.Roll); //Convert yaw, pitch, roll to quaternion
double ct0 = cos(DegToRad(FirstController.Yaw));
double ct1 = sin(DegToRad(FirstController.Yaw));
double ct2 = cos(DegToRad(FirstController.Roll));
double ct3 = sin(DegToRad(FirstController.Roll));
double ct4 = cos(DegToRad(FirstController.Pitch));
double ct5 = sin(DegToRad(FirstController.Pitch)); pose.qRotation.w = ct0 * ct2 * ct4 + ct1 * ct3 * ct5;
pose.qRotation.x = ct0 * ct3 * ct4 - ct1 * ct2 * ct5;
pose.qRotation.y = ct0 * ct2 * ct5 + ct1 * ct3 * ct4;
pose.qRotation.z = ct1 * ct2 * ct4 - ct0 * ct3 * ct5; pose.vecPosition[0] = FirstController.X - 0.2;
pose.vecPosition[1] = FirstController.Y;
pose.vecPosition[2] = FirstController.Z - 0.5;
}
else
{
// SecondController
// Move Forward/Backward
if ((GetAsyncKeyState('T') & 0x8000) != 0)
SecondController.Z -= StepCtrlPos;
if ((GetAsyncKeyState('G') & 0x8000) != 0)
SecondController.Z += StepCtrlPos; // Move Right/Left
if ((GetAsyncKeyState('F') & 0x8000) != 0)
SecondController.X -= StepCtrlPos;
if ((GetAsyncKeyState('H') & 0x8000) != 0)
SecondController.X += StepCtrlPos; // Move Up/Down
if ((GetAsyncKeyState('R') & 0x8000) != 0)
SecondController.Y += StepCtrlPos;
if ((GetAsyncKeyState('Q') & 0x8000) != 0)
SecondController.Y -= StepCtrlPos; // Rotate Up/Down
if ((GetAsyncKeyState('I') & 0x8000) != 0) SecondController.Yaw += StepRot;
if ((GetAsyncKeyState('K') & 0x8000) != 0) SecondController.Yaw -= StepRot; // Rotate Right/Left
if ((GetAsyncKeyState('J') & 0x8000) != 0) SecondController.Roll += StepRot;
if ((GetAsyncKeyState('L') & 0x8000) != 0) SecondController.Roll -= StepRot; // Roll Right/Left
if ((GetAsyncKeyState('U') & 0x8000) != 0) SecondController.Pitch += StepRot;
if ((GetAsyncKeyState('O') & 0x8000) != 0) SecondController.Pitch -= StepRot; SecondController.Yaw = OffsetYPR(SecondController.Yaw);
SecondController.Pitch = OffsetYPR(SecondController.Pitch);
SecondController.Roll = OffsetYPR(SecondController.Roll); //Convert yaw, pitch, roll to quaternion
double ct0 = cos(DegToRad(SecondController.Yaw));
double ct1 = sin(DegToRad(SecondController.Yaw));
double ct2 = cos(DegToRad(SecondController.Roll));
double ct3 = sin(DegToRad(SecondController.Roll));
double ct4 = cos(DegToRad(SecondController.Pitch));
double ct5 = sin(DegToRad(SecondController.Pitch)); pose.qRotation.w = ct0 * ct2 * ct4 + ct1 * ct3 * ct5;
pose.qRotation.x = ct0 * ct3 * ct4 - ct1 * ct2 * ct5;
pose.qRotation.y = ct0 * ct2 * ct5 + ct1 * ct3 * ct4;
pose.qRotation.z = ct1 * ct2 * ct4 - ct0 * ct3 * ct5; pose.vecPosition[0] = SecondController.X + 0.2;
pose.vecPosition[1] = SecondController.Y;
pose.vecPosition[2] = SecondController.Z - 0.5;
} return pose;
}
Same as above, pose is the function that holds device data, controller data for this one. 与以上相同,pose是保存设备数据,控制器数据的功能。
Else, there is another important function for the controllers, it's RunFrame() that holds controller button state: 另外,控制器还有另一个重要功能,它是保持控制器按钮状态的RunFrame():
void RunFrame()
{
// Your driver would read whatever hardware state is
// associated with its input components and pass that
// in to UpdateBooleanComponent. This could happen in RunFrame or
// on a thread of your own that's reading USB
// state. There's no need to update input state unless it changes,
// but it doesn't do any harm to do so.if (ControllerIndex == 1)
{
VRDriverInput()->UpdateBooleanComponent(HButtons[0],
(0x8000 & GetAsyncKeyState(0x39)) != 0, 0); //Application Menu
VRDriverInput()->UpdateBooleanComponent(HButtons[1],
(0x8000 & GetAsyncKeyState(0x30)) != 0, 0); //Grip
VRDriverInput()->UpdateBooleanComponent(HButtons[2],
(0x8000 & GetAsyncKeyState(0xDB)) != 0, 0); //System
VRDriverInput()->UpdateBooleanComponent(HButtons[3],
(0x8000 & GetAsyncKeyState(0xDD)) != 0, 0); //TrackpadVRDriverInput()->UpdateScalarComponent(HAnalog[0], 0.0, 0); //Trackpad x
VRDriverInput()->UpdateScalarComponent(HAnalog[1], 0.0, 0); //Trackpad yif ((GetAsyncKeyState('W') & 0x8000) != 0)
VRDriverInput()->UpdateScalarComponent(HAnalog[0], 1.0, 0);if ((GetAsyncKeyState('X') & 0x8000) != 0)
VRDriverInput()->UpdateScalarComponent(HAnalog[1], 1.0, 0);if ((GetAsyncKeyState('C') & 0x8000) != 0) //Trigger
{
VRDriverInput()->UpdateScalarComponent(HAnalog[2], 1.0, 0);
}
else
{
VRDriverInput()->UpdateScalarComponent(HAnalog[2], 0.0, 0);
}
}
else
{
VRDriverInput()->UpdateBooleanComponent(HButtons[0],
(0x8000 & GetAsyncKeyState(VK_F1)) != 0, 0); //Application Menu
VRDriverInput()->UpdateBooleanComponent(HButtons[1],
(0x8000 & GetAsyncKeyState(VK_F2)) != 0, 0); //Grip
VRDriverInput()->UpdateBooleanComponent(HButtons[2],
(0x8000 & GetAsyncKeyState(VK_F3)) != 0, 0); //System
VRDriverInput()->UpdateBooleanComponent(HButtons[3],
(0x8000 & GetAsyncKeyState(VK_F4)) != 0, 0); //Trackpad VRDriverInput()->UpdateScalarComponent(HAnalog[0], 0.0, 0); //Trackpad x
VRDriverInput()->UpdateScalarComponent(HAnalog[1], 0.0, 0); //Trackpad y if ((GetAsyncKeyState('V') & 0x8000) != 0) //Trigger
{
VRDriverInput()->UpdateScalarComponent(HAnalog[2], 1.0, 0);
}
else
{
VRDriverInput()->UpdateScalarComponent(HAnalog[2], 0.0, 0);
}
} if (m_unObjectId != k_unTrackedDeviceIndexInvalid)
{
VRServerDriverHost()->TrackedDevicePoseUpdated
(m_unObjectId, GetPose(), sizeof(DriverPose_t));
}
Now, this isn't a pose structure that holds the data, it's VRDriverInput()-> 现在,这不是保存数据的姿势结构,它是VRDriverInput()-\u003e
We have reached the end of the main code explanation, now let's talk about the steamvr.vrsettings file, it contains all the VR settings concerning the HMD display. 我们已经到了主要代码说明的结尾,现在让我们来谈谈steamvr.vrsettings文件,其中包含有关HMD显示的所有VR设置。
{
"CustomHMD" :
{
"ipd" : 0.065,
"windowX" : 0,
"windowY" : 0,
"windowWidth" : 1920,
"windowHeight" : 1080,
"renderWidth" : 1920,
"renderHeight" : 1080,
"secondsFromVsyncToPhotons" : 0,
"displayFrequency" : 144,
"DistortionK1" : 0,
"DistortionK2" : 0,
"ZoomWidth" : 0.99,
"ZoomHeight" : 0.99,
"DistanceBetweenEyes" : 0,
"ScreenOffsetX" : 0,
"enable" : true,
"DebugMode" : true
},
"steamvr" :
{
"forcedDriver" : "CustomHMD",
"supersampleScale" : 1
}
}
And all those data, are handled in the driver code described above, like for the IPD as example: 所有这些数据都在上述驱动程序代码中处理,例如对于IPD:
void GetEyeOutputViewport(EVREye eEye, uint32_t* pnX, uint32_t* pnY,
uint32_t* pnWidth, uint32_t* pnHeight)
{
*pnY = m_nScreenOffsetX;
*pnWidth = m_nWindowWidth / 2;
*pnHeight = m_nWindowHeight; if (eEye == Eye_Left)
{
*pnX = m_nDistanceBetweenEyes;
}
else
{
*pnX = (m_nWindowWidth / 2) - m_nDistanceBetweenEyes;
}
}
Ok, now, once you have built the project, you need to run Install_VRDriver.bat located in the zip. 好的,现在,一旦构建了项目,就需要运行位于zip文件中的Install_VRDriver.bat。
@echo offIF EXIST "C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\CustomHMD\bin\win32"
GOTO SKIP1
mkdir "C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\CustomHMD\bin\win32"
:SKIP1 IF EXIST "C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\CustomHMD\bin\win64"
GOTO SKIP2
mkdir "C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\CustomHMD\bin\win64"
:SKIP2 set check = 0 echo ********************
xcopy /y /q "%cd%\steamvr.vrsettings"
"C:\Program Files (x86)\Steam\config"
if errorlevel 0 set /a check += 1 xcopy /y /q "%cd%\Release\x86\driver_CustomHMD.dll"
"C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\CustomHMD\bin\win32"
if errorlevel 0 set /a check += 1 xcopy /y /q "%cd%\Release\x64\driver_CustomHMD.dll"
"C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\CustomHMD\bin\win64"
if errorlevel 0 set /a check += 1 echo ********************
echo %check%/3 Files copied
echo ******************** pause
It will automatically install the drivers that you have built. 它将自动安装您已构建的驱动程序。
Then, go to Steam and run SteamVR, you're good to go to develop your own VR solution. 然后,转到Steam并运行SteamVR,您最好去开发自己的VR解决方案。
This is for the software part, now if you want to develop the hardware part, I suggest you choose Arduino to send data from sensor connected on Arduino board, through Serial or HID protocol to the PC and this driver. 这是用于软件部分的,现在,如果您要开发硬件部分,建议您选择Arduino,以通过Serial或HID协议从Arduino板上连接的传感器向PC和此驱动程序发送数据。