· 6 years ago · Nov 19, 2019, 02:48 PM
1//============ Copyright (c) Valve Corporation, All rights reserved. ============
2
3#include <vector>
4#include <thread>
5#include <chrono>
6#include <string>
7#include <thread>
8#include <atomic>
9
10#include <stdint.h>
11#include <windows.h>
12
13#include "openvr_driver.h"
14
15using namespace vr;
16using namespace std;
17
18#define GRIP_BTN 0x0001
19#define THUMB_BTN 0x0002
20#define A_BTN 0x0004
21#define B_BTN 0x0008
22#define MENU_BTN 0x0010
23#define SYS_BTN 0x0020
24
25#define StepCtrlPos 0.005;
26#define StepPos 0.0033;
27#define StepRot 0.4 * 0.5;
28
29typedef struct _HMDData
30{
31 double X, Y, Z;
32 double Yaw, Pitch, Roll;
33} THMD, *PHMD;
34
35typedef struct _Controller
36{
37 double X, Y, Z;
38 double Yaw, Pitch, Roll;
39 unsigned short Buttons;
40 float Trigger;
41 float AxisX, AxisY, AxisZ;
42} TController, *PController;
43
44THMD HMD;
45TController FirstController, SecondController;
46
47class CSampleDeviceDriver : public ITrackedDeviceServerDriver, public IVRDisplayComponent
48{
49 public:
50
51 // keys for use with the settings API
52 const char *k_pch_Sample_Section = "CustomHMD";
53 const char *k_pch_Sample_SerialNumber_String = "serialNumber";
54 const char *k_pch_Sample_ModelNumber_String = "modelNumber";
55 const char *k_pch_SteamVR_IPD_Float = "ipd";
56 const char *k_pch_Sample_WindowX_Int32 = "windowX";
57 const char *k_pch_Sample_WindowY_Int32 = "windowY";
58 const char *k_pch_Sample_WindowWidth_Int32 = "windowWidth";
59 const char *k_pch_Sample_WindowHeight_Int32 = "windowHeight";
60 const char *k_pch_Sample_RenderWidth_Int32 = "renderWidth";
61 const char *k_pch_Sample_RenderHeight_Int32 = "renderHeight";
62 const char *k_pch_Sample_SecondsFromVsyncToPhotons_Float = "secondsFromVsyncToPhotons";
63 const char *k_pch_Sample_DisplayFrequency_Float = "displayFrequency";
64 const char *k_pch_Sample_DistortionK1_Float = "DistortionK1";
65 const char *k_pch_Sample_DistortionK2_Float = "DistortionK2";
66 const char *k_pch_Sample_ZoomWidth_Float = "ZoomWidth";
67 const char *k_pch_Sample_ZoomHeight_Float = "ZoomHeight";
68 const char *k_pch_Sample_DistanceBetweenEyes_Int32 = "DistanceBetweenEyes";
69 const char *k_pch_Sample_ScreenOffsetX_Int32 = "ScreenOffsetX";
70 const char *k_pch_Sample_DebugMode_Bool = "DebugMode";
71
72 TrackedDeviceIndex_t m_unObjectId;
73 PropertyContainerHandle_t m_ulPropertyContainer;
74 string m_sSerialNumber;
75 string m_sModelNumber;
76 float m_flIPD;
77 int32_t m_nWindowX;
78 int32_t m_nWindowY;
79 int32_t m_nWindowWidth;
80 int32_t m_nWindowHeight;
81 int32_t m_nRenderWidth;
82 int32_t m_nRenderHeight;
83 float m_flSecondsFromVsyncToPhotons;
84 float m_flDisplayFrequency;
85 float m_fDistortionK1;
86 float m_fDistortionK2;
87 float m_fZoomWidth;
88 float m_fZoomHeight;
89 int32_t m_nDistanceBetweenEyes;
90 int32_t m_nScreenOffsetX;
91 bool m_bDebugMode;
92
93 double DegToRad(double Deg)
94 {
95 return (Deg * 0.017453292519943);
96 }
97
98 double OffsetYPR(double Deg)
99 {
100 if (Deg < -180)
101 {
102 Deg += 360;
103 }
104 else if (Deg > 180)
105 {
106 Deg -= 360;
107 }
108
109 return (Deg);
110 }
111
112 CSampleDeviceDriver()
113 {
114 m_unObjectId = k_unTrackedDeviceIndexInvalid;
115 m_ulPropertyContainer = k_ulInvalidPropertyContainer;
116 m_sSerialNumber = "SerialNumber";
117 m_sModelNumber = "ModelNumber";
118 m_flIPD = VRSettings()->GetFloat(k_pch_SteamVR_Section, k_pch_SteamVR_IPD_Float);
119 m_nWindowX = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_WindowX_Int32);
120 m_nWindowY = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_WindowY_Int32);
121 m_nWindowWidth = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_WindowWidth_Int32);
122 m_nWindowHeight = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_WindowHeight_Int32);
123 m_nRenderWidth = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_RenderWidth_Int32);
124 m_nRenderHeight = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_RenderHeight_Int32);
125 m_flSecondsFromVsyncToPhotons = VRSettings()->GetFloat(k_pch_Sample_Section, k_pch_Sample_SecondsFromVsyncToPhotons_Float);
126 m_flDisplayFrequency = VRSettings()->GetFloat(k_pch_Sample_Section, k_pch_Sample_DisplayFrequency_Float);
127 m_fDistortionK1 = VRSettings()->GetFloat(k_pch_Sample_Section, k_pch_Sample_DistortionK1_Float);
128 m_fDistortionK2 = VRSettings()->GetFloat(k_pch_Sample_Section, k_pch_Sample_DistortionK2_Float);
129 m_fZoomWidth = VRSettings()->GetFloat(k_pch_Sample_Section, k_pch_Sample_ZoomWidth_Float);
130 m_fZoomHeight = VRSettings()->GetFloat(k_pch_Sample_Section, k_pch_Sample_ZoomHeight_Float);
131 m_nDistanceBetweenEyes = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_DistanceBetweenEyes_Int32);
132 m_nScreenOffsetX = VRSettings()->GetInt32(k_pch_Sample_Section, k_pch_Sample_ScreenOffsetX_Int32);
133 m_bDebugMode = VRSettings()->GetBool(k_pch_Sample_Section, k_pch_Sample_DebugMode_Bool);
134 }
135
136 ~CSampleDeviceDriver()
137 {
138
139 }
140
141 EVRInitError Activate(TrackedDeviceIndex_t unObjectId)
142 {
143 m_unObjectId = unObjectId;
144 m_ulPropertyContainer = VRProperties()->TrackedDeviceToPropertyContainer(m_unObjectId);
145
146 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_ModelNumber_String, m_sModelNumber.c_str());
147 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_RenderModelName_String, m_sModelNumber.c_str());
148 VRProperties()->SetFloatProperty(m_ulPropertyContainer, Prop_UserIpdMeters_Float, m_flIPD);
149 VRProperties()->SetFloatProperty(m_ulPropertyContainer, Prop_UserHeadToEyeDepthMeters_Float, 0.f);
150 VRProperties()->SetFloatProperty(m_ulPropertyContainer, Prop_DisplayFrequency_Float, m_flDisplayFrequency);
151 VRProperties()->SetFloatProperty(m_ulPropertyContainer, Prop_SecondsFromVsyncToPhotons_Float, m_flSecondsFromVsyncToPhotons);
152
153 // return a constant that's not 0 (invalid) or 1 (reserved for Oculus)
154 VRProperties()->SetUint64Property(m_ulPropertyContainer, Prop_CurrentUniverseId_Uint64, 2);
155
156 // avoid "not fullscreen" warnings from vrmonitor
157 VRProperties()->SetBoolProperty(m_ulPropertyContainer, Prop_IsOnDesktop_Bool, false);
158
159 //Debug mode activate Windowed Mode (borderless fullscreen), locked to 30 FPS, for testing
160 VRProperties()->SetBoolProperty(m_ulPropertyContainer, Prop_DisplayDebugMode_Bool, m_bDebugMode);
161
162 // Icons can be configured in code or automatically configured by an external file "drivername\resources\driver.vrresources".
163 // Icon properties NOT configured in code (post Activate) are then auto-configured by the optional presence of a driver's "drivername\resources\driver.vrresources".
164 // In this manner a driver can configure their icons in a flexible data driven fashion by using an external file.
165 //
166 // The structure of the driver.vrresources file allows a driver to specialize their icons based on their HW.
167 // Keys matching the value in "Prop_ModelNumber_String" are considered first, since the driver may have model specific icons.
168 // An absence of a matching "Prop_ModelNumber_String" then considers the ETrackedDeviceClass ("HMD", "Controller", "GenericTracker", "TrackingReference")
169 // since the driver may have specialized icons based on those device class names.
170 //
171 // An absence of either then falls back to the "system.vrresources" where generic device class icons are then supplied.
172 //
173 // Please refer to "bin\drivers\sample\resources\driver.vrresources" which contains this sample configuration.
174 //
175 // "Alias" is a reserved key and specifies chaining to another json block.
176 //
177 // In this sample configuration file (overly complex FOR EXAMPLE PURPOSES ONLY)....
178 //
179 // "Model-v2.0" chains through the alias to "Model-v1.0" which chains through the alias to "Model-v Defaults".
180 //
181 // 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.
182 // Thus "Prop_NamedIconPathDeviceAlertLow_String" in each model's block represent a specialization specific for that "model".
183 // Keys in "Model-v Defaults" are an example of mapping to the same states, and here all map to "Prop_NamedIconPathDeviceOff_String".
184
185 // Setup properties directly in code.
186 // Path values are of the form {drivername}\icons\some_icon_filename.png
187 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceOff_String, "{CustomHMD}/icons/headset_sample_status_off.png");
188 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceSearching_String, "{CustomHMD}/icons/headset_sample_status_searching.gif");
189 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceSearchingAlert_String,"{CustomHMD}/icons/headset_sample_status_searching_alert.gif");
190 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceReady_String, "{CustomHMD}/icons/headset_sample_status_ready.png");
191 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceReadyAlert_String, "{CustomHMD}/icons/headset_sample_status_ready_alert.png");
192 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceNotReady_String, "{CustomHMD}/icons/headset_sample_status_error.png");
193 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceStandby_String, "{CustomHMD}/icons/headset_sample_status_standby.png");
194 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceAlertLow_String, "{CustomHMD}/icons/headset_sample_status_ready_low.png");
195
196 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceOff_String, "{CustomHMD}/icons/controller_status_off.png");
197 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceSearching_String, "{CustomHMD}/icons/controller_status_searching.gif");
198 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceSearchingAlert_String,"{CustomHMD}/icons/controller_status_searching_alert.gif");
199 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceReady_String, "{CustomHMD}/icons/controller_status_ready.png");
200 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceReadyAlert_String, "{CustomHMD}/icons/controller_status_ready_alert.png");
201 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceNotReady_String, "{CustomHMD}/icons/controller_status_error.png");
202 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceStandby_String, "{CustomHMD}/icons/controller_status_standby.png",);
203 // VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_NamedIconPathDeviceAlertLow_String, "{CustomHMD}/icons/controller_status_ready_low.png");
204
205 return VRInitError_None;
206 }
207
208 void Deactivate()
209 {
210 m_unObjectId = k_unTrackedDeviceIndexInvalid;
211 }
212
213 void EnterStandby()
214 {
215
216 }
217
218 void* GetComponent(const char* pchComponentNameAndVersion)
219 {
220 if (!_stricmp(pchComponentNameAndVersion, IVRDisplayComponent_Version))
221 {
222 return (IVRDisplayComponent*)this;
223 }
224
225 // override this to add a component to a driver
226 return NULL;
227 }
228
229 void PowerOff()
230 {
231
232 }
233
234 void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize)
235 {
236 if (unResponseBufferSize >= 1)
237 pchResponseBuffer[0] = 0;
238 }
239
240 void GetWindowBounds(int32_t* pnX, int32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight)
241 {
242 *pnX = m_nWindowX;
243 *pnY = m_nWindowY;
244 *pnWidth = m_nWindowWidth;
245 *pnHeight = m_nWindowHeight;
246 }
247
248 bool IsDisplayOnDesktop()
249 {
250 return true;
251 }
252
253 bool IsDisplayRealDisplay()
254 {
255 return false;
256 }
257
258 void GetRecommendedRenderTargetSize(uint32_t* pnWidth, uint32_t* pnHeight)
259 {
260 *pnWidth = m_nRenderWidth;
261 *pnHeight = m_nRenderHeight;
262 }
263
264 void GetEyeOutputViewport(EVREye eEye, uint32_t* pnX, uint32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight)
265 {
266 *pnY = m_nScreenOffsetX;
267 *pnWidth = m_nWindowWidth / 2;
268 *pnHeight = m_nWindowHeight;
269
270 if (eEye == Eye_Left)
271 {
272 *pnX = m_nDistanceBetweenEyes;
273 }
274 else
275 {
276 *pnX = (m_nWindowWidth / 2) - m_nDistanceBetweenEyes;
277 }
278 }
279
280 void GetProjectionRaw(EVREye eEye, float* pfLeft, float* pfRight, float* pfTop, float* pfBottom)
281 {
282 *pfLeft = -1.0;
283 *pfRight = 1.0;
284 *pfTop = -1.0;
285 *pfBottom = 1.0;
286 }
287
288 DistortionCoordinates_t ComputeDistortion(EVREye eEye, float fU, float fV)
289 {
290 DistortionCoordinates_t coordinates;
291
292 // Distortion for lens from https://github.com/HelenXR/openvr_survivor/blob/master/src/head_mount_display_device.cc
293
294 float hX;
295 float hY;
296 double rr;
297 double r2;
298 double theta;
299
300 rr = sqrt((fU - 0.5f) * (fU - 0.5f) + (fV - 0.5f) * (fV - 0.5f));
301 r2 = rr * (1 + m_fDistortionK1 * (rr * rr) + m_fDistortionK2 * (rr * rr * rr * rr));
302 theta = atan2(fU - 0.5f, fV - 0.5f);
303 hX = sin(theta) * r2 * m_fZoomWidth;
304 hY = cos(theta) * r2 * m_fZoomHeight;
305
306 coordinates.rfBlue[0] = hX + 0.5f;
307 coordinates.rfBlue[1] = hY + 0.5f;
308 coordinates.rfGreen[0] = hX + 0.5f;
309 coordinates.rfGreen[1] = hY + 0.5f;
310 coordinates.rfRed[0] = hX + 0.5f;
311 coordinates.rfRed[1] = hY + 0.5f;
312
313 return coordinates;
314 }
315
316 // Headset Control
317 DriverPose_t GetPose()
318 {
319 DriverPose_t pose = { 0 };
320
321 pose.poseIsValid = true;
322 pose.result = TrackingResult_Running_OK;
323 pose.deviceIsConnected = true;
324
325 pose.qWorldFromDriverRotation.w = 1;
326 pose.qWorldFromDriverRotation.x = 0;
327 pose.qWorldFromDriverRotation.y = 0;
328 pose.qWorldFromDriverRotation.z = 0;
329
330 pose.qDriverFromHeadRotation.w = 1;
331 pose.qDriverFromHeadRotation.x = 0;
332 pose.qDriverFromHeadRotation.y = 0;
333 pose.qDriverFromHeadRotation.z = 0;
334
335 // Headset
336 // Move Forward/Backward
337 if ((GetAsyncKeyState(VK_UP) & 0x8000) != 0) HMD.Z -= StepPos;
338 if ((GetAsyncKeyState(VK_DOWN) & 0x8000) != 0) HMD.Z += StepPos;
339
340 // Move Right/Left
341 if ((GetAsyncKeyState(VK_LEFT) & 0x8000) != 0) HMD.X -= StepPos;
342 if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) != 0) HMD.X += StepPos;
343
344 // Move Up/Down
345 if ((GetAsyncKeyState(VK_NUMPAD0) & 0x8000) != 0) HMD.Y += StepPos;
346 if ((GetAsyncKeyState(VK_DECIMAL) & 0x8000) != 0) HMD.Y -= StepPos;
347
348 // Rotate Right/Left
349 if ((GetAsyncKeyState(VK_NUMPAD1) & 0x8000) != 0) HMD.Pitch += StepRot;
350 if ((GetAsyncKeyState(VK_NUMPAD3) & 0x8000) != 0) HMD.Pitch -= StepRot;
351
352 // Rotate Up/Down
353 if ((GetAsyncKeyState(VK_NUMPAD5) & 0x8000) != 0) HMD.Roll += StepRot;
354 if ((GetAsyncKeyState(VK_NUMPAD2) & 0x8000) != 0) HMD.Roll -= StepRot;
355
356 // Roll Left/Right
357 if ((GetAsyncKeyState(VK_NUMPAD4) & 0x8000) != 0) HMD.Yaw += StepRot;
358 if ((GetAsyncKeyState(VK_NUMPAD6) & 0x8000) != 0) HMD.Yaw -= StepRot;
359
360 HMD.Yaw = OffsetYPR(HMD.Yaw);
361 HMD.Pitch = OffsetYPR(HMD.Pitch);
362 HMD.Roll = OffsetYPR(HMD.Roll);
363
364 //Convert yaw, pitch, roll to quaternion
365 double t0 = cos(DegToRad(HMD.Yaw));
366 double t1 = sin(DegToRad(HMD.Yaw));
367 double t2 = cos(DegToRad(HMD.Roll));
368 double t3 = sin(DegToRad(HMD.Roll));
369 double t4 = cos(DegToRad(HMD.Pitch));
370 double t5 = sin(DegToRad(HMD.Pitch));
371
372 //Set head tracking rotation
373 pose.qRotation.w = t0 * t2 * t4 + t1 * t3 * t5;
374 pose.qRotation.x = t0 * t3 * t4 - t1 * t2 * t5;
375 pose.qRotation.y = t0 * t2 * t5 + t1 * t3 * t4;
376 pose.qRotation.z = t1 * t2 * t4 - t0 * t3 * t5;
377
378 pose.vecPosition[0] = HMD.X;
379 pose.vecPosition[1] = HMD.Y;
380 pose.vecPosition[2] = HMD.Z;
381
382 return pose;
383 }
384
385 void RunFrame()
386 {
387 // In a real driver, this should happen from some pose tracking thread.
388 // The RunFrame interval is unspecified and can be very irregular if some other
389 // driver blocks it for some periodic task.
390
391 VRServerDriverHost()->TrackedDevicePoseUpdated(m_unObjectId, GetPose(), sizeof(DriverPose_t));
392 }
393
394 string GetSerialNumber() const { return m_sSerialNumber; }
395};
396
397class CSampleControllerDriver : public ITrackedDeviceServerDriver
398{
399 public:
400
401 int32_t ControllerIndex;
402 TrackedDeviceIndex_t m_unObjectId;
403 PropertyContainerHandle_t m_ulPropertyContainer;
404 VRInputComponentHandle_t m_compHaptic;
405 VRInputComponentHandle_t ulSkeletalComponentHandle;
406 VRInputComponentHandle_t HButtons[4], HAnalog[3];
407
408 double DegToRad(double Deg)
409 {
410 return (Deg * 0.017453292519943);
411 }
412
413 double OffsetYPR(double Deg)
414 {
415 if (Deg < -180)
416 {
417 Deg += 360;
418 }
419 else if (Deg > 180)
420 {
421 Deg -= 360;
422 }
423
424 return (Deg);
425 }
426
427 CSampleControllerDriver()
428 {
429 m_unObjectId = k_unTrackedDeviceIndexInvalid;
430 m_ulPropertyContainer = k_ulInvalidPropertyContainer;
431 }
432
433 void SetControllerIndex(int32_t CtrlIndex)
434 {
435 ControllerIndex = CtrlIndex;
436 }
437
438 ~CSampleControllerDriver()
439 {
440
441 }
442
443 EVRInitError Activate(TrackedDeviceIndex_t unObjectId)
444 {
445 m_unObjectId = unObjectId;
446 m_ulPropertyContainer = VRProperties()->TrackedDeviceToPropertyContainer(m_unObjectId);
447
448 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_ControllerType_String, "vive_controller");
449 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_RenderModelName_String, "vr_controller_vive_1_5");
450
451 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_TrackingSystemName_String, "VR Controller");
452 VRProperties()->SetInt32Property(m_ulPropertyContainer, Prop_DeviceClass_Int32, TrackedDeviceClass_Controller);
453
454 switch (ControllerIndex)
455 {
456 case 1:
457 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_SerialNumber_String, "CTRL1Serial");
458 break;
459
460 case 2:
461 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_SerialNumber_String, "CTRL2Serial");
462 break;
463 }
464
465 uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL;
466 VRProperties()->SetUint64Property(m_ulPropertyContainer, Prop_SupportedButtons_Uint64, supportedButtons);
467 VRProperties()->SetInt32Property(m_ulPropertyContainer, Prop_Axis0Type_Int32, k_eControllerAxis_TrackPad);
468
469 // return a constant that's not 0 (invalid) or 1 (reserved for Oculus)
470 //VRProperties()->SetUint64Property( m_ulPropertyContainer, Prop_CurrentUniverseId_Uint64, 2 );
471
472 // avoid "not fullscreen" warnings from vrmonitor
473 //VRProperties()->SetBoolProperty( m_ulPropertyContainer, Prop_IsOnDesktop_Bool, false );
474
475 // our sample device isn't actually tracked, so set this property to avoid having the icon blink in the status window
476 //VRProperties()->SetBoolProperty( m_ulPropertyContainer, Prop_NeverTracked_Bool, false );
477
478 // even though we won't ever track we want to pretend to be the right hand so binding will work as expected
479
480 switch (ControllerIndex)
481 {
482 case 1:
483 VRProperties()->SetInt32Property(m_ulPropertyContainer, Prop_ControllerRoleHint_Int32, TrackedControllerRole_LeftHand);
484 break;
485
486 case 2:
487 VRProperties()->SetInt32Property(m_ulPropertyContainer, Prop_ControllerRoleHint_Int32, TrackedControllerRole_RightHand);
488 break;
489 }
490
491 // this file tells the UI what to show the user for binding this controller as well as what default bindings should
492 // be for legacy or other apps
493 VRProperties()->SetStringProperty(m_ulPropertyContainer, Prop_InputProfilePath_String, "{CustomHMD}/input/CustomHMD_profile.json");
494
495 // Create all the input components
496 // Buttons handles
497 VRDriverInput()->CreateBooleanComponent(m_ulPropertyContainer, "/input/application_menu/click", &HButtons[0]);
498 VRDriverInput()->CreateBooleanComponent(m_ulPropertyContainer, "/input/grip/click", &HButtons[1]);
499 VRDriverInput()->CreateBooleanComponent(m_ulPropertyContainer, "/input/system/click", &HButtons[2]);
500 VRDriverInput()->CreateBooleanComponent(m_ulPropertyContainer, "/input/trackpad/click", &HButtons[3]);
501
502 // Analog handles
503 VRDriverInput()->CreateScalarComponent(m_ulPropertyContainer, "/input/trackpad/x", &HAnalog[0], VRScalarType_Absolute, VRScalarUnits_NormalizedTwoSided);
504 VRDriverInput()->CreateScalarComponent(m_ulPropertyContainer, "/input/trackpad/y", &HAnalog[1], VRScalarType_Absolute, VRScalarUnits_NormalizedTwoSided);
505 VRDriverInput()->CreateScalarComponent(m_ulPropertyContainer, "/input/trigger/value", &HAnalog[2], VRScalarType_Absolute, VRScalarUnits_NormalizedTwoSided);
506
507 //char buffer[50] = "CreateSkeletonComponent failed. Error: ";
508 //_itoa_s(err, buffer, 10);
509 //MessageBoxA(0, "Hello", buffer, 1);
510
511 // create our haptic component
512 VRDriverInput()->CreateHapticComponent(m_ulPropertyContainer, "/output/haptic", &m_compHaptic);
513
514 return VRInitError_None;
515 }
516
517 void Deactivate()
518 {
519 m_unObjectId = k_unTrackedDeviceIndexInvalid;
520 }
521
522 void EnterStandby()
523 {
524
525 }
526
527 void* GetComponent(const char* pchComponentNameAndVersion)
528 {
529 // override this to add a component to a driver
530 return NULL;
531 }
532
533 void PowerOff()
534 {
535
536 }
537
538 void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize)
539 {
540 if (unResponseBufferSize >= 1)
541 pchResponseBuffer[0] = 0;
542 }
543
544 // Controller Control
545 DriverPose_t GetPose()
546 {
547 DriverPose_t pose = { 0 };
548
549 pose.poseIsValid = true;
550 pose.result = TrackingResult_Running_OK;
551 pose.deviceIsConnected = true;
552
553 pose.qWorldFromDriverRotation.w = 1;
554 pose.qWorldFromDriverRotation.x = 0;
555 pose.qWorldFromDriverRotation.y = 0;
556 pose.qWorldFromDriverRotation.z = 0;
557
558 pose.qDriverFromHeadRotation.w = 1;
559 pose.qDriverFromHeadRotation.x = 0;
560 pose.qDriverFromHeadRotation.y = 0;
561 pose.qDriverFromHeadRotation.z = 0;
562
563 if (ControllerIndex == 1)
564 {
565 // FirstController
566 // Move Forward/Backward
567 if ((GetAsyncKeyState('Y') & 0x8000) != 0) FirstController.Z -= StepCtrlPos;
568 if ((GetAsyncKeyState(' ') & 0x8000) != 0) FirstController.Z += StepCtrlPos;
569
570 // Move Right/Left
571 if ((GetAsyncKeyState('A') & 0x8000) != 0) FirstController.X -= StepCtrlPos;
572 if ((GetAsyncKeyState('D') & 0x8000) != 0) FirstController.X += StepCtrlPos;
573
574 // Move Up/Down
575 if ((GetAsyncKeyState('Z') & 0x8000) != 0) FirstController.Y += StepCtrlPos;
576 if ((GetAsyncKeyState('E') & 0x8000) != 0) FirstController.Y -= StepCtrlPos;
577
578 // Rotate Up/Down
579 if ((GetAsyncKeyState('I') & 0x8000) != 0) FirstController.Yaw += StepRot;
580 if ((GetAsyncKeyState('K') & 0x8000) != 0) FirstController.Yaw -= StepRot;
581
582 // Rotate Right/Left
583 if ((GetAsyncKeyState('J') & 0x8000) != 0) FirstController.Roll += StepRot;
584 if ((GetAsyncKeyState('L') & 0x8000) != 0) FirstController.Roll -= StepRot;
585
586 // Roll Right/Left
587 if ((GetAsyncKeyState('U') & 0x8000) != 0) FirstController.Pitch += StepRot;
588 if ((GetAsyncKeyState('O') & 0x8000) != 0) FirstController.Pitch -= StepRot;
589
590 FirstController.Yaw = OffsetYPR(FirstController.Yaw);
591 FirstController.Pitch = OffsetYPR(FirstController.Pitch);
592 FirstController.Roll = OffsetYPR(FirstController.Roll);
593
594 //Convert yaw, pitch, roll to quaternion
595 double ct0 = cos(DegToRad(FirstController.Yaw));
596 double ct1 = sin(DegToRad(FirstController.Yaw));
597 double ct2 = cos(DegToRad(FirstController.Roll));
598 double ct3 = sin(DegToRad(FirstController.Roll));
599 double ct4 = cos(DegToRad(FirstController.Pitch));
600 double ct5 = sin(DegToRad(FirstController.Pitch));
601
602 pose.qRotation.w = ct0 * ct2 * ct4 + ct1 * ct3 * ct5;
603 pose.qRotation.x = ct0 * ct3 * ct4 - ct1 * ct2 * ct5;
604 pose.qRotation.y = ct0 * ct2 * ct5 + ct1 * ct3 * ct4;
605 pose.qRotation.z = ct1 * ct2 * ct4 - ct0 * ct3 * ct5;
606
607 pose.vecPosition[0] = FirstController.X - 0.2;
608 pose.vecPosition[1] = FirstController.Y;
609 pose.vecPosition[2] = FirstController.Z - 0.5;
610 }
611 else
612 {
613 // SecondController
614 // Move Forward/Backward
615 if ((GetAsyncKeyState('T') & 0x8000) != 0) SecondController.Z -= StepCtrlPos;
616 if ((GetAsyncKeyState('G') & 0x8000) != 0) SecondController.Z += StepCtrlPos;
617
618 // Move Right/Left
619 if ((GetAsyncKeyState('F') & 0x8000) != 0) SecondController.X -= StepCtrlPos;
620 if ((GetAsyncKeyState('H') & 0x8000) != 0) SecondController.X += StepCtrlPos;
621
622 // Move Up/Down
623 if ((GetAsyncKeyState('R') & 0x8000) != 0) SecondController.Y += StepCtrlPos;
624 if ((GetAsyncKeyState('Q') & 0x8000) != 0) SecondController.Y -= StepCtrlPos;
625
626 // Rotate Up/Down
627 if ((GetAsyncKeyState('I') & 0x8000) != 0) SecondController.Yaw += StepRot;
628 if ((GetAsyncKeyState('K') & 0x8000) != 0) SecondController.Yaw -= StepRot;
629
630 // Rotate Right/Left
631 if ((GetAsyncKeyState('J') & 0x8000) != 0) SecondController.Roll += StepRot;
632 if ((GetAsyncKeyState('L') & 0x8000) != 0) SecondController.Roll -= StepRot;
633
634 // Roll Right/Left
635 if ((GetAsyncKeyState('U') & 0x8000) != 0) SecondController.Pitch += StepRot;
636 if ((GetAsyncKeyState('O') & 0x8000) != 0) SecondController.Pitch -= StepRot;
637
638 SecondController.Yaw = OffsetYPR(SecondController.Yaw);
639 SecondController.Pitch = OffsetYPR(SecondController.Pitch);
640 SecondController.Roll = OffsetYPR(SecondController.Roll);
641
642 //Convert yaw, pitch, roll to quaternion
643 double ct0 = cos(DegToRad(SecondController.Yaw));
644 double ct1 = sin(DegToRad(SecondController.Yaw));
645 double ct2 = cos(DegToRad(SecondController.Roll));
646 double ct3 = sin(DegToRad(SecondController.Roll));
647 double ct4 = cos(DegToRad(SecondController.Pitch));
648 double ct5 = sin(DegToRad(SecondController.Pitch));
649
650 pose.qRotation.w = ct0 * ct2 * ct4 + ct1 * ct3 * ct5;
651 pose.qRotation.x = ct0 * ct3 * ct4 - ct1 * ct2 * ct5;
652 pose.qRotation.y = ct0 * ct2 * ct5 + ct1 * ct3 * ct4;
653 pose.qRotation.z = ct1 * ct2 * ct4 - ct0 * ct3 * ct5;
654
655 pose.vecPosition[0] = SecondController.X + 0.2;
656 pose.vecPosition[1] = SecondController.Y;
657 pose.vecPosition[2] = SecondController.Z - 0.5;
658 }
659
660 return pose;
661 }
662
663 void RunFrame()
664 {
665 // Your driver would read whatever hardware state is associated with its input components and pass that
666 // in to UpdateBooleanComponent. This could happen in RunFrame or on a thread of your own that's reading USB
667 // state. There's no need to update input state unless it changes, but it doesn't do any harm to do so.
668
669 if (ControllerIndex == 1)
670 {
671 VRDriverInput()->UpdateBooleanComponent(HButtons[0], (0x8000 & GetAsyncKeyState(0x39)) != 0, 0); //Application Menu
672 VRDriverInput()->UpdateBooleanComponent(HButtons[1], (0x8000 & GetAsyncKeyState(0x30)) != 0, 0); //Grip
673 VRDriverInput()->UpdateBooleanComponent(HButtons[2], (0x8000 & GetAsyncKeyState(0xDB)) != 0, 0); //System
674 VRDriverInput()->UpdateBooleanComponent(HButtons[3], (0x8000 & GetAsyncKeyState(0xDD)) != 0, 0); //Trackpad
675
676 VRDriverInput()->UpdateScalarComponent(HAnalog[0], 0.0, 0); //Trackpad x
677 VRDriverInput()->UpdateScalarComponent(HAnalog[1], 0.0, 0); //Trackpad y
678
679 if ((GetAsyncKeyState('W') & 0x8000) != 0)
680 VRDriverInput()->UpdateScalarComponent(HAnalog[0], 1.0, 0);
681
682 if ((GetAsyncKeyState('X') & 0x8000) != 0)
683 VRDriverInput()->UpdateScalarComponent(HAnalog[1], 1.0, 0);
684
685 if ((GetAsyncKeyState('C') & 0x8000) != 0) //Trigger
686 {
687 VRDriverInput()->UpdateScalarComponent(HAnalog[2], 1.0, 0);
688 }
689 else
690 {
691 VRDriverInput()->UpdateScalarComponent(HAnalog[2], 0.0, 0);
692 }
693 }
694 else
695 {
696 VRDriverInput()->UpdateBooleanComponent(HButtons[0], (0x8000 & GetAsyncKeyState(VK_F1)) != 0, 0); //Application Menu
697 VRDriverInput()->UpdateBooleanComponent(HButtons[1], (0x8000 & GetAsyncKeyState(VK_F2)) != 0, 0); //Grip
698 VRDriverInput()->UpdateBooleanComponent(HButtons[2], (0x8000 & GetAsyncKeyState(VK_F3)) != 0, 0); //System
699 VRDriverInput()->UpdateBooleanComponent(HButtons[3], (0x8000 & GetAsyncKeyState(VK_F4)) != 0, 0); //Trackpad
700
701 VRDriverInput()->UpdateScalarComponent(HAnalog[0], 0.0, 0); //Trackpad x
702 VRDriverInput()->UpdateScalarComponent(HAnalog[1], 0.0, 0); //Trackpad y
703
704 if ((GetAsyncKeyState('V') & 0x8000) != 0) //Trigger
705 {
706 VRDriverInput()->UpdateScalarComponent(HAnalog[2], 1.0, 0);
707 }
708 else
709 {
710 VRDriverInput()->UpdateScalarComponent(HAnalog[2], 0.0, 0);
711 }
712 }
713
714 if (m_unObjectId != k_unTrackedDeviceIndexInvalid)
715 {
716 VRServerDriverHost()->TrackedDevicePoseUpdated(m_unObjectId, GetPose(), sizeof(DriverPose_t));
717 }
718 }
719
720 void ProcessEvent(const VREvent_t & vrEvent)
721 {
722 switch (vrEvent.eventType)
723 {
724 case VREvent_Input_HapticVibration:
725 {
726 if (vrEvent.data.hapticVibration.componentHandle == m_compHaptic)
727 {
728 // This is where you would send a signal to your hardware to trigger actual haptic feedback
729 }
730 }
731 break;
732 }
733 }
734
735 string GetSerialNumber() const
736 {
737 switch (ControllerIndex)
738 {
739 case 1:
740 return "CTRL1Serial";
741 break;
742
743 case 2:
744 return "CTRL2Serial";
745 break;
746 }
747 }
748};
749
750class CServerDriver_Sample : public IServerTrackedDeviceProvider
751{
752 public:
753
754 CSampleDeviceDriver* m_pNullHmdLatest;
755 CSampleControllerDriver* m_pController;
756 CSampleControllerDriver* m_pController2;
757
758 const char* const* GetInterfaceVersions() { return k_InterfaceVersions; }
759 bool ShouldBlockStandbyMode() { return false; }
760 void EnterStandby() {}
761 void LeaveStandby() {}
762
763 EVRInitError Init(IVRDriverContext* pDriverContext)
764 {
765 VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext);
766
767 m_pNullHmdLatest = new CSampleDeviceDriver();
768 VRServerDriverHost()->TrackedDeviceAdded(m_pNullHmdLatest->GetSerialNumber().c_str(), TrackedDeviceClass_HMD, m_pNullHmdLatest);
769
770 m_pController = new CSampleControllerDriver();
771 m_pController->SetControllerIndex(1);
772 VRServerDriverHost()->TrackedDeviceAdded(m_pController->GetSerialNumber().c_str(), TrackedDeviceClass_Controller, m_pController);
773
774 m_pController2 = new CSampleControllerDriver();
775 m_pController2->SetControllerIndex(2);
776 VRServerDriverHost()->TrackedDeviceAdded(m_pController2->GetSerialNumber().c_str(), TrackedDeviceClass_Controller, m_pController2);
777
778 return VRInitError_None;
779 }
780
781 void Cleanup()
782 {
783 delete m_pNullHmdLatest;
784 m_pNullHmdLatest = NULL;
785
786 delete m_pController;
787 m_pController = NULL;
788
789 delete m_pController2;
790 m_pController2 = NULL;
791 }
792
793 void RunFrame()
794 {
795 m_pNullHmdLatest->RunFrame();
796 m_pController->RunFrame();
797 m_pController2->RunFrame();
798
799 VREvent_t vrEvent;
800 while (VRServerDriverHost()->PollNextEvent(&vrEvent, sizeof(vrEvent)))
801 {
802 m_pController->ProcessEvent(vrEvent);
803 m_pController2->ProcessEvent(vrEvent);
804 }
805 }
806};
807
808// This is the main for the driver, this is how openvr finds out about what kinds of devices your driver provides.
809// Conceptually, Openvr considers your driver to be a device provider and you provide a IServerTrackedDeviceProvider
810// that openvr will give an IVRDriverContext to.
811#define HMD_DLL_EXPORT extern "C" __declspec(dllexport)
812#define HMD_DLL_IMPORT extern "C" __declspec(dllimport)
813
814HMD_DLL_EXPORT void *HmdDriverFactory(const char* pInterfaceName, int* pReturnCode)
815{
816 static CServerDriver_Sample g_serverDriverNull;
817
818 return &g_serverDriverNull;
819}