Compare commits

..
457 Commits
Author SHA1 Message Date
Hyblocker 5ecef2fb1b chore: bump version 2026-09-24 22:34:52 +02:00
Hyblocker c637aaf568 fix: dont overload base station BLE stack 2026-09-24 22:34:39 +02:00
Hyblocker 8794135cc2 fix: compute confidence for latency to better handle spinning 2026-09-24 22:34:09 +02:00
Hyblocker ec8d97d065 fix: update axis variance to ground truth 2026-09-24 22:30:58 +02:00
Hyblocker 8077296a8e chore: handle some edge cases 2026-09-24 22:30:03 +02:00
Hyblocker e1baf24097 chore: formatting 2026-09-23 21:58:53 +02:00
Hyblocker ad74e1dfe9 chore: more logging 2026-09-23 21:55:50 +02:00
Hyblocker 0daefeb3f1 fix: calibration quality not remembering axis variance 2026-09-23 21:55:10 +02:00
Hyblocker 4b44ae2e33 chore: optimise hot loop allocations a bit 2026-09-23 21:54:40 +02:00
Hyblocker 1c14061930 chore: improve linux launch script 2026-09-20 23:05:24 +02:00
Hyblocker b708c95c26 vendor: add zstd 2026-09-20 23:04:56 +02:00
Hyblocker 06ce8fbf39 fix: compile error 2026-09-05 00:21:03 +02:00
Hyblocker 9b1968a4e5 fix: bump threshold slightly 2026-09-05 00:20:53 +02:00
Hyblocker e45cc2e94d fix: fine tune BLE params for windows to be more deterministic 2026-09-04 23:58:30 +02:00
Hyblocker d588ffefc4 fix: calibration algorithm numerical stability shouldnt corrupt values 2026-09-04 23:51:59 +02:00
Hyblocker ec0a3e6d52 fix: base stations not powering on at start-up #62 2026-09-04 00:10:38 +02:00
Hyblocker 346b4845dd chore: bump version 2026-09-03 23:56:44 +02:00
Hyblocker ef2bf4857f fix: dpi awareness? 2026-09-03 23:54:40 +02:00
Hyblocker 026b085568 fix: force calibration fixes 2026-09-03 23:54:28 +02:00
Hyblocker b3c7c19e82 chore: ignore more events 2026-09-03 23:54:05 +02:00
Hyblocker 684d4d4a4c chore: add finnish translation 2026-09-03 23:53:56 +02:00
Hyblocker 21570cb66a fix: tracker turning off would select something else 2026-09-03 23:53:41 +02:00
Hyblocker c2a77d5c5d fix: some devices are ignored for calibrations in some cases 2026-09-03 23:53:19 +02:00
Hyblocker f4446b410c feat: add driver conflict fixes 2026-09-03 23:52:28 +02:00
Hyblocker 3f2bff91fc feat: add launch script for linux 2026-08-30 15:05:38 +02:00
Hyblocker 0850b3fe09 fix: forgor to commit 2026-08-30 12:51:35 +02:00
Hyblocker a85659bb6a Merge branch 'develop' of https://github.com/hyblocker/OpenVR-SpaceCalibrator into develop 2026-08-30 12:39:33 +02:00
Hyblocker daa5d542c2 Merge pull request #59 from LorcyTF/develop
German localization
2026-08-30 12:36:25 +02:00
Hyblocker a80cb71432 fix: force apply calibration tweaks for continuous 2026-08-29 19:54:50 +02:00
Hyblocker e7a9563f54 fix: reset not resetting correctly 2026-08-29 19:54:24 +02:00
Hyblocker 1ae50692cc fix: editing doesnt update 2026-08-29 19:53:47 +02:00
Hyblocker 24d8ad9da6 fix: target device not being transformed during intiial calib / standard calib 2026-08-29 17:45:55 +02:00
Hyblocker 2a41e7933b fix: formatting should use CRLF 2026-08-29 11:54:15 +02:00
Hyblocker ce9f453cb5 fix: maybe fix cont tracker? 2026-08-29 11:48:13 +02:00
Lorcy f8f4ad6985 Update TRANSLATING.md
removed "missing" lable from German
2026-08-28 20:52:00 +02:00
Lorcy 606388bfd9 German localization 2026-08-28 20:50:36 +02:00
Hyblocker 2d094f7ac5 fix: calibrations not applying when force calibration is true 2026-08-28 17:22:42 +02:00
Hyblocker a7d5d80bdc fix: overlay in vr dashboard rendering inconsistencies 2026-08-28 09:30:22 +02:00
Hyblocker 34e914a73d chore: add editorconfig for more consistency 2026-08-27 16:58:19 +02:00
Hyblocker 39739f8236 chore: license text entirely inline, we dont want 2 scrolling containers in each other 2026-08-27 16:58:06 +02:00
Hyblocker 455053cc8a feat: add Korean localisation 2026-08-27 16:39:40 +02:00
Hyblocker 18b04658f3 chore: clarify instructions for translations 2026-08-27 16:35:10 +02:00
Hyblocker 75b585ec2f chore: add clangd compilation db 2026-08-26 22:42:47 +02:00
Hyblocker ce8c721131 fix: why did it have .disable in the file name 2026-08-26 22:42:29 +02:00
Hyblocker 83f7ae677d chore: cleanup 2026-08-26 18:40:41 +02:00
Hyblocker 0430a0b7fb fix: vro 🥀 2026-08-25 15:44:07 +02:00
Hyblocker 1c469c2554 fix: linux build 2026-08-25 15:30:42 +02:00
Hyblocker d0f867793e chore: clang format 2026-08-25 11:30:05 +02:00
Hyblocker 3648fb3ec8 chore: bump version 2026-08-24 17:05:23 +02:00
Hyblocker ad6fb8fc0a feat: English US 2026-08-24 17:05:11 +02:00
Hyblocker 77dda7ff66 fix: handle devices not being assigned if turned on after boot 2026-08-24 16:45:03 +02:00
Hyblocker 896051ee6c fix: hide invalid calibration warning if selected devices are valid 2026-08-24 16:16:49 +02:00
Hyblocker 6f841f5c8d fix: make continuous calibration use a marquee progress bar 2026-08-24 16:16:31 +02:00
Hyblocker 50dfe23fb4 fix: make ghost entries more visually obvious 2026-08-24 15:56:02 +02:00
Hyblocker bb9feed0d8 fix: correct pose connection state being stale 2026-08-23 14:08:12 +02:00
Hyblocker 011f9b1f9c fix: base station 1.0 power state should be cached 2026-08-22 16:51:14 +02:00
Hyblocker a7e7584c88 chore: update italian 2026-08-20 01:00:32 +02:00
Hyblocker 77bb7cc695 chore: group implot3d 2026-08-19 17:14:33 +02:00
Hyblocker 4b558517b5 chore: more logging 2026-08-19 17:14:23 +02:00
Hyblocker 2cddea5f5b feat: update translations 2026-08-19 17:07:22 +02:00
Hyblocker 7806d61532 Merge pull request #56 from davidlongest/fix-about-scroll-crash
OOM Fix for Vulkan
2026-08-19 17:06:35 +02:00
davidlongest f8d551f480 Scrolling through the about page caused it to run out of descriptor pools. This uses the built-in descriptor pooling and just increases the size of the pool. 2026-08-18 23:23:09 +01:00
Hyblocker e11ba6a1c7 feat: latency estimation 2026-08-17 22:54:20 +02:00
Hyblocker 26dc86427f feat: relative calibration blending 2026-08-17 22:53:47 +02:00
Hyblocker af18a4ec31 feat: learn page polish 2026-08-17 22:52:07 +02:00
Hyblocker 9ae08a9ff3 fix: typos 2026-08-17 20:12:02 +02:00
Hyblocker cad7df1ac8 feat: add japanese 2026-08-17 20:11:55 +02:00
Hyblocker 0e7c378f29 fix: vk validation bug 2026-08-16 09:12:11 +02:00
Hyblocker cd7313b909 Merge pull request #55 from davidlongest/fix-ui-not-rendering
This fixes an image load issue specifically on Vulkan.
2026-08-16 08:45:52 +02:00
davidlongest 96b6d23503 This fixes an image load issue specifically on Vulkan. 2026-08-16 04:39:52 +01:00
Hyblocker f62c5b5a2b Update README.md 2026-08-15 21:41:34 +02:00
Hyblocker 5d1d99ceaa fix: epic linux build fail 2026-08-15 21:33:30 +02:00
Hyblocker 4d15255f82 Merge pull request #53 from ainaracatgirl/develop
Add new strings to Spanish (Spain)
2026-08-15 21:13:36 +02:00
Hyblocker 6dd6d29eab Merge pull request #54 from Flippyya/patch-1
Update no.json: Added base_stations_state_faulty field
2026-08-15 21:13:25 +02:00
Hyblocker 81a690665f chore: temp image assets 2026-08-15 21:09:31 +02:00
Hyblocker 3d2056e9d8 feat: actual images for tutorial 2026-08-15 20:52:13 +02:00
Hyblocker 996b997698 chore: update langs 2026-08-15 20:48:53 +02:00
Flippy 41e76f012b Update no.json: Added base_stations_state_faulty field 2026-08-15 17:31:35 +02:00
Hyblocker ce4bfb0521 chore: better data layout 2026-08-15 17:21:16 +02:00
Hyblocker afde5e80e5 chore: alias steam link 2026-08-15 17:21:01 +02:00
Ainara Garcia 890347a84b feat: add new strings to Spanish (Spain) 2026-08-15 17:17:36 +02:00
Ainara Garcia c745f14c27 Merge pull request #1 from hyblocker/develop
merge updates
2026-08-15 17:15:49 +02:00
Hyblocker 3af45e98d6 feat: detect red led on base station 2.0s 2026-08-15 17:11:02 +02:00
Hyblocker 34a4d4cc21 fix: crappy error handling 2026-08-15 03:00:24 +02:00
Hyblocker a0a14de29b feat: add translation credits 2026-08-15 03:00:15 +02:00
Hyblocker a8682b4b74 fix: minor improvements 2026-08-15 03:00:05 +02:00
Hyblocker c2368d7e69 fix: auto-detection 2026-08-15 02:59:47 +02:00
Hyblocker 3f5d0cf01a Merge branch 'develop' of https://github.com/hyblocker/OpenVR-SpaceCalibrator into develop 2026-08-14 15:42:14 +02:00
Hyblocker 0d5611d354 Merge pull request #52 from Flippyya/develop
feat: add Norwegian localization
2026-08-14 15:41:29 +02:00
Hyblocker 26adec1097 Merge pull request #51 from ainaracatgirl/develop
feat: add Spanish (Spain)
2026-08-14 15:40:57 +02:00
Hyblocker 30aa1aa0ab feat: images in ui 2026-08-14 12:42:45 +02:00
Hyblocker eabd71b217 feat: add support for loading images 2026-08-14 12:38:19 +02:00
Flippy 4fd5eb1bc1 Added norwegian language file 2026-08-12 14:53:03 +02:00
Ainara Garcia 4881abf719 feat: add Spanish (Spain) 2026-08-12 14:44:08 +02:00
Hyblocker 1a8e656737 chore: make window slightly smaller
better suited for 1080p
2026-08-12 01:12:08 +02:00
Hyblocker ca50bad07f fix: disable auto fix playspace jumps for now 2026-08-12 01:11:54 +02:00
Hyblocker b93838dc87 feat: russian 2026-08-11 23:07:05 +02:00
Hyblocker 42cbbaf63c chore: better enum name 2026-08-11 09:59:16 +02:00
Hyblocker 9dd7cf705b feat: add italian and polish 2026-08-11 09:58:23 +02:00
Hyblocker 54bfcd75ed fix: typo 2026-08-09 23:05:41 +02:00
Hyblocker bfa1b7cdf9 fix: map catalan 2026-08-09 21:20:57 +02:00
Hyblocker 07058d057a chore: use more CJK fonts so that we cover more glyphs 2026-08-09 21:10:41 +02:00
Hyblocker 6295eab220 lang: update french 2026-08-09 20:47:03 +02:00
Hyblocker 5abcf65a9c lang: add chinese simplified 2026-08-09 20:46:16 +02:00
Hyblocker cd3c79c565 fix: default values 2026-08-08 22:25:45 +02:00
Hyblocker 724cbb3dbc chore: adjust readme 2026-08-08 18:43:53 +02:00
Hyblocker b12b78521f fix: correct pointers to develop branch
nova branch has been deleted in prep for release
2026-08-08 18:25:59 +02:00
Hyblocker d20733e792 fix: bruh 2026-08-08 18:18:54 +02:00
Hyblocker e9b97c88b6 fix: make crash handler windows only 2026-08-08 18:08:17 +02:00
Hyblocker 9867e66093 feat: add tutorial text stuff 2026-08-08 16:58:02 +02:00
Hyblocker d47ef29cdf feat: disable calibrating buttons if no devices are selected 2026-08-08 16:18:58 +02:00
Hyblocker 35785e9e3d fix: no-op 2026-08-07 13:04:26 +02:00
Hyblocker e63e30e3a8 feat: add minidump generation on crash 2026-08-07 12:13:56 +02:00
Hyblocker 432f07342f feat: add button to reset settings 2026-08-07 01:33:13 +02:00
Hyblocker a1c00334c5 chore: simplify 2026-08-06 18:34:34 +02:00
Hyblocker f7f94fe8dd chore: enum to string for config 2026-08-06 18:34:19 +02:00
Hyblocker b57f3d3727 fix: warn when stage tracking is disabled 2026-08-06 13:37:58 +02:00
Hyblocker 4b1049bec6 chore: improve clarity 2026-08-06 13:36:12 +02:00
Hyblocker 2dd2f88b1a fix: vertically center channel text 2026-08-04 23:05:29 +02:00
Hyblocker ed15478080 fix: use correct api 2026-08-04 22:45:56 +02:00
Hyblocker 8620b9b870 chore: use less internals 2026-08-04 22:45:38 +02:00
Hyblocker e8bdea1e3f chore: sort base stations by SN so that they are ordered deterministically 2026-08-04 02:01:21 +02:00
Hyblocker 9558a4224d feat: add tutorial tab 2026-08-04 01:42:47 +02:00
Hyblocker df82252a09 fix: add rate limiting to BLE retries 2026-08-04 01:35:07 +02:00
Hyblocker e25e592113 fix: ignore base stations that are booting 2026-08-04 01:34:35 +02:00
Hyblocker 664fa56c92 feat: debug sample points on unit sphere 2026-08-04 01:34:11 +02:00
Hyblocker bf5bb40e0b fix: center base station channel btns 2026-08-04 01:33:45 +02:00
Hyblocker 4a67d66143 fix: make base station 2.0 channel collisions use a card 2026-08-04 01:29:41 +02:00
Hyblocker fd4d6e07a0 feat: progress bar in status card 2026-08-04 01:29:07 +02:00
Hyblocker 2349b0e8b3 fix: make missing hmd text a card 2026-08-04 01:28:48 +02:00
Hyblocker d490cea5b2 feat: advanced ui tabs 2026-08-04 01:28:24 +02:00
Hyblocker 831562803e fix: alignment issues 2026-08-04 01:27:58 +02:00
Hyblocker aa080f5282 feat: danger cards 2026-08-04 01:27:24 +02:00
Hyblocker aab17cbadc vendor: add implot3d 2026-08-04 01:27:07 +02:00
Hyblocker 4e8b04de2a feat: update lang file 2026-08-03 02:16:48 +02:00
Hyblocker 030839a74e fix: (bad) accessor for driverpose_t debugging 2026-08-02 12:11:31 +02:00
Hyblocker 548c81f36a fix: write more stuff to settings 2026-08-02 12:11:09 +02:00
Hyblocker 79d664f425 chore: minor UI/UX improvements 2026-08-02 12:10:38 +02:00
Hyblocker 51a8dacbc2 chore: simplify 2026-08-02 12:09:50 +02:00
Hyblocker fcbfd629cb feat: base station nicknames and editing etc 2026-08-02 12:09:41 +02:00
Hyblocker 5cfae0f74c feat: auto-playspace jump detection 2026-08-02 12:08:28 +02:00
Hyblocker e7166ebfb6 feat: invalidate metrics 2026-08-02 12:07:08 +02:00
Hyblocker ab5c236c22 chore: fine tune 2026-08-02 12:06:39 +02:00
Hyblocker 674dceb768 feat: minimum rotation variance 2026-08-02 12:05:58 +02:00
Hyblocker 9548fccf5d fix: improve rotation calibrations 2026-08-02 11:56:31 +02:00
Hyblocker 8f06f25da5 feat: init base station mgmt settings on fresh installs 2026-08-02 11:55:58 +02:00
Hyblocker 3dc496dfb4 fix: more lock free base station ops 2026-08-02 11:55:01 +02:00
Hyblocker 70f419aed2 fix: smarter alignment 2026-07-31 01:59:59 +02:00
Hyblocker 5d44c3508c fix: bigger hitboxes 2026-07-31 01:59:44 +02:00
Hyblocker 55fd3ef7bd feat: make window slightly bigger 2026-07-31 01:59:34 +02:00
Hyblocker 795d870cc4 chore: consistent colours 2026-07-30 20:17:31 +02:00
Hyblocker 0c8e856f96 fix: double log 2026-07-30 16:09:08 +02:00
Hyblocker da594fff55 chore: lock some stuff behind advanced settings 2026-07-30 16:08:17 +02:00
Hyblocker eb076ad53f feat: show LICENSE data in about page 2026-07-30 16:05:51 +02:00
Hyblocker 9e38ecdbd2 chore: pack data more tightly 2026-07-30 15:45:02 +02:00
Hyblocker d80726d7b5 chore: update license 2026-07-30 15:39:03 +02:00
Hyblocker f4bcfb05d0 feat: prediction in driver 2026-07-26 22:05:12 +02:00
Hyblocker 4fc6f5eab7 fix: more dx11 cleanup 2026-07-26 22:04:50 +02:00
Hyblocker b187315a5f fix: try fixing patching in CI? 2026-07-26 22:04:35 +02:00
Hyblocker 77b592a930 feat: ui overhaul 2026-07-26 02:05:40 +02:00
Hyblocker bd2ecfd766 fix: standby detection not persisting between scan ops 2026-07-26 02:04:56 +02:00
Hyblocker 3e8d6aab7f fix: bluetooth crash 2026-07-26 02:04:38 +02:00
Hyblocker 10a6f31f3d fix: legacy configs not being converted properly 2026-07-26 02:04:24 +02:00
Hyblocker fa8ec2cfb8 fix: debug guard 2026-07-26 02:04:07 +02:00
Hyblocker 8fab1301e4 feat: set cursor to hand when hovering on widgets 2026-07-26 02:04:00 +02:00
Hyblocker f1a6316de9 fix: clear samples when chaperone or wireless event happens 2026-07-26 02:03:04 +02:00
Hyblocker 6964a73de9 fix: edge case handling 2026-07-26 01:54:37 +02:00
Hyblocker 3bad583e9c fix: imgui textbox interactions 2026-07-26 01:53:53 +02:00
Hyblocker 7ca02f07dc fix: catch edge case?? 2026-07-26 01:53:35 +02:00
Hyblocker 66b398359a chore: suppress more events 2026-07-26 01:53:26 +02:00
Hyblocker 8859fbd791 feat: nord theme 2026-07-24 13:11:41 +02:00
Hyblocker c66fa95d4e fix: crash when SteamVR isn't running 2026-07-24 13:11:22 +02:00
Hyblocker f8792559b0 feat: enable auto base station management at startup/shutdown 2026-07-24 13:11:06 +02:00
Hyblocker 83a67de58a chore: better default settings 2026-07-24 13:07:08 +02:00
Hyblocker c8c074a4d7 feat: detect old firmware base station 2.0s and keep track of it
this is to better handle standby support in the UI
2026-07-24 13:06:55 +02:00
Hyblocker bc3d27eaed fix: map standby to sleep on old FW 2.0s
old FW 2.0s seemingly do not support standby, so make standby commands actually perform sleep in these cases
2026-07-24 12:54:55 +02:00
Hyblocker 1a635edaa2 fix: bluetooth logging is clearer 2026-07-24 12:54:06 +02:00
Hyblocker 9297cddba3 fix: strncmp instead of strcmp 2026-07-24 12:53:53 +02:00
Hyblocker b530571af2 fix: BLE scan thread dying instantly 2026-07-24 12:52:47 +02:00
Hyblocker 54bbdf56f7 feat: beautify about page 2026-07-23 12:30:54 +02:00
Hyblocker 70a8e73903 feat: beautify settings 2026-07-23 12:30:37 +02:00
Hyblocker d299c048fc fix: graph scale 2026-07-23 12:30:22 +02:00
Hyblocker 224f9e37fe chore: fix vulkan renderer a bit so that it compiles
need to compare with the original backend to see if there are any other major changes
2026-07-23 12:29:34 +02:00
Hyblocker 04d14954b0 feat: beautify calibration ui a bit 2026-07-23 12:22:41 +02:00
Hyblocker ff41d5e1d4 feat: add icons to tabs 2026-07-23 12:20:36 +02:00
Hyblocker 057e98a194 chore: update license 2026-07-23 12:16:21 +02:00
Hyblocker a8a2aab7c0 chore: make tracking status log every 5s rather than every frame
reduces spam + keeps logs more concise
2026-07-23 12:16:08 +02:00
Hyblocker ecb23b2d17 fix: async base station management
used to block main thread
2026-07-23 12:15:27 +02:00
Hyblocker e321cea6ba feat: add IMGUI_USE_DEBUG_WINDOW 2026-07-23 12:14:16 +02:00
Hyblocker 30f3bd2b70 feat: add icon font support 2026-07-23 12:13:19 +02:00
Hyblocker 45ac0237f5 fix: patch simpleble to ignore characteristics props before sending command
makes base station management actually work cuz base stations lie about their capabilities
2026-07-23 12:12:15 +02:00
Hyblocker 30933b96fd vendor: update imgui 2026-07-23 12:10:50 +02:00
Hyblocker a0853851c2 feat: add support for a few more things on linux 2026-07-19 03:22:20 +02:00
Hyblocker b4afcd56a9 chore: minor improvements to logging 2026-07-19 03:18:55 +02:00
Hyblocker 81c42bdf75 feat: query vsync for prediction later 2026-07-19 03:10:30 +02:00
Hyblocker 2b1ddd2269 feat: auto-detect devices 2026-07-19 03:09:03 +02:00
Hyblocker 00914a7d4e feat: ui changes 2026-07-19 03:08:19 +02:00
Hyblocker 0346c4bfce feat: data metric tracking 2026-07-19 03:07:04 +02:00
Hyblocker 241979fb05 feat: base station management 2026-07-19 03:05:21 +02:00
Hyblocker 602f225e6e chore: update readme 2026-07-19 03:03:04 +02:00
Hyblocker 5df52bf95b chore: translating guidelines 2026-07-19 03:02:51 +02:00
Hyblocker 4986a310ab chore: update year 2026-07-19 03:02:43 +02:00
Hyblocker 2686dbe5e0 chore: add new deps to license 2026-07-19 03:02:35 +02:00
Hyblocker a5c8135a5c fix: reject more bad translation calibrations 2026-07-18 23:15:24 +02:00
Hyblocker ee8c0ad0d0 fix: applying calibrations in bad state 2026-07-18 23:06:51 +02:00
Hyblocker e3d8585895 chore: readd math.pdf 2026-07-15 12:33:17 +02:00
Hyblocker a272846bfd feat: integrate simpleble 2026-07-15 01:12:51 +02:00
Hyblocker ab7ead25e2 chore: cleanup event handling a lot 2026-07-15 01:12:31 +02:00
Hyblocker f620fea10c vendor: add simpleble 2026-07-13 22:49:48 +02:00
Hyblocker 1539be0d80 chore: fix some typos with vk renderer 2026-07-12 16:49:05 +02:00
Hyblocker 0477dadf15 feat(win32): impl some low level apis 2026-07-12 02:01:01 +02:00
Hyblocker 71102ba0a2 fix: dx11 memory leak 2026-07-12 01:57:35 +02:00
Hyblocker 3e69bf63db feat: enable implot 2026-07-11 19:57:34 +02:00
Hyblocker 1f0dc059e4 vendor: add implot 2026-07-11 17:07:01 +02:00
Hyblocker 33e203f116 chore: launchDirInFileBrowser should take std:::filesystem::path not std::string 2026-07-11 13:11:07 +02:00
Hyblocker 6b36a563fe fix: church russian mapping to wrong dialect (???) 2026-07-11 13:09:36 +02:00
Hyblocker c6e9680dcd fix: dead link 2026-07-11 00:43:19 +02:00
Hyblocker 0c29d69e32 fix: missing header for linux 2026-07-11 00:02:27 +02:00
Hyblocker 8fee7ca19e fix: driver pose velocity is in driver space, remove double transform application 2026-07-10 23:47:52 +02:00
Hyblocker 2c584f139a feat: vk renderer minor improvements 2026-07-09 02:12:28 +02:00
Hyblocker aba682f8c4 feat: minor ui changes 2026-07-09 02:12:08 +02:00
Hyblocker 0ec1fa9359 fix: broken apply condition 2026-07-09 02:11:42 +02:00
Hyblocker a422784023 feat: try reconnecting to IPC server to resolve race conditions 2026-07-09 02:11:23 +02:00
Hyblocker 214414faff feat: bold fonts 2026-07-09 02:10:57 +02:00
Hyblocker eb231a72de fix: dont invalidate calibration for a device when in bad state 2026-07-06 23:23:37 +02:00
Hyblocker 11cd795270 fix: require both tracking devices to be active for valid apply 2026-07-06 23:22:41 +02:00
Hyblocker 7902da8259 fix: linux LD_LIBRARY_PATH to include executable dir 2026-07-06 23:21:57 +02:00
Hyblocker f999cd58c8 fix: linux build 2026-07-06 01:32:05 +02:00
Hyblocker 902ddab527 fix: typo 2026-07-06 01:19:32 +02:00
Hyblocker 0f78dd2ce0 fix: cleanup openvr event handling a bit 2026-07-06 01:19:07 +02:00
Hyblocker 06cbabbc4d feat: minor ui improvements 2026-07-06 01:18:56 +02:00
Hyblocker 6f03442c11 fix: relative calibrations active in standard mode when they shouldn't 2026-07-06 01:18:21 +02:00
Hyblocker 343c8c31ac feat: add dx11 and vulkan renderers 2026-07-06 01:17:13 +02:00
Hyblocker 4f31295d72 chore: some more logging, remove double parantheses 2026-07-02 14:18:02 +02:00
Hyblocker 8b0b1a1679 vendor: add vulkan deps 2026-07-02 14:17:04 +02:00
Hyblocker 8a1b3a2332 fix: ipc fail not presenting error in ui 2026-06-27 17:26:59 +02:00
Hyblocker 9b0d5551dd feat: bad larping?? 2026-06-25 11:37:09 +02:00
Hyblocker 1e8d7eccea feat: initialise values 2026-06-25 11:27:39 +02:00
Hyblocker 6f75e51f46 chore: remove legacy blend transform 2026-06-25 11:27:23 +02:00
Hyblocker 4f108856a8 chore: keep internal calibration in eigen form in driver 2026-06-25 11:06:08 +02:00
Hyblocker b8b9630cad chore: move DriverPose to Eigen conversion to func 2026-06-25 01:22:40 +02:00
Hyblocker 51482132a2 fix: force a calibration in standard mode 2026-06-25 01:21:53 +02:00
Hyblocker 4a5b76b7c9 feat: rework ui a lot 2026-06-25 01:21:37 +02:00
Hyblocker a37e6c8dd0 fix: relative calibration error testing 2026-06-25 00:23:02 +02:00
Hyblocker e42b2d5ee6 chore: hide gcc arm64 note
thanks eigen for being well designed
2026-06-24 02:34:31 +02:00
Hyblocker a0f7e13586 chore: will it fix ci archiving? 2026-06-22 22:50:18 +02:00
Hyblocker 0cc2d4c344 fix: calibration deadlock 2026-06-22 20:47:40 +02:00
Hyblocker f8d048a4e9 feat: add motion vectors to ui 2026-06-22 20:47:27 +02:00
Hyblocker 1b859befac fix: glfw minimise fix for linux 2026-06-22 20:46:13 +02:00
Hyblocker 44ece3d9ab feat: relative pose caching 2026-06-22 20:46:02 +02:00
Hyblocker 7bcea0035e fix: oops 2026-06-22 20:37:39 +02:00
Hyblocker 81839a983c chore: more logging cuz i wanna see wtf is going wrong 2026-06-22 20:37:23 +02:00
Hyblocker 6a35ffaf6d chore: note 2026-06-22 20:37:12 +02:00
Hyblocker ecde0335d4 feat: make motion vectors optional 2026-06-22 20:37:00 +02:00
Hyblocker 0fa961ce3e chore: cleanup config data 2026-06-22 20:34:48 +02:00
Hyblocker 5a334e6b55 chore: pin ubuntu version 2026-06-22 18:09:58 +02:00
Hyblocker 4d68859d54 fix: arm64 ci 2026-06-22 18:09:33 +02:00
Hyblocker d1959ca22d fix: bad fallthrough 2026-06-20 02:32:17 +02:00
Hyblocker 9c6daad6f7 fix: hopefully linux builds copy the overlay too now 2026-06-19 01:13:08 +02:00
Hyblocker a7e0384c9e chore: update workflow stuff 2026-06-19 00:55:26 +02:00
Hyblocker 178bd98517 fix: includes 2026-06-19 00:47:38 +02:00
Hyblocker bfd4061f22 fix: ci on nova 2026-06-19 00:38:54 +02:00
Hyblocker 4698a18158 fix: ImGui SteamVR interop crashes
idk i forgor to test lmao
2026-06-19 00:26:18 +02:00
Hyblocker 9097a249f9 fix: calibrations not applying immediately on load 2026-06-19 00:17:52 +02:00
Hyblocker f0a0584bcf fix: log spam 2026-06-19 00:17:31 +02:00
Hyblocker 272859db20 feat: minor ui improvements 2026-06-19 00:17:15 +02:00
Hyblocker cf55671d75 feat: reject bad translation calibrations 2026-06-19 00:16:03 +02:00
Hyblocker cf28e166a4 fix: HOLY SHIT WE HAVE RELATIVE CALIBRATIONS 2026-06-19 00:15:05 +02:00
Hyblocker d7e18df9d3 chore: remove redundant function 2026-06-19 00:13:03 +02:00
Hyblocker 8855ea8118 fix: set errno to 0 before usage 2026-06-15 23:21:56 +02:00
Hyblocker a817fa1357 chore: better logging 2026-06-15 23:21:05 +02:00
Hyblocker e5de8af3d0 chore: update 2026-06-12 15:07:49 +02:00
Hyblocker 4d41db7297 feat: i think this ci should work 2026-06-12 15:07:37 +02:00
Hyblocker be4457a361 chore: minor improvements 2026-06-11 00:53:14 +02:00
Hyblocker 272d7813bd feat: scale calibration core 2026-06-11 00:52:51 +02:00
Hyblocker 6697328bef fix: improve relative calibration 2026-06-11 00:37:40 +02:00
Hyblocker 809a20e127 chore: simplify protocol 2026-06-11 00:36:36 +02:00
Hyblocker b614dd7f43 fix: incorrect executable dir on linux 2026-06-11 00:34:50 +02:00
Hyblocker fceb192d6f fix: remove prefix from linux driver binaries 2026-06-11 00:34:14 +02:00
Hyblocker 3998ca4d85 feat: pre-alloc deltas 2026-06-10 16:48:26 +02:00
Hyblocker 57f25187b8 chore: more consistent naming 2026-06-10 16:48:17 +02:00
Hyblocker 62f68516e8 fix: aarch64 cmake issues 2026-06-10 16:47:48 +02:00
Hyblocker 1fc7d26deb feat: axis variance and rejection 2026-06-10 00:25:43 +02:00
Hyblocker 51d739ead2 chore: github issue template 2026-06-09 22:48:57 +02:00
Hyblocker 44a005c8b6 fix: typo 2026-06-08 01:39:38 +02:00
Hyblocker d96bbd45da fix: clear sample history on reset 2026-06-07 19:17:46 +02:00
Hyblocker 1e11a23f82 feat: keep track of 5x calibration sample count samples for error metrics
this should minimise false positives due to lack of movement by keeping a larger window of trustworthy calibrations we can rely on
2026-06-07 19:09:23 +02:00
Hyblocker 92355bb1a5 fix: increase min delta calibration threshold 2026-06-07 19:07:52 +02:00
Hyblocker 33d1d4d99a fix: locales not loading properly 2026-06-07 01:49:33 +02:00
Hyblocker 517079c90a fix: include min delta samples required for valid calibration 2026-06-07 01:32:29 +02:00
Hyblocker f847e89c80 chore: fix impl include 2026-06-07 01:30:44 +02:00
Hyblocker 076446e4b8 fix: re-compute rms error every calibration attempt
accounts for hmd drifting + recentering
2026-06-02 23:57:43 +02:00
Hyblocker cd53f96c35 chore: move constants to header 2026-06-02 23:14:22 +02:00
Hyblocker 4b83887036 feat: rms error validation 2026-06-02 00:05:05 +02:00
Hyblocker cce2cb8215 fix: continuous hide hmd tracker 2026-05-31 23:12:42 +02:00
Hyblocker 0dec2960e6 feat: minor build system consistency improvements 2026-05-29 12:51:10 +02:00
Hyblocker 706f08aca6 feat: auto determine system locale on linux 2026-05-29 12:50:49 +02:00
Hyblocker 512561baa0 fix: utf8 stuff (pending thorough investigation) 2026-05-29 12:50:30 +02:00
Hyblocker 9998022fb5 feat: begin work for linux compile 2026-05-29 01:11:17 +02:00
Hyblocker 1420bec65b fix: broken calibrations 2026-05-29 00:25:10 +02:00
Hyblocker 4dbd40f9e8 fix: fat fingered the calibration stuff lmao 2026-05-28 11:52:13 +02:00
Hyblocker b9df43240c chore: use calibration error
to be used for UI later to aid in self troubleshooting
2026-05-27 12:35:06 +02:00
Hyblocker fbc20f0bc4 fix: logging not working as expected some times 2026-05-27 12:10:14 +02:00
Hyblocker c334ca7aac feat: add hungraian (thanks Lenr) 2026-05-27 11:54:14 +02:00
Hyblocker fb4b0237be fix: reject calibrations with poor sample variance 2026-05-27 11:53:07 +02:00
Hyblocker 93052bd226 feat: idk try relative calibration shit idfk man 2026-05-26 23:09:16 +02:00
Hyblocker e164881182 feat: new button style 2026-05-26 23:08:48 +02:00
Hyblocker a11329dc16 feat: apply last calibration on load 2026-05-26 23:08:23 +02:00
Hyblocker 2f0a642bfe fix: padding issue 2026-05-26 23:02:59 +02:00
Hyblocker 3c282ccfa2 chore: remove lang stubs; better to hide from the ui tbh 2026-05-26 20:13:07 +02:00
Hyblocker f2b76b079e fix: copy paste fat finger final boss 2026-05-26 19:00:32 +02:00
Hyblocker fb8c500493 feat: add french (thanks Hash) 2026-05-26 18:57:11 +02:00
Hyblocker a7fddbfd96 chore: lang stubs 2026-05-26 18:56:59 +02:00
Hyblocker afa11c919d fix: more work on relative calibrations 2026-05-26 01:34:45 +02:00
Hyblocker 0d210661b9 fix: progress sampling issue 2026-05-26 01:34:29 +02:00
Hyblocker 4c3abee063 chore: ui quirks 2026-05-26 01:33:43 +02:00
Hyblocker e88acdea1d feat: continuous calibraton 2026-05-26 01:33:28 +02:00
Hyblocker 21cc563e7f fix: use classic verbose ifdef behaviour for release 2026-05-25 00:24:51 +02:00
Hyblocker 93309c322b feat: add better device class handling 2026-05-25 00:08:56 +02:00
Hyblocker ba6fc87960 feat: more logging 2026-05-25 00:08:28 +02:00
Hyblocker f883bd9acf chore: inconsistency 2026-05-25 00:07:21 +02:00
Hyblocker 047f49d162 feat: add locale support to ui 2026-05-25 00:07:09 +02:00
Hyblocker 465cbab2f9 feat: add Poppins and M+1p fonts 2026-05-25 00:06:15 +02:00
Hyblocker afac7851dd chore: refactor + handle state slightly better 2026-05-24 21:46:41 +02:00
Hyblocker 303831e12d chore: disable ctrl + tab nav more 2026-05-24 21:46:13 +02:00
Hyblocker f8bc3bb288 fix: warning 2026-05-24 21:46:01 +02:00
Hyblocker d9c98f3783 fix(temp): add some more logging for apply; should help identify why its fucked 2026-05-24 21:45:48 +02:00
Hyblocker 362d51dac3 fix: i am very stupid the ipc was overwriting bruhhhhhhhhhh 2026-05-24 21:44:58 +02:00
Hyblocker 2bc77f3645 feat: describe (poorly) what to do to calibrate 2026-05-21 11:31:32 +02:00
Hyblocker 02277d479c chore: improve logging around IPC 2026-05-21 11:31:11 +02:00
Hyblocker b1673443c9 fix: remap speed values to new values when upgrading legacy config 2026-05-21 11:25:44 +02:00
Hyblocker 0246ac474c fix: changing hmd tracking system may result in inapplicable calibration applying when it shouldnt 2026-05-18 21:39:12 +02:00
Hyblocker eb09902b09 chore: formatting 2026-05-18 21:37:19 +02:00
Hyblocker dc8ede1dcd fix: device poses may be broken until initial calibration is applied 2026-05-18 21:37:10 +02:00
Hyblocker 07e74ad3a7 fix: last device should now be garuanteed to have a calibration applied eventually 2026-05-18 21:36:32 +02:00
Hyblocker 3d2cf4a91c chore: more logging 2026-05-18 21:35:55 +02:00
Hyblocker 0fa0e65b68 chore: consistent fallback strings for enums 2026-05-18 21:35:05 +02:00
Hyblocker 0a96fdbc52 feat: log all devices during calib flow 2026-05-18 21:31:28 +02:00
Hyblocker e8d414e0fa chore: bump version 2026-05-11 20:45:14 +02:00
Hyblocker 265938b8cf feat: relative calibration stub (BUGGY! DO NOT USE!) 2026-05-11 20:44:55 +02:00
Hyblocker 32757b5553 chore: steam frame friendly strings 2026-05-11 20:44:28 +02:00
Hyblocker 7b7de12370 fix: calibration logic should be more robust 2026-05-11 20:44:07 +02:00
Hyblocker 50ad087b7d feat: apply calibration on any useful device event 2026-05-11 20:43:03 +02:00
Hyblocker 6018ac015f fix: fix device selection being inconsistent 2026-05-11 20:42:33 +02:00
Hyblocker d565a9022a feat: add config writing / saving 2026-05-11 20:41:32 +02:00
Hyblocker ed5c52c695 fix: add another check for VD - SN is hard coded 2026-05-11 20:39:43 +02:00
Hyblocker a83a738613 feat: disable ignoring tracking systems in debug builds 2026-05-11 20:38:38 +02:00
Hyblocker 748e92af0d chore: describe pragma warning 4668 2026-05-11 20:38:13 +02:00
Hyblocker 31da7d0a26 fix: compilation error 2026-05-11 20:38:00 +02:00
Hyblocker 9d7e36295c feat: add groundwork for relative calibrations 2026-05-11 20:36:35 +02:00
Hyblocker 93c51d074b fix: fix clearing not working correctly 2026-05-11 20:33:01 +02:00
Hyblocker 002de60d8a fix: crash when not using VD hmd 2026-05-10 23:01:48 +02:00
Hyblocker 043920647d fix: make windows manifest valid 2026-04-10 14:12:37 +02:00
Hyblocker 3114bf2865 feat: verbose logs 2026-04-10 14:12:14 +02:00
Hyblocker ea6bdaf007 fix: rheading issue for vd querying 2026-04-10 14:11:54 +02:00
Hyblocker 65730e976c fix: driver couldn't update poses 2026-04-10 14:11:40 +02:00
Hyblocker 24a834d4ea chore: config can store multiple calibrations 2026-04-10 14:11:23 +02:00
Hyblocker 1235573ef1 fix: typo in logs 2026-04-10 14:11:04 +02:00
Hyblocker e5ae464968 feat: dpi aware manifest 2026-04-08 12:44:16 +02:00
Hyblocker 54ab63564a feat: merge calibration stages into single step 2026-04-08 12:28:04 +02:00
Hyblocker 5565fef975 chore: actually calibration start should clear samples lol 2026-04-04 20:05:42 +02:00
Hyblocker ebe6ba4867 chore: make start clear a calibration before running 2026-04-04 20:03:41 +02:00
Hyblocker e6a4099164 fix: simplify dwm dark mode 2026-04-04 20:02:41 +02:00
Hyblocker 3b20345994 fix: calibration translation was fucked 2026-04-02 01:21:58 +02:00
Hyblocker ce0472ff0c fix: calibration crashes 2026-04-01 23:28:19 +02:00
Hyblocker 87414e6e62 feat: vd detection 2026-04-01 01:16:49 +02:00
Hyblocker 6b0c62bc3e chore: cleanup 2026-04-01 01:15:53 +02:00
Hyblocker 1a8350d899 fix: default values 2026-04-01 01:13:57 +02:00
Hyblocker 85de768e72 fix: imgui assert 2026-04-01 01:13:44 +02:00
Hyblocker b45d06618b feat: calibration progress 2026-04-01 01:13:33 +02:00
Hyblocker 37da8fe7ed fix: modernise tracking system selection code 2026-03-31 20:55:53 +02:00
Hyblocker 837c4f23e1 fix: better linux ifdef 2026-03-31 20:54:06 +02:00
Hyblocker 78b804ec54 fix: make config point to versioned data 2026-03-31 20:53:53 +02:00
Hyblocker 5c57705a40 chore: more cross platform prep 2026-03-27 20:12:43 +01:00
Hyblocker e14d78a64a chore: fix some minor things 2026-03-27 18:36:57 +01:00
Hyblocker a307705d93 feat: make it usable i think 2026-03-27 01:24:43 +01:00
Hyblocker f37439148b feat: implement some ui 2026-03-21 23:54:26 +01:00
Hyblocker beb52ec9c2 fix: enforce config version 2026-03-21 20:08:44 +01:00
Hyblocker 4496969b50 feat: it boots 2026-03-10 00:17:18 +01:00
Hyblocker 1765712b3b fix: correct some stuff for crashes 2026-03-10 00:11:39 +01:00
Hyblocker 116c4e40f2 chore: rewrite hooks to handle malformed data across ABI boundary 2026-03-10 00:04:52 +01:00
Hyblocker 1d6867736a fix: incorrect langs dir 2026-03-10 00:03:54 +01:00
Hyblocker 26c2ce6779 fix: incorrect logger string in overlay and driver 2026-03-10 00:03:36 +01:00
Hyblocker 01487238b9 fix: driver crash 2026-03-10 00:03:21 +01:00
Hyblocker cb5bc28316 fix: forgot to add breaks for steamvr logging 2026-03-10 00:02:47 +01:00
Hyblocker 6e0ca20bda fix: msvc fixes 2026-03-01 20:08:19 +01:00
Hyblocker 33d2baae9c fix: warnings in imgui locale handling 2026-02-26 11:42:44 +01:00
Hyblocker 9c22d7330d fix: cast size to DWORD to supress warning in ipc subsystem 2026-02-26 11:42:33 +01:00
Hyblocker 5cfbf61305 fix: adjust build stuff to work on msvc and clang. need to test gcc later 2026-02-26 11:42:01 +01:00
Hyblocker 40130a21a5 chore: support vs2022 open dir cmake mode 2026-02-26 11:40:57 +01:00
Hyblocker 835b53a661 fix: why were these utf16-LE??? 2026-02-26 11:40:26 +01:00
Hyblocker 18a90052e8 chore: make warning guards generalise better 2026-02-22 17:21:06 +01:00
Hyblocker 44dc8893f2 feat: consistency improvements 2026-02-19 09:53:07 +01:00
Hyblocker 10db4155a0 chore: minor fixes 2025-12-16 22:11:31 +01:00
Hyblocker 6625783fd9 chore: glz::json_t is deprecated, use glz::generic 2025-12-15 00:05:55 +01:00
Hyblocker 5ecf086141 feat: write some steamvr overlay stuff
its windows specific rn :(
2025-12-15 00:05:39 +01:00
Hyblocker 1cdbcefc14 feat: more calibration work 2025-12-15 00:03:37 +01:00
Hyblocker 3d8ce214c8 feat: add calibration logger 2025-12-15 00:03:13 +01:00
Hyblocker 0ba44ca476 feat: Initial UI 2025-11-28 11:52:53 +01:00
Hyblocker c1316d0ac4 fix: Move pose updates to calibration 2025-11-28 11:52:47 +01:00
Hyblocker 94e67a708a feat: Begin implementing calibration alg 2025-11-28 11:52:20 +01:00
Hyblocker 24b325ccd9 fix: Improve namespacing 2025-11-28 11:51:19 +01:00
Hyblocker 23fda0c30b chore: Formatting 2025-11-16 20:19:38 +01:00
Hyblocker 46ead1c3c7 fix: Add error handling to driver hooks 2025-11-16 20:18:14 +01:00
Hyblocker fe1a1ae5f1 feat: Initial driver impl done 2025-11-16 20:18:02 +01:00
Hyblocker 872cf88884 chore: Update driver entrypoint 2025-11-16 20:17:31 +01:00
Hyblocker e806c21d13 feat: VRmath 2025-11-16 20:17:18 +01:00
Hyblocker 2e5a8858b3 feat: Quirk handling stub 2025-11-16 20:17:07 +01:00
Hyblocker 13b243e0fe feat: Support json with comments 2025-11-16 12:13:56 +01:00
Hyblocker f7d0f5fde4 feat: Load legacy config from registry 2025-11-16 12:11:49 +01:00
Hyblocker 2fb7358c88 chore: Simplify logger defs 2025-11-16 12:11:26 +01:00
Hyblocker 7810c4d958 chore: Make codebase a bit more consistent 2025-11-16 12:02:46 +01:00
Hyblocker c4a11171c0 vendor: Update dear ImGUI 2025-11-16 12:02:12 +01:00
Hyblocker 0a552ee46f chore: Make booleans more portable 2025-11-15 16:37:01 +01:00
Hyblocker 7de3891015 feat: The trackers get a little quirky at night 2025-11-15 16:35:55 +01:00
Hyblocker aab35d46ea chore: Add en_GB lang
Adds support for comments in lang files
2025-11-11 15:56:43 +01:00
Hyblocker dc09c5bc9e fix: Minor fixes 2025-11-11 15:52:52 +01:00
Hyblocker 465d48db99 feat: VR state work 2025-11-09 12:45:47 +01:00
Hyblocker ffdc47e39f feat: Config 2025-11-09 12:45:35 +01:00
Hyblocker c2516ed007 fix: Possible SteamVR crash (invalid tracker idx) 2025-11-09 12:45:25 +01:00
Hyblocker 3ab2ab8eab feat: Begin calibration 2025-11-09 12:45:05 +01:00
Hyblocker cc78b51f47 fix: Correct some stuff 2025-11-09 12:44:50 +01:00
Hyblocker e7f2d281b5 vendor: Add eigen 2025-11-09 12:43:47 +01:00
Hyblocker 704f3737a4 feat: a bunch of stuff 2025-10-28 00:14:01 +01:00
Hyblocker ce5e8d1604 vendor: Remove simdjson
we glazing it sigmas
2025-10-27 21:42:38 +01:00
Hyblocker 9b311dca27 chore: C++23 baby 2025-10-27 21:39:51 +01:00
Hyblocker 80c870b4c8 vendor: Add glaze 2025-10-27 21:39:39 +01:00
Hyblocker 9d43782d38 (chore): Begin work on calibration core algorithm 2025-10-25 18:03:44 +02:00
Hyblocker 5a6720edb3 (feat): Minor server IPC things 2025-10-25 18:03:24 +02:00
Hyblocker d5095fa98c (feat): VR system polling 2025-10-25 18:03:11 +02:00
Hyblocker 14ef99cd71 (feat): Begin working on server 2025-10-23 09:42:32 +02:00
Hyblocker 70da588273 (chore): Move UI code to user_interface 2025-10-23 09:42:13 +02:00
Hyblocker 2f4d048fd9 (feat): More logging 2025-10-23 09:41:26 +02:00
Hyblocker 09e6b2d45f (feat): Thread name 2025-10-23 09:40:13 +02:00
Hyblocker d0c2698fda (fix): Memory leak 2025-10-23 09:39:50 +02:00
Hyblocker d5cc2452d2 (feat): Add localisation (win32) 2025-10-23 09:39:35 +02:00
Hyblocker c7afa94b08 (vendor): IPC client 2025-10-22 23:28:57 +02:00
Hyblocker 78e8348c00 (vendor): Add simdjson 2025-10-22 23:28:41 +02:00
Hyblocker cdbd393db9 (feat): Steam single instance handling 2025-09-02 23:18:27 +02:00
Hyblocker 5ebda87fc8 (vendor):Update glad 2025-09-02 23:17:59 +02:00
Hyblocker 07afe73512 (vendor): Add stb image 2025-09-02 23:17:50 +02:00
Hyblocker 49602cedde (chore): Abstract away window creation 2025-08-29 01:03:41 +02:00
Hyblocker 0f01a0d1f9 (vendor): Move glad to folder 2025-08-29 01:03:29 +02:00
Hyblocker d53a1ce7a1 (chore): spaces 2025-08-29 01:03:14 +02:00
Hyblocker bbe85304da (feat): initial window 2025-08-27 01:49:56 +02:00
Hyblocker 8bbfe9b6d8 (vendor): Add glad 2025-08-27 01:49:36 +02:00
Hyblocker 66fc532d7f (feat): Impl basic overlay 2025-08-27 01:49:22 +02:00
Hyblocker 4a841b1594 (feat): Implement skeleton driver and hooks 2025-08-27 01:29:39 +02:00
Hyblocker f81e2ad7a0 (vendor): Ipc lib 2025-08-27 01:29:09 +02:00
Hyblocker 13a0978ffb (feat): Define protocol v1 2025-08-27 01:29:01 +02:00
Hyblocker 35d83f2380 (feat): Logging 2025-08-27 01:28:50 +02:00
Hyblocker 65f786504b (vendor): Update SteamVR 2025-08-27 01:27:51 +02:00
Hyblocker 70563be965 (chore): C++20 2025-08-27 01:27:37 +02:00
Hyblocker 26687734b5 (chore): Change generator to clang 2025-08-27 01:26:57 +02:00
Hyblocker 220ac18ce1 (vendor): Add quill 2025-08-27 01:26:46 +02:00
Hyblocker ad4fcb7814 (vendor): Add fmt 2025-08-09 23:14:33 +02:00
Hyblocker 33a086637e (chore): nova rewrite initial commit 2025-08-09 14:35:44 +02:00
2414 changed files with 457094 additions and 213338 deletions

No files matched your search

+216
View File
@@ -0,0 +1,216 @@
---
Language: Cpp
BasedOnStyle: WebKit
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: DontAlign
AlignTrailingComments:
Kind: Never
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: All
BreakBeforeBraces: WebKit
BreakBeforeConceptDeclarations: Always
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: ^"(llvm|llvm-c|clang|clang-c)/
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: ^(<|"(gtest|gmock|isl|json)/)
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: .*
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: (Test)?$
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
LambdaBodyIndentation: Signature
LineEnding: CRLF
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseInsensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDeclarationName: false
AfterFunctionDefinitionName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: true
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
+2
View File
@@ -0,0 +1,2 @@
CompileFlags:
CompilationDatabase: "bin"
+10
View File
@@ -0,0 +1,10 @@
root = true
[*]
end_of_line = crlf
insert_final_newline = true
trim_trailing_whitespace = true
[*.c,*.cc,*.cpp,*.hpp,*.h]
indent_style = space
indent_size = 4
+7
View File
@@ -0,0 +1,7 @@
* text=auto eol=crlf
*.cpp text eol=crlf
*.cc text eol=crlf
*.c text eol=crlf
*.h text eol=crlf
*.hpp text eol=crlf
*.sh text eol=lf
+2
View File
@@ -0,0 +1,2 @@
ko_fi: hyblocker
patreon: hekky
+48 -4
View File
@@ -1,9 +1,53 @@
---
name: Bug report
about: Something is broken
title: ''
labels: bug
assignees: ''
description: Report an issue with Space Calibrator.
title: '[Bug]: '
labels:
- bug
body:
- type: textarea
attributes:
label: Description
description: A clear and short description of the problem
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: >-
Provide clear steps to trigger the issue if possible. If you're unsure,
describe what you did prior to it happening and what you tried doing to
solve it.
- type: textarea
attributes:
label: Expected behaviour
description: Describe what you expected to happen
- type: input
attributes:
label: VR headset
description: >-
The VR headset you are using, e.g. Meta Quest 3, Pico 4, Steam Frame,
Samsung Galaxy XR, etc.
placeholder: Meta Quest 3
- type: input
attributes:
label: Connection method
description: >-
How you are connecting your VR headset to your computer, eg Virtual
Desktop, Steam Link, Air Link, Oculus Link Cable.
placeholder: Steam Link
- type: input
attributes:
label: What are you calibrating with?
description: List the trackers you are using here if you're unsure.
placeholder: Vive Tracker 3.0
- type: markdown
attributes:
value: >-
You are advised to also provide logs to help make it easier for me to
troubleshoot your issues. You can go to Space Calibrator -> Settings and
click the [View logs] button to open the folder. Please attach
`log_overlay_latest.log` and `log_driver_latest.log`. Thank you!
---
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

+53
View File
@@ -0,0 +1,53 @@
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
workflow_dispatch:
env:
BUILD_TYPE: RelWithDebInfo
jobs:
build:
name: Build (${{ matrix.name }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: windows-x64
os: windows-2025-vs2026
artifact-name: space-calibrator-win64
- name: linux-x64
os: ubuntu-24.04
container: registry.gitlab.steamos.cloud/steamrt/steamrt4/sdk:latest
artifact-name: space-calibrator-linux-x64
- name: linux-arm64
os: ubuntu-24.04-arm
container: registry.gitlab.steamos.cloud/steamrt/steamrt4/sdk/arm64:latest
artifact-name: space-calibrator-linux-arm64
container: ${{ matrix.container }}
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Configure CMake
run: cmake -B ./build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
run: cmake --build ./build --config ${{env.BUILD_TYPE}}
- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact-name }}
path: |
./build/artifacts/
./build/driver_01spacecalibrator/
+6 -6
View File
@@ -4,9 +4,9 @@ ipch
*.opendb
*.aps
*.user
/OpenVR-SpaceCalibrator/x64/
/OpenVR-SpaceCalibratorDriver/x64/
/x64/
/lib/boost*
/install/OpenVR-SpaceCalibrator.exe
lib/Release/
.code
/install/*.exe
[bB]in
[bB]in_linux64
[bB]in_linuxaarch64
[oO]ut
+39 -3
View File
@@ -1,3 +1,39 @@
[submodule "lib/minhook"]
path = lib/minhook
url = https://github.com/bdunderscore/minhook.git
[submodule "vendor/imgui"]
path = vendor/imgui
url = https://github.com/ocornut/imgui
[submodule "vendor/openvr"]
path = vendor/openvr
url = https://github.com/ValveSoftware/openvr
[submodule "vendor/glfw"]
path = vendor/glfw
url = https://github.com/glfw/glfw
[submodule "vendor/minhook"]
path = vendor/minhook
url = https://github.com/TsudaKageyu/minhook
[submodule "vendor/fmt"]
path = vendor/fmt
url = https://github.com/fmtlib/fmt
[submodule "vendor/quill"]
path = vendor/quill
url = https://github.com/odygrd/quill
[submodule "vendor/glaze"]
path = vendor/glaze
url = https://github.com/stephenberry/glaze.git
[submodule "vendor/volk"]
path = vendor/volk
url = https://github.com/zeux/volk.git
[submodule "vendor/Vulkan-Headers"]
path = vendor/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "vendor/implot"]
path = vendor/implot
url = https://github.com/epezent/implot
[submodule "vendor/simpleble"]
path = vendor/simpleble
url = https://github.com/simpleble/simpleble
[submodule "vendor/implot3d"]
path = vendor/implot3d
url = git@github.com:brenocq/implot3d.git
[submodule "vendor/zstd"]
path = vendor/zstd
url = https://github.com/facebook/zstd.git
+91
View File
@@ -0,0 +1,91 @@
cmake_minimum_required (VERSION 3.8)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Build directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts)
# Function because lazy
function(adjust_bin_paths lib)
set_target_properties(${lib}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts
)
endfunction()
# windows prefers win8.1 sdk instead of win 10 sdk for some god forsaken reason
if(WIN32)
set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "Forcing Win10 SDK" FORCE)
else()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
project("SpaceCalibrator")
# 3rd party stuff
add_subdirectory ("vendor")
# warnings for our code
if ("${CMAKE_CXX_COMPILER}" MATCHES "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
# add_compile_options(-Werror=return-type -Wall -Wextra -Wpedantic /MP)
add_compile_options(-Werror=return-type)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
# add_compile_options(-Werror=return-type -Wall -Wextra -Wpedantic)
add_compile_options(-Werror=return-type -Wno-psabi)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Intel")
# add_compile_options(-Werror=return-type -Wall -Wextra -Wpedantic)
add_compile_options(/Qdiag-error-return)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
add_compile_options(
# treat warnings as errors
/we4715 # not all control paths return a value
/we4828 # disallow invalid characters
# prinf-like functions: format mismatch
/we4473 # : not enough arguments passed for format string
/we4474 # : too many arguments passed for format string
/we4475 # : length modifier cannot be used with type field character in format specifier
/we4476 # : unknown type field character in format specifier
/we4477 # : format string requires an argument of type , but variadic argument has type
/we4478 # : positional and non-positional placeholders cannot be mixed in the same format string
/we4775 # nonstandard extension used in format string of function
/we4776 # % is not allowed in the format string of function
/we4777 # : format string requires an argument of type , but variadic argument has type
/we4778 # : unterminated format string
# macro arg mismatch
/we4002 # too many actual parameters for macro 'identifier'
/we4003 # not enough actual parameters for macro 'identifier'
/Zc:threadSafeInit- # https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics
/MP # multiprocessor compilation
/utf-8 # utf-8 source & exec
/GF) # eliminate duplicate strings
endif()
# Match SteamVR build flags on MSVC
if(MSVC)
add_compile_options(/guard:cf /GS)
add_link_options("/GUARD:CF")
add_compile_options($<$<CONFIG:Release>:/Ot> $<$<CONFIG:Release>:/Oi> $<$<CONFIG:Release>:/Gy>)
endif()
# force relative RPATH for linux so that shared objects may be found relative to the binary (fixes Steam launch)
if (UNIX AND NOT APPLE)
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH "")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
SET(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
endif()
# add editorconfig to vcxprojs
if (WIN32)
add_custom_target(FormattingRules SOURCES "${CMAKE_SOURCE_DIR}/.editorconfig" "${CMAKE_SOURCE_DIR}/.clang-format")
set_property(TARGET FormattingRules PROPERTY FOLDER "Solution Items")
endif()
# Include project
add_subdirectory ("src")
+48
View File
@@ -0,0 +1,48 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Clang-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Clang-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}
+2
View File
@@ -0,0 +1,2 @@
@ECHO OFF
cmake -G "Visual Studio 17 2022" -A x64 -B bin -S .
+301 -25
View File
@@ -1,7 +1,8 @@
== OpenVR-SpaceCalibrator ==
== Space Calibrator ==
MIT License
Copyright (c) 2023-2026 Hyblocker and other contributors
Copyright (c) 2020-2022 Justin Li and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -112,7 +113,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The MIT License (MIT)
Copyright (c) 2014-2022 Omar Cornut
Copyright (c) 2014-2026 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -136,7 +137,8 @@ SOFTWARE.
MIT License
Copyright (c) 2020 Evan Pezent
Copyright (c) 2020-2024 Evan Pezent
Copyright (c) 2025-2026 Breno Cunha Queiroz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -159,7 +161,8 @@ SOFTWARE.
== glfw ==
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org>
Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -180,31 +183,71 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source
distribution.
== gl3w ==
== glad ==
The glad source code:
The MIT License (MIT)
Copyright (c) 2013-2022 David Herberth
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This is free and unencumbered software released into the public domain.
The Khronos Specifications:
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
Copyright (c) 2013-2020 The Khronos Group Inc.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The EGL Specification and various headers:
Copyright (c) 2007-2016 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Materials.
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
== Eigen ==
@@ -585,4 +628,237 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
defined by the Mozilla Public License, v. 2.0.
== SimpleBLE ==
Official builds include SimpleBLE under a commercial license granted to Hyblocker. That license doesn’t
cover forks. SimpleBLE itself is under BSL 1.1 — use that (or get your own commercial license) if you
build/fork from source.
# SimpleBLE Commercial License Agreement
Please [email us](mailto:contact@simpleble.org) or [leave us a message on our website](https://www.simpleble.org/contact?utm_source=github&utm_medium=referral&utm_campaign=simpleble_license) if you would like to purchase a commercial license or would like more information.
-------------------------------------------------------------------------------
# Business Source License 1.1
License text copyright © 2017 MariaDB Corporation Ab, All Rights Reserved. "Business Source License"
is a trademark of MariaDB Corporation Ab.
## Terms
The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and
make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above,
permitting limited production use.
Effective on the Change Date, or the fourth anniversary of the first publicly available distribution
of a specific version of the Licensed Work under this License, whichever comes first, the Licensor
hereby grants you rights under the terms of the Change License, and the rights granted in the
paragraph above terminate.
If your use of the Licensed Work does not comply with the requirements currently in effect as
described in this License, you must purchase a commercial license from the Licensor, its affiliated
entities, or authorized resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are
subject to this License. This License applies separately for each version of the Licensed Work and
the Change Date may vary for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy of the Licensed Work.
If you receive the Licensed Work in original or modified form from a third party, the terms and
conditions set forth in this License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically terminate your rights
under this License for the current and all other versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of Licensor or its affiliates
(provided that you may use a trademark or logo of Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN "AS IS" BASIS. LICENSOR
HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION)
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
== glaze ==
MIT License
Copyright (c) 2019 - present, Stephen Berry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
== quill ==
MIT License
Copyright (c) 2020 - present, Odysseas Georgoudis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
== fmt ==
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
== volk ==
Copyright (c) 2018-2026 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
== Vulkan-Headers ==
MIT License
Copyright (c) 2015-2023 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
== IconFontCppHeaders ==
Copyright (c) 2017 Juliette Foucaut and Doug Binks
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
== implot3d ==
MIT License
Copyright (c) 2024-2026 Breno Cunha Queiroz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
== zstd ==
BSD License
For Zstandard software
Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook, nor Meta, nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then
exec "$SCRIPT_DIR/SpaceCalibratorx64" "$@"
elif [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
if [ -e "$SCRIPT_DIR/SpaceCalibratorArm64" ]; then
exec "$SCRIPT_DIR/SpaceCalibratorArm64" "$@"
else
echo "SpaceCalibrator: aarch64 binary missing, falling back to x64..."
exec "$SCRIPT_DIR/SpaceCalibratorx64" "$@"
fi
else
echo "SpaceCalibrator: Unsupported architecture $ARCH"
exit 1
fi
+1
View File
@@ -0,0 +1 @@
You may find the source code of this program at https://github.com/hyblocker/OpenVR-SpaceCalibrator . You should also have a LICENSE file alongside this one.
-54
View File
@@ -1,54 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32819.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenVR-SpaceCalibrator", "OpenVR-SpaceCalibrator\OpenVR-SpaceCalibrator.vcxproj", "{63BAE169-D595-4464-8E7E-50D6FD379348}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenVR-SpaceCalibratorDriver", "OpenVR-SpaceCalibratorDriver\OpenVR-SpaceCalibratorDriver.vcxproj", "{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}"
ProjectSection(ProjectDependencies) = postProject
{F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {F142A341-5EE0-442D-A15F-98AE9B48DBAE}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "lib\minhook\build\VC16\libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63BAE169-D595-4464-8E7E-50D6FD379348}.Debug|x64.ActiveCfg = Debug|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Debug|x64.Build.0 = Debug|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Debug|x86.ActiveCfg = Debug|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Debug|x86.Build.0 = Debug|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Release|x64.ActiveCfg = Release|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Release|x64.Build.0 = Release|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Release|x86.ActiveCfg = Release|x64
{63BAE169-D595-4464-8E7E-50D6FD379348}.Release|x86.Build.0 = Release|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Debug|x64.ActiveCfg = Debug|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Debug|x64.Build.0 = Debug|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Debug|x86.ActiveCfg = Debug|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Debug|x86.Build.0 = Debug|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Release|x64.ActiveCfg = Release|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Release|x64.Build.0 = Release|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Release|x86.ActiveCfg = Release|x64
{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}.Release|x86.Build.0 = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.ActiveCfg = Debug|Win32
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.Build.0 = Debug|Win32
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.ActiveCfg = Release|Win32
{F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BCE727E8-4604-4D4F-B5FF-321A7D7868D6}
EndGlobalSection
EndGlobal
-552
View File
@@ -1,552 +0,0 @@
#include "stdafx.h"
#include "Calibration.h"
#include "CalibrationMetrics.h"
#include "Configuration.h"
#include "IPCClient.h"
#include "CalibrationCalc.h"
#include "VRState.h"
#include <string>
#include <vector>
#include <iostream>
#include <Eigen/Dense>
inline vr::HmdQuaternion_t operator*(const vr::HmdQuaternion_t& lhs, const vr::HmdQuaternion_t& rhs) {
return {
(lhs.w * rhs.w) - (lhs.x * rhs.x) - (lhs.y * rhs.y) - (lhs.z * rhs.z),
(lhs.w * rhs.x) + (lhs.x * rhs.w) + (lhs.y * rhs.z) - (lhs.z * rhs.y),
(lhs.w * rhs.y) + (lhs.y * rhs.w) + (lhs.z * rhs.x) - (lhs.x * rhs.z),
(lhs.w * rhs.z) + (lhs.z * rhs.w) + (lhs.x * rhs.y) - (lhs.y * rhs.x)
};
}
CalibrationContext CalCtx;
IPCClient Driver;
static protocol::DriverPoseShmem shmem;
namespace {
CalibrationCalc calibration;
inline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const double(&vector)[3]) {
vr::HmdQuaternion_t vectorQuat = { 0.0, vector[0], vector[1] , vector[2] };
vr::HmdQuaternion_t conjugate = { quat.w, -quat.x, -quat.y, -quat.z };
auto rotatedVectorQuat = quat * vectorQuat * conjugate;
return { rotatedVectorQuat.x, rotatedVectorQuat.y, rotatedVectorQuat.z };
}
inline Eigen::Matrix3d quaternionRotateMatrix(const vr::HmdQuaternion_t& quat) {
return Eigen::Quaterniond(quat.w, quat.x, quat.y, quat.z).toRotationMatrix();
}
struct DSample
{
bool valid;
Eigen::Vector3d ref, target;
};
bool StartsWith(const std::string& str, const std::string& prefix)
{
if (str.length() < prefix.length())
return false;
return str.compare(0, prefix.length(), prefix) == 0;
}
bool EndsWith(const std::string& str, const std::string& suffix)
{
if (str.length() < suffix.length())
return false;
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
}
Eigen::Vector3d AxisFromRotationMatrix3(Eigen::Matrix3d rot)
{
return Eigen::Vector3d(rot(2, 1) - rot(1, 2), rot(0, 2) - rot(2, 0), rot(1, 0) - rot(0, 1));
}
double AngleFromRotationMatrix3(Eigen::Matrix3d rot)
{
return acos((rot(0, 0) + rot(1, 1) + rot(2, 2) - 1.0) / 2.0);
}
vr::HmdQuaternion_t VRRotationQuat(const Eigen::Quaterniond& rotQuat)
{
vr::HmdQuaternion_t vrRotQuat;
vrRotQuat.x = rotQuat.coeffs()[0];
vrRotQuat.y = rotQuat.coeffs()[1];
vrRotQuat.z = rotQuat.coeffs()[2];
vrRotQuat.w = rotQuat.coeffs()[3];
return vrRotQuat;
}
vr::HmdQuaternion_t VRRotationQuat(Eigen::Vector3d eulerdeg)
{
auto euler = eulerdeg * EIGEN_PI / 180.0;
Eigen::Quaterniond rotQuat =
Eigen::AngleAxisd(euler(0), Eigen::Vector3d::UnitZ()) *
Eigen::AngleAxisd(euler(1), Eigen::Vector3d::UnitY()) *
Eigen::AngleAxisd(euler(2), Eigen::Vector3d::UnitX());
return VRRotationQuat(rotQuat);
}
vr::HmdVector3d_t VRTranslationVec(Eigen::Vector3d transcm)
{
auto trans = transcm * 0.01;
vr::HmdVector3d_t vrTrans;
vrTrans.v[0] = trans[0];
vrTrans.v[1] = trans[1];
vrTrans.v[2] = trans[2];
return vrTrans;
}
DSample DeltaRotationSamples(Sample s1, Sample s2)
{
// Difference in rotation between samples.
auto dref = s1.ref.rot * s2.ref.rot.transpose();
auto dtarget = s1.target.rot * s2.target.rot.transpose();
// When stuck together, the two tracked objects rotate as a pair,
// therefore their axes of rotation must be equal between any given pair of samples.
DSample ds;
ds.ref = AxisFromRotationMatrix3(dref);
ds.target = AxisFromRotationMatrix3(dtarget);
// Reject samples that were too close to each other.
auto refA = AngleFromRotationMatrix3(dref);
auto targetA = AngleFromRotationMatrix3(dtarget);
ds.valid = refA > 0.4 && targetA > 0.4 && ds.ref.norm() > 0.01 && ds.target.norm() > 0.01;
ds.ref.normalize();
ds.target.normalize();
return ds;
}
Pose ConvertPose(const vr::DriverPose_t &driverPose) {
Eigen::Quaterniond driverToWorldQ(
driverPose.qWorldFromDriverRotation.w,
driverPose.qWorldFromDriverRotation.x,
driverPose.qWorldFromDriverRotation.y,
driverPose.qWorldFromDriverRotation.z
);
Eigen::Vector3d driverToWorldV(
driverPose.vecWorldFromDriverTranslation[0],
driverPose.vecWorldFromDriverTranslation[1],
driverPose.vecWorldFromDriverTranslation[2]
);
Eigen::Quaterniond driverRot = driverToWorldQ * Eigen::Quaterniond(
driverPose.qRotation.w,
driverPose.qRotation.x,
driverPose.qRotation.y,
driverPose.qRotation.z
);
Eigen::Vector3d driverPos = driverToWorldV + driverToWorldQ * Eigen::Vector3d(
driverPose.vecPosition[0],
driverPose.vecPosition[1],
driverPose.vecPosition[2]
);
Eigen::AffineCompact3d xform = Eigen::Translation3d(driverPos) * driverRot;
return Pose(xform);
}
bool CollectSample(const CalibrationContext& ctx)
{
vr::DriverPose_t reference, target;
reference.poseIsValid = false;
target.poseIsValid = false;
reference = ctx.devicePoses[ctx.referenceID];
target = ctx.devicePoses[ctx.targetID];
bool ok = true;
if (!reference.poseIsValid)
{
CalCtx.Log("Reference device is not tracking\n"); ok = false;
}
if (!target.poseIsValid)
{
CalCtx.Log("Target device is not tracking\n"); ok = false;
}
if (!ok)
{
if (CalCtx.state != CalibrationState::Continuous) {
CalCtx.Log("Aborting calibration!\n");
CalCtx.state = CalibrationState::None;
}
return false;
}
calibration.PushSample(Sample(
ConvertPose(reference),
ConvertPose(target)
));
return true;
}
bool AssignTargets() {
auto state = VRState::Load();
if (CalCtx.referenceID < 0) {
CalCtx.referenceID = state.FindDevice(CalCtx.referenceStandby.trackingSystem, CalCtx.referenceStandby.model, CalCtx.referenceStandby.serial);
}
if (CalCtx.targetID < 0) {
CalCtx.targetID = state.FindDevice(CalCtx.targetStandby.trackingSystem, CalCtx.targetStandby.model, CalCtx.targetStandby.serial);
}
return CalCtx.referenceID >= 0 && CalCtx.targetID >= 0;
}
}
void InitCalibrator()
{
Driver.Connect();
shmem.Open(OPENVR_SPACECALIBRATOR_SHMEM_NAME);
}
void ResetAndDisableOffsets(uint32_t id)
{
vr::HmdVector3d_t zeroV;
zeroV.v[0] = zeroV.v[1] = zeroV.v[2] = 0;
vr::HmdQuaternion_t zeroQ;
zeroQ.x = 0; zeroQ.y = 0; zeroQ.z = 0; zeroQ.w = 1;
protocol::Request req(protocol::RequestSetDeviceTransform);
req.setDeviceTransform = { id, false, zeroV, zeroQ, 1.0 };
Driver.SendBlocking(req);
}
static_assert(vr::k_unTrackedDeviceIndex_Hmd == 0, "HMD index expected to be 0");
void ScanAndApplyProfile(CalibrationContext &ctx)
{
std::unique_ptr<char[]> buffer_array(new char
[vr::k_unMaxPropertyStringSize]);
char* buffer = buffer_array.get();
ctx.enabled = ctx.validProfile;
protocol::Request setParamsReq(protocol::RequestSetAlignmentSpeedParams);
setParamsReq.setAlignmentSpeedParams = ctx.alignmentSpeedParams;
Driver.SendBlocking(setParamsReq);
for (uint32_t id = 0; id < vr::k_unMaxTrackedDeviceCount; ++id)
{
auto deviceClass = vr::VRSystem()->GetTrackedDeviceClass(id);
if (deviceClass == vr::TrackedDeviceClass_Invalid)
continue;
/*if (deviceClass == vr::TrackedDeviceClass_HMD) // for debugging unexpected universe switches
{
vr::ETrackedPropertyError err = vr::TrackedProp_Success;
auto universeId = vr::VRSystem()->GetUint64TrackedDeviceProperty(id, vr::Prop_CurrentUniverseId_Uint64, &err);
printf("uid %d err %d\n", universeId, err);
ResetAndDisableOffsets(id);
continue;
}*/
if (!ctx.enabled)
{
ResetAndDisableOffsets(id);
continue;
}
vr::ETrackedPropertyError err = vr::TrackedProp_Success;
vr::VRSystem()->GetStringTrackedDeviceProperty(id, vr::Prop_TrackingSystemName_String, buffer, vr::k_unMaxPropertyStringSize, &err);
if (err != vr::TrackedProp_Success)
{
ResetAndDisableOffsets(id);
continue;
}
std::string trackingSystem(buffer);
if (id == vr::k_unTrackedDeviceIndex_Hmd)
{
//auto p = ctx.devicePoses[id].mDeviceToAbsoluteTracking.m;
//printf("HMD %d: %f %f %f\n", id, p[0][3], p[1][3], p[2][3]);
if (trackingSystem != ctx.referenceTrackingSystem)
{
// Currently using an HMD with a different tracking system than the calibration.
ctx.enabled = false;
}
ResetAndDisableOffsets(id);
continue;
}
if (trackingSystem != ctx.targetTrackingSystem)
{
ResetAndDisableOffsets(id);
continue;
}
protocol::Request req(protocol::RequestSetDeviceTransform);
req.setDeviceTransform = {
id,
true,
VRTranslationVec(ctx.calibratedTranslation),
VRRotationQuat(ctx.calibratedRotation),
ctx.calibratedScale
};
req.setDeviceTransform.lerp = CalCtx.state == CalibrationState::Continuous;
req.setDeviceTransform.quash = CalCtx.state == CalibrationState::Continuous && id == CalCtx.targetID && CalCtx.quashTargetInContinuous;
Driver.SendBlocking(req);
}
if (ctx.enabled && ctx.chaperone.valid && ctx.chaperone.autoApply)
{
uint32_t quadCount = 0;
vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(nullptr, &quadCount);
// Heuristic: when SteamVR resets to a blank-ish chaperone, it uses empty geometry,
// but manual adjustments (e.g. via a play space mover) will not touch geometry.
if (quadCount != ctx.chaperone.geometry.size())
{
ApplyChaperoneBounds();
}
}
}
void StartCalibration()
{
CalCtx.state = CalibrationState::Begin;
CalCtx.wantedUpdateInterval = 0.0;
CalCtx.messages.clear();
calibration.Clear();
Metrics::WriteLogAnnotation("StartCalibration");
}
void StartContinuousCalibration() {
CalCtx.calibrationSpeed = CalibrationContext::FAST;
StartCalibration();
CalCtx.state = CalibrationState::Continuous;
CalCtx.Log("Collecting initial samples...");
Metrics::WriteLogAnnotation("StartContinuousCalibration");
}
void EndContinuousCalibration() {
CalCtx.state = CalibrationState::None;
SaveProfile(CalCtx);
Metrics::WriteLogAnnotation("EndContinuousCalibration");
}
void CalibrationTick(double time)
{
if (!vr::VRSystem())
return;
auto &ctx = CalCtx;
if ((time - ctx.timeLastTick) < 0.05)
return;
if (ctx.state == CalibrationState::Continuous || ctx.state == CalibrationState::ContinuousStandby) {
ctx.ClearLogOnMessage();
}
ctx.timeLastTick = time;
shmem.ReadNewPoses([&](const protocol::DriverPoseShmem::AugmentedPose& augmented_pose) {
if (augmented_pose.deviceId >= 0 && augmented_pose.deviceId <= vr::k_unMaxTrackedDeviceCount) {
ctx.devicePoses[augmented_pose.deviceId] = augmented_pose.pose;
}
});
if (ctx.state == CalibrationState::None || ctx.state == CalibrationState::ContinuousStandby
|| (ctx.state == CalibrationState::Continuous && !calibration.isValid()))
{
if ((time - ctx.timeLastScan) >= 1.0)
{
ScanAndApplyProfile(ctx);
ctx.timeLastScan = time;
}
}
if (ctx.state == CalibrationState::ContinuousStandby) {
if (AssignTargets()) {
StartContinuousCalibration();
}
else {
ctx.wantedUpdateInterval = 0.5;
ctx.Log("Waiting for devices...");
return;
}
}
if (ctx.state == CalibrationState::None) {
ctx.wantedUpdateInterval = 1.0;
return;
}
if (ctx.state == CalibrationState::Editing)
{
ctx.wantedUpdateInterval = 0.1;
if ((time - ctx.timeLastScan) >= 0.1)
{
ScanAndApplyProfile(ctx);
ctx.timeLastScan = time;
}
return;
}
bool ok = true;
if (ctx.referenceID == -1 || ctx.referenceID >= vr::k_unMaxTrackedDeviceCount) {
CalCtx.Log("Missing reference device\n");
ok = false;
}
if (ctx.targetID == -1 || ctx.targetID >= vr::k_unMaxTrackedDeviceCount)
{
CalCtx.Log("Missing target device\n");
ok = false;
}
if (ctx.state == CalibrationState::Begin)
{
char referenceSerial[256], targetSerial[256];
referenceSerial[0] = targetSerial[0] = 0;
vr::VRSystem()->GetStringTrackedDeviceProperty(ctx.referenceID, vr::Prop_SerialNumber_String, referenceSerial, 256);
vr::VRSystem()->GetStringTrackedDeviceProperty(ctx.targetID, vr::Prop_SerialNumber_String, targetSerial, 256);
char buf[256];
snprintf(buf, sizeof buf, "Reference device ID: %d, serial: %s\n", ctx.referenceID, referenceSerial);
CalCtx.Log(buf);
snprintf(buf, sizeof buf, "Target device ID: %d, serial %s\n", ctx.targetID, targetSerial);
CalCtx.Log(buf);
ScanAndApplyProfile(ctx);
if (!CalCtx.ReferencePoseIsValid())
{
CalCtx.Log("Reference device is not tracking\n"); ok = false;
}
if (!CalCtx.TargetPoseIsValid())
{
CalCtx.Log("Target device is not tracking\n"); ok = false;
}
if (ok) {
//ResetAndDisableOffsets(ctx.targetID);
ctx.state = CalibrationState::Rotation;
ctx.wantedUpdateInterval = 0.0;
CalCtx.Log("Starting calibration...\n");
return;
}
}
if (!ok)
{
if (ctx.state != CalibrationState::Continuous) {
ctx.state = CalibrationState::None;
CalCtx.Log("Aborting calibration!\n");
}
return;
}
if (!CollectSample(ctx))
{
return;
}
CalCtx.Progress(calibration.SampleCount(), (int)CalCtx.SampleCount());
while (calibration.SampleCount() > CalCtx.SampleCount()) calibration.ShiftSample();
if (calibration.SampleCount() >= CalCtx.SampleCount())
{
LARGE_INTEGER start_time;
QueryPerformanceCounter(&start_time);
bool ok, lerp = false;
if (CalCtx.state == CalibrationState::Continuous) {
CalCtx.messages.clear();
calibration.enableStaticRecalibration = CalCtx.enableStaticRecalibration;
ok = calibration.ComputeIncremental(lerp, CalCtx.continuousCalibrationThreshold);
}
else {
calibration.enableStaticRecalibration = false;
ok = calibration.ComputeOneshot();
}
if (calibration.isValid()) {
ctx.calibratedRotation = calibration.EulerRotation();
ctx.calibratedTranslation = calibration.Transformation().translation() * 100.0; // convert to cm units for profile storage
auto vrTrans = VRTranslationVec(ctx.calibratedTranslation);
auto vrRot = VRRotationQuat(Eigen::Quaterniond(calibration.Transformation().rotation()));
ctx.validProfile = true;
SaveProfile(ctx);
ScanAndApplyProfile(ctx);
CalCtx.Log("Finished calibration, profile saved\n");
}
else
{
CalCtx.Log("Calibration failed.\n");
}
LARGE_INTEGER end_time;
QueryPerformanceCounter(&end_time);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
double duration = (end_time.QuadPart - start_time.QuadPart) / (double)freq.QuadPart;
Metrics::computationTime.Push(duration * 1000.0);
Metrics::WriteLogEntry();
if (CalCtx.state != CalibrationState::Continuous) {
ctx.state = CalibrationState::None;
calibration.Clear();
}
else {
for (int i = 0; i < 10; i++) calibration.ShiftSample();
}
}
}
void LoadChaperoneBounds()
{
vr::VRChaperoneSetup()->RevertWorkingCopy();
uint32_t quadCount = 0;
vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(nullptr, &quadCount);
CalCtx.chaperone.geometry.resize(quadCount);
vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(&CalCtx.chaperone.geometry[0], &quadCount);
vr::VRChaperoneSetup()->GetWorkingStandingZeroPoseToRawTrackingPose(&CalCtx.chaperone.standingCenter);
vr::VRChaperoneSetup()->GetWorkingPlayAreaSize(&CalCtx.chaperone.playSpaceSize.v[0], &CalCtx.chaperone.playSpaceSize.v[1]);
CalCtx.chaperone.valid = true;
}
void ApplyChaperoneBounds()
{
vr::VRChaperoneSetup()->RevertWorkingCopy();
vr::VRChaperoneSetup()->SetWorkingCollisionBoundsInfo(&CalCtx.chaperone.geometry[0], (uint32_t)CalCtx.chaperone.geometry.size());
vr::VRChaperoneSetup()->SetWorkingStandingZeroPoseToRawTrackingPose(&CalCtx.chaperone.standingCenter);
vr::VRChaperoneSetup()->SetWorkingPlayAreaSize(CalCtx.chaperone.playSpaceSize.v[0], CalCtx.chaperone.playSpaceSize.v[1]);
vr::VRChaperoneSetup()->CommitWorkingCopy(vr::EChaperoneConfigFile_Live);
}
void DebugApplyRandomOffset() {
protocol::Request req(protocol::RequestDebugOffset);
Driver.SendBlocking(req);
}
-196
View File
@@ -1,196 +0,0 @@
#pragma once
#include <Eigen/Core>
#include <Windows.h>
#include <openvr.h>
#include <vector>
#include <deque>
#include "../Protocol.h"
enum class CalibrationState
{
None,
Begin,
Rotation,
Translation,
Editing,
Continuous,
ContinuousStandby,
};
struct StandbyDevice {
std::string trackingSystem;
std::string model, serial;
};
struct CalibrationContext
{
CalibrationState state = CalibrationState::None;
int32_t referenceID = -1, targetID = -1;
StandbyDevice targetStandby, referenceStandby;
Eigen::Vector3d calibratedRotation;
Eigen::Vector3d calibratedTranslation;
double calibratedScale;
std::string referenceTrackingSystem;
std::string targetTrackingSystem;
bool enabled = false;
bool validProfile = false;
bool clearOnLog = false;
bool quashTargetInContinuous = false;
double timeLastTick = 0, timeLastScan = 0;
double wantedUpdateInterval = 1.0;
float continuousCalibrationThreshold;
protocol::AlignmentSpeedParams alignmentSpeedParams;
bool enableStaticRecalibration;
enum Speed
{
FAST = 0,
SLOW = 1,
VERY_SLOW = 2
};
Speed calibrationSpeed = FAST;
vr::DriverPose_t devicePoses[vr::k_unMaxTrackedDeviceCount];
CalibrationContext() {
calibratedScale = 1.0;
memset(devicePoses, 0, sizeof(devicePoses));
ResetConfig();
}
void ResetConfig() {
alignmentSpeedParams.thr_rot_tiny = 0.49f * (EIGEN_PI / 180.0f);
alignmentSpeedParams.thr_rot_small = 0.5f * (EIGEN_PI / 180.0f);
alignmentSpeedParams.thr_rot_large = 5.0f * (EIGEN_PI / 180.0f);
alignmentSpeedParams.thr_trans_tiny = 0.98f / 1000.0; // mm
alignmentSpeedParams.thr_trans_small = 1.0f / 1000.0; // mm
alignmentSpeedParams.thr_trans_large = 20.0f / 1000.0; // mm
alignmentSpeedParams.align_speed_tiny = 1.0f;
alignmentSpeedParams.align_speed_small = 1.0f;
alignmentSpeedParams.align_speed_large = 2.0f;
continuousCalibrationThreshold = 1.5f;
enableStaticRecalibration = true;
}
struct Chaperone
{
bool valid = false;
bool autoApply = true;
std::vector<vr::HmdQuad_t> geometry;
vr::HmdMatrix34_t standingCenter;
vr::HmdVector2_t playSpaceSize;
} chaperone;
void ClearLogOnMessage() {
clearOnLog = true;
}
void Clear()
{
chaperone.geometry.clear();
chaperone.standingCenter = vr::HmdMatrix34_t();
chaperone.playSpaceSize = vr::HmdVector2_t();
chaperone.valid = false;
calibratedRotation = Eigen::Vector3d();
calibratedTranslation = Eigen::Vector3d();
calibratedScale = 1.0;
referenceTrackingSystem = "";
targetTrackingSystem = "";
enabled = false;
validProfile = false;
}
size_t SampleCount()
{
switch (calibrationSpeed)
{
case FAST:
return 100;
case SLOW:
return 250;
case VERY_SLOW:
return 500;
}
return 100;
}
struct Message
{
enum Type
{
String,
Progress
} type = String;
Message(Type type) : type(type) { }
std::string str;
int progress, target;
};
std::deque<Message> messages;
void Log(const std::string &msg)
{
if (clearOnLog) {
messages.clear();
clearOnLog = false;
}
if (messages.empty() || messages.back().type == Message::Progress)
messages.push_back(Message(Message::String));
OutputDebugStringA(msg.c_str());
messages.back().str += msg;
std::cerr << msg;
while (messages.size() > 15) messages.pop_front();
}
void Progress(int current, int target)
{
if (messages.empty() || messages.back().type == Message::String)
messages.push_back(Message(Message::Progress));
messages.back().progress = current;
messages.back().target = target;
}
bool TargetPoseIsValid() const {
return targetID >= 0 && targetID <= vr::k_unMaxTrackedDeviceCount
&& devicePoses[targetID].poseIsValid;
}
bool ReferencePoseIsValid() const {
return referenceID >= 0 && referenceID <= vr::k_unMaxTrackedDeviceCount
&& devicePoses[referenceID].poseIsValid;
}
};
extern CalibrationContext CalCtx;
void InitCalibrator();
void CalibrationTick(double time);
void StartCalibration();
void StartContinuousCalibration();
void EndContinuousCalibration();
void LoadChaperoneBounds();
void ApplyChaperoneBounds();
void PushCalibrationApplyTime();
void ShowCalibrationDebug(int r, int c);
void DebugApplyRandomOffset();
-640
View File
@@ -1,640 +0,0 @@
#include "CalibrationCalc.h"
#include "Calibration.h"
#include "CalibrationMetrics.h"
#include "..\Protocol.h"
inline vr::HmdQuaternion_t operator*(const vr::HmdQuaternion_t& lhs, const vr::HmdQuaternion_t& rhs) {
return {
(lhs.w * rhs.w) - (lhs.x * rhs.x) - (lhs.y * rhs.y) - (lhs.z * rhs.z),
(lhs.w * rhs.x) + (lhs.x * rhs.w) + (lhs.y * rhs.z) - (lhs.z * rhs.y),
(lhs.w * rhs.y) + (lhs.y * rhs.w) + (lhs.z * rhs.x) - (lhs.x * rhs.z),
(lhs.w * rhs.z) + (lhs.z * rhs.w) + (lhs.x * rhs.y) - (lhs.y * rhs.x)
};
}
namespace {
inline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const double(&vector)[3]) {
vr::HmdQuaternion_t vectorQuat = { 0.0, vector[0], vector[1] , vector[2] };
vr::HmdQuaternion_t conjugate = { quat.w, -quat.x, -quat.y, -quat.z };
auto rotatedVectorQuat = quat * vectorQuat * conjugate;
return { rotatedVectorQuat.x, rotatedVectorQuat.y, rotatedVectorQuat.z };
}
inline Eigen::Matrix3d quaternionRotateMatrix(const vr::HmdQuaternion_t& quat) {
return Eigen::Quaterniond(quat.w, quat.x, quat.y, quat.z).toRotationMatrix();
}
struct DSample
{
bool valid;
Eigen::Vector3d ref, target;
};
bool StartsWith(const std::string& str, const std::string& prefix)
{
if (str.length() < prefix.length())
return false;
return str.compare(0, prefix.length(), prefix) == 0;
}
bool EndsWith(const std::string& str, const std::string& suffix)
{
if (str.length() < suffix.length())
return false;
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
}
Eigen::Vector3d AxisFromRotationMatrix3(Eigen::Matrix3d rot)
{
return Eigen::Vector3d(rot(2, 1) - rot(1, 2), rot(0, 2) - rot(2, 0), rot(1, 0) - rot(0, 1));
}
double AngleFromRotationMatrix3(Eigen::Matrix3d rot)
{
return acos((rot(0, 0) + rot(1, 1) + rot(2, 2) - 1.0) / 2.0);
}
vr::HmdQuaternion_t VRRotationQuat(Eigen::Vector3d eulerdeg)
{
auto euler = eulerdeg * EIGEN_PI / 180.0;
Eigen::Quaterniond rotQuat =
Eigen::AngleAxisd(euler(0), Eigen::Vector3d::UnitZ()) *
Eigen::AngleAxisd(euler(1), Eigen::Vector3d::UnitY()) *
Eigen::AngleAxisd(euler(2), Eigen::Vector3d::UnitX());
vr::HmdQuaternion_t vrRotQuat;
vrRotQuat.x = rotQuat.coeffs()[0];
vrRotQuat.y = rotQuat.coeffs()[1];
vrRotQuat.z = rotQuat.coeffs()[2];
vrRotQuat.w = rotQuat.coeffs()[3];
return vrRotQuat;
}
vr::HmdVector3d_t VRTranslationVec(Eigen::Vector3d transcm)
{
auto trans = transcm * 0.01;
vr::HmdVector3d_t vrTrans;
vrTrans.v[0] = trans[0];
vrTrans.v[1] = trans[1];
vrTrans.v[2] = trans[2];
return vrTrans;
}
DSample DeltaRotationSamples(Sample s1, Sample s2)
{
// Difference in rotation between samples.
auto dref = s1.ref.rot * s2.ref.rot.transpose();
auto dtarget = s1.target.rot * s2.target.rot.transpose();
// When stuck together, the two tracked objects rotate as a pair,
// therefore their axes of rotation must be equal between any given pair of samples.
DSample ds;
ds.ref = AxisFromRotationMatrix3(dref);
ds.target = AxisFromRotationMatrix3(dtarget);
// Reject samples that were too close to each other.
auto refA = AngleFromRotationMatrix3(dref);
auto targetA = AngleFromRotationMatrix3(dtarget);
ds.valid = refA > 0.4 && targetA > 0.4 && ds.ref.norm() > 0.01 && ds.target.norm() > 0.01;
ds.ref.normalize();
ds.target.normalize();
return ds;
}
}
const double CalibrationCalc::AxisVarianceThreshold = 0.001;
void CalibrationCalc::PushSample(const Sample& sample) {
m_samples.push_back(sample);
}
void CalibrationCalc::Clear() {
m_estimatedTransformation.setIdentity();
m_isValid = false;
m_samples.clear();
m_axisVariance = 0.0;
m_refToTargetPose = Eigen::AffineCompact3d::Identity();
m_relativePosCalibrated = false;
}
Eigen::Vector3d CalibrationCalc::CalibrateRotation() const {
std::vector<DSample> deltas;
for (size_t i = 0; i < m_samples.size(); i++)
{
for (size_t j = 0; j < i; j++)
{
auto delta = DeltaRotationSamples(m_samples[i], m_samples[j]);
if (delta.valid)
deltas.push_back(delta);
}
}
//char buf[256];
//snprintf(buf, sizeof buf, "Got %zd samples with %zd delta samples\n", m_samples.size(), deltas.size());
//CalCtx.Log(buf);
// Kabsch algorithm
Eigen::MatrixXd refPoints(deltas.size(), 3), targetPoints(deltas.size(), 3);
Eigen::Vector3d refCentroid(0, 0, 0), targetCentroid(0, 0, 0);
for (size_t i = 0; i < deltas.size(); i++)
{
refPoints.row(i) = deltas[i].ref;
refCentroid += deltas[i].ref;
targetPoints.row(i) = deltas[i].target;
targetCentroid += deltas[i].target;
}
refCentroid /= (double)deltas.size();
targetCentroid /= (double)deltas.size();
for (size_t i = 0; i < deltas.size(); i++)
{
refPoints.row(i) -= refCentroid;
targetPoints.row(i) -= targetCentroid;
}
auto crossCV = refPoints.transpose() * targetPoints;
Eigen::BDCSVD<Eigen::MatrixXd> bdcsvd;
auto svd = bdcsvd.compute(crossCV, Eigen::ComputeThinU | Eigen::ComputeThinV);
Eigen::Matrix3d i = Eigen::Matrix3d::Identity();
if ((svd.matrixU() * svd.matrixV().transpose()).determinant() < 0)
{
i(2, 2) = -1;
}
Eigen::Matrix3d rot = svd.matrixV() * i * svd.matrixU().transpose();
rot.transposeInPlace();
Eigen::Vector3d euler = rot.eulerAngles(2, 1, 0) * 180.0 / EIGEN_PI;
//snprintf(buf, sizeof buf, "Calibrated rotation: yaw=%.2f pitch=%.2f roll=%.2f\n", euler[1], euler[2], euler[0]);
//CalCtx.Log(buf);
return euler;
}
Eigen::Vector3d CalibrationCalc::CalibrateTranslation(const Eigen::Matrix3d &rotation) const
{
std::vector<std::pair<Eigen::Vector3d, Eigen::Matrix3d>> deltas;
for (size_t i = 0; i < m_samples.size(); i++)
{
Sample s_i = m_samples[i];
s_i.target.rot = rotation * s_i.target.rot;
s_i.target.trans = rotation * s_i.target.trans;
for (size_t j = 0; j < i; j++)
{
Sample s_j = m_samples[j];
s_j.target.rot = rotation * s_j.target.rot;
s_j.target.trans = rotation * s_j.target.trans;
auto QAi = s_i.ref.rot.transpose();
auto QAj = s_j.ref.rot.transpose();
auto dQA = QAj - QAi;
auto CA = QAj * (s_j.ref.trans - s_j.target.trans) - QAi * (s_i.ref.trans - s_i.target.trans);
deltas.push_back(std::make_pair(CA, dQA));
auto QBi = s_i.target.rot.transpose();
auto QBj = s_j.target.rot.transpose();
auto dQB = QBj - QBi;
auto CB = QBj * (s_j.ref.trans - s_j.target.trans) - QBi * (s_i.ref.trans - s_i.target.trans);
deltas.push_back(std::make_pair(CB, dQB));
}
}
Eigen::VectorXd constants(deltas.size() * 3);
Eigen::MatrixXd coefficients(deltas.size() * 3, 3);
for (size_t i = 0; i < deltas.size(); i++)
{
for (int axis = 0; axis < 3; axis++)
{
constants(i * 3 + axis) = deltas[i].first(axis);
coefficients.row(i * 3 + axis) = deltas[i].second.row(axis);
}
}
Eigen::Vector3d trans = coefficients.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(constants);
auto transcm = trans * 100.0;
//char buf[256];
//snprintf(buf, sizeof buf, "Calibrated translation x=%.2f y=%.2f z=%.2f\n", transcm[0], transcm[1], transcm[2]);
//CalCtx.Log(buf);
return trans;
}
namespace {
Pose ApplyTransform(const Pose& originalPose, const Eigen::AffineCompact3d& transform) {
Pose pose(originalPose);
pose.rot = transform.rotation() * pose.rot;
pose.trans = transform * pose.trans;
return pose;
}
Pose ApplyTransform(const Pose & originalPose, const Eigen::Vector3d & vrTrans, const Eigen::Matrix3d & rotMat) {
Pose pose(originalPose);
pose.rot = rotMat * pose.rot;
pose.trans = vrTrans + (rotMat * pose.trans);
return pose;
}
}
Eigen::AffineCompact3d CalibrationCalc::ComputeCalibration() const {
Eigen::Vector3d rotation = CalibrateRotation();
Eigen::Matrix3d rotationMat = quaternionRotateMatrix(VRRotationQuat(rotation));
Eigen::Vector3d translation = CalibrateTranslation(rotationMat);
Eigen::AffineCompact3d rot(rotationMat);
Eigen::Translation3d trans(translation);
return trans * rot;
}
double CalibrationCalc::RetargetingErrorRMS(
const Eigen::Vector3d& hmdToTargetPos,
const Eigen::AffineCompact3d& calibration
) const {
double errorAccum = 0;
int sampleCount = 0;
for (auto& sample : m_samples) {
if (!sample.valid) continue;
// Apply transformation
const auto updatedPose = ApplyTransform(sample.target, calibration);
const Eigen::Vector3d hmdPoseSpace = sample.ref.rot * hmdToTargetPos + sample.ref.trans;
// Compute error term
double error = (updatedPose.trans - hmdPoseSpace).squaredNorm();
errorAccum += error;
sampleCount++;
}
return sqrt(errorAccum / sampleCount);
}
Eigen::Vector3d CalibrationCalc::ComputeRefToTargetOffset(const Eigen::AffineCompact3d& calibration) const {
Eigen::Vector3d accum = Eigen::Vector3d::Zero();
int sampleCount = 0;
for (auto& sample : m_samples) {
if (!sample.valid) continue;
// Apply transformation
const auto updatedPose = ApplyTransform(sample.target, calibration);
// Now move the transform from world to HMD space
const auto hmdOriginPos = updatedPose.trans - sample.ref.trans;
const auto hmdSpace = sample.ref.rot.inverse() * hmdOriginPos;
accum += hmdSpace;
sampleCount++;
}
accum /= sampleCount;
return accum;
}
Eigen::Vector4d CalibrationCalc::ComputeAxisVariance(
const Eigen::AffineCompact3d& calibration
) const {
// We want to determine if the user rotated in enough axis to find a unique solution.
// It's sufficient to rotate in two axis - this is because once we constrain the mapping
// of those two orthogonal basis vectors, the third is determined by the cross product of
// those two basis vectors. So, the question we then have to answer is - after accounting for
// translational movement of the HMD itself, are we too close to having only moved on a plane?
// To determine this, we perform primary component analysis on the rotation quaternions themselves.
// Since an angle axis quaternion is defined as the sum of Qidentity*cos(angle/2) + Qaxis*sin(angle/2),
// we expect that rotations around a single axis will have two primary components: One corresponding
// to the identity component, and one to the axis component. Thus, we check the variance (eigenvalue) of
// the third primary component to see if we've moved in two axis.
std::ostringstream dbgStream;
std::vector<Eigen::Vector4d> points;
Eigen::Vector4d mean = Eigen::Vector4d::Zero();
for (auto& sample : m_samples) {
if (!sample.valid) continue;
auto q = Eigen::Quaterniond(sample.target.rot);
auto point = Eigen::Vector4d(q.w(), q.x(), q.y(), q.z());
mean += point;
points.push_back(point);
}
mean /= (double) points.size();
// Compute covariance matrix
Eigen::Matrix4d covMatrix = Eigen::Matrix4d::Zero();
for (auto& point : points) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
covMatrix(i, j) += (point(i) - mean(i)) * (point(j) - mean(j));
}
}
}
covMatrix /= (double) points.size();
Eigen::SelfAdjointEigenSolver<Eigen::Matrix4d> solver;
solver.compute(covMatrix);
return solver.eigenvalues();
}
bool CalibrationCalc::ValidateCalibration(const Eigen::AffineCompact3d &calibration, double *error, Eigen::Vector3d *posOffsetV) {
bool ok = true;
const auto posOffset = ComputeRefToTargetOffset(calibration);
if (posOffsetV) *posOffsetV = posOffset;
// char buf[256];
//snprintf(buf, sizeof buf, "HMD to target offset: (%.2f, %.2f, %.2f)\n", posOffset(0), posOffset(1), posOffset(2));
//CalCtx.Log(buf);
double rmsError = RetargetingErrorRMS(posOffset, calibration);
//snprintf(buf, sizeof buf, "Position error (RMS): %.3f\n", rmsError);
//CalCtx.Log(buf);
if (rmsError > 0.1) ok = false;
if (error) *error = rmsError;
return ok;
}
// Given:
// R - the reference pose (in reference world space)
// T - the target pose (in target world space)
// C - the true calibration (target world -> reference world)
// We assume that there is some "static target pose" S s.t.:
// R * S = C * T (we'll call this the static target pose)
// To compute S:
// S = R^-1 * C * T
// To compute C:
// R * S * T^-1 = C
namespace {
class PoseAverager {
private:
Eigen::Matrix<double, 4, Eigen::Dynamic> quatAvg;
Eigen::Vector3d accum = Eigen::Vector3d::Zero();
int i = 0;
public:
PoseAverager(size_t n_samples) {
quatAvg.resize(4, n_samples);
}
template<typename P>
void Push(const P &pose) {
const Eigen::Quaterniond rot(pose.rotation());
quatAvg.col(i++) = Eigen::Vector4d(rot.w(), rot.x(), rot.y(), rot.z());
accum += pose.translation();
}
Eigen::AffineCompact3d Average() {
// https://stackoverflow.com/a/27410865/36723
auto quatT = quatAvg.transpose();
Eigen::Matrix4d quatMul = quatAvg * quatT;
Eigen::SelfAdjointEigenSolver<Eigen::Matrix4d> solver;
solver.compute(quatMul);
Eigen::Vector4d quatAvgV = solver.eigenvectors().col(3).real().normalized();
Eigen::Quaterniond avgQ(quatAvgV(0), quatAvgV(1), quatAvgV(2), quatAvgV(3));
avgQ.normalize();
Eigen::AffineCompact3d pose(avgQ);
pose.pretranslate(accum * (1.0 / i));
return pose;
}
template<typename XS, typename F>
static Eigen::AffineCompact3d AverageFor(const XS& samples, const F& poseProvider) {
int sampleCount = 0;
for (auto& sample : samples) {
if (!sample.valid) continue;
sampleCount++;
}
PoseAverager accum(sampleCount);
for (auto& sample : samples) {
if (!sample.valid) continue;
auto pose = poseProvider(sample);
accum.Push(pose);
}
return accum.Average();
}
};
}
// S = R^-1 * C * T
Eigen::AffineCompact3d CalibrationCalc::EstimateRefToTargetPose(const Eigen::AffineCompact3d &calibration) const {
auto avg = PoseAverager::AverageFor(m_samples, [&](const auto& sample) {
return Eigen::Affine3d(sample.ref.ToAffine().inverse() * calibration * sample.target.ToAffine());
});
#if 0
Eigen::Vector3d eulerAvgQ = avg.rotation().eulerAngles(2, 1, 0) * 180.0 / EIGEN_PI;
Eigen::Vector3d trans = Eigen::Vector3d(avg.translation());
std::ostringstream oss;
oss << "==========================================================================================\n";
oss << "Avg rot: " << eulerAvgQ.x() << ", " << eulerAvgQ.y() << ", " << eulerAvgQ.z() << "\n";
oss << "Avg trans:\n" << trans.x() << ", " << trans.y() << ", " << trans.z() << "\n";
OutputDebugStringA(oss.str().c_str());
#endif
return avg;
}
// S = R^-1 * C * T
// R * S * T^-1 = C
// R * (R^-1 * C * T) * T^-1 = C
/*
* This calibration routine attempts to use the estimated refToTargetPose to derive the
* playspace calibration based on the relative position of reference and target device.
* This computation can be performed even when the devices are not moving.
*/
bool CalibrationCalc::CalibrateByRelPose(Eigen::AffineCompact3d &out) const {
// R * S * T^-1 = C
out = PoseAverager::AverageFor(m_samples, [&](const auto& sample) {
return Eigen::AffineCompact3d(sample.ref.ToAffine() * m_refToTargetPose * sample.target.ToAffine().inverse());
});
return true;
}
bool CalibrationCalc::ComputeOneshot() {
auto calibration = ComputeCalibration();
bool valid = ValidateCalibration(calibration);
if (valid) {
m_estimatedTransformation = calibration;
m_isValid = true;
return true;
}
else {
CalCtx.Log("Not updating: Low-quality calibration result\n");
return false;
}
}
void CalibrationCalc::ComputeInstantOffset() {
const auto &latestSample = m_samples.back();
// Apply transformation
const auto updatedPose = ApplyTransform(latestSample.target, m_estimatedTransformation);
// Now move the transform from world to HMD space
const auto hmdOriginPos = updatedPose.trans - latestSample.ref.trans;
const auto hmdSpace = latestSample.ref.rot.inverse() * hmdOriginPos;
Metrics::posOffset_lastSample.Push(hmdSpace * 1000);
}
bool CalibrationCalc::ComputeIncremental(bool &lerp, double threshold) {
Metrics::RecordTimestamp();
double priorCalibrationError = INFINITY;
Eigen::Vector3d priorPosOffset;
if (m_isValid) {
ValidateCalibration(m_estimatedTransformation, &priorCalibrationError, &priorPosOffset);
Metrics::posOffset_currentCal.Push(priorPosOffset * 1000);
Metrics::error_currentCal.Push(priorCalibrationError * 1000);
if (priorCalibrationError < 0.005) {
return false;
}
}
double newError = INFINITY;
bool newCalibrationValid = false;
Eigen::AffineCompact3d byRelPose;
Eigen::AffineCompact3d calibration;
bool usingRelPose = false;
double relPoseError = INFINITY;
if (enableStaticRecalibration && CalibrateByRelPose(byRelPose)) {
Eigen::Vector3d relPosOffset;
ValidateCalibration(byRelPose, &relPoseError, &relPosOffset);
Metrics::posOffset_byRelPose.Push(relPosOffset * 1000);
Metrics::error_byRelPose.Push(relPoseError * 1000);
if (relPoseError < 0.010 || m_relativePosCalibrated && relPoseError < 0.025) {
newCalibrationValid = true;
usingRelPose = true;
newError = relPoseError;
calibration = byRelPose;
if (relPoseError * threshold >= priorCalibrationError) {
return false;
}
}
}
double newVariance = 0;
if (!newCalibrationValid) {
calibration = ComputeCalibration();
newVariance = ComputeAxisVariance(calibration)(1);
Metrics::axisIndependence.Push(newVariance);
if (newVariance < AxisVarianceThreshold && newVariance < m_axisVariance) {
newCalibrationValid = false;
} else {
newCalibrationValid = ValidateCalibration(calibration, &newError, &m_posOffset);
Metrics::posOffset_rawComputed.Push(m_posOffset * 1000);
}
if (m_isValid) {
if (priorCalibrationError < newError * threshold) {
// If we have a more noisy calibration than before, avoid updating.
newCalibrationValid = false;
}
}
Metrics::error_rawComputed.Push(newError * 1000);
ComputeInstantOffset();
}
#if 0
char tmp[256];
snprintf(tmp, sizeof tmp, "Prior calibration error: %.3f (valid: %s) sct %d; new error %.3f; new better? %s\n",
priorCalibrationError, m_isValid ? "yes" : "no", stableCt, newError, !oldCalibrationBetter ? "yes" : "no");
CalCtx.Log(tmp);
#endif
// Now, can we use the relative pose to perform a rapid correction?
if (!newCalibrationValid) {
double existingPoseErrorUsingRelPosition = RetargetingErrorRMS(m_refToTargetPose.translation(), m_estimatedTransformation);
Metrics::error_currentCalRelPose.Push(existingPoseErrorUsingRelPosition * 1000);
if (relPoseError * threshold < existingPoseErrorUsingRelPosition || newCalibrationValid && relPoseError < newError) {
newCalibrationValid = true;
usingRelPose = true;
newError = relPoseError;
calibration = byRelPose;
}
}
if (newCalibrationValid) {
lerp = m_isValid;
m_relativePosCalibrated = m_relativePosCalibrated || newError < 0.005;
if (!m_isValid) {
CalCtx.Log("Applying initial transformation...");
}
else if (m_relativePosCalibrated) {
CalCtx.Log("Applying updated transformation...");
} else {
CalCtx.Log("Applying temporary transformation...");
}
m_isValid = true;
m_estimatedTransformation = calibration;
m_axisVariance = newVariance;
if (!usingRelPose) {
m_refToTargetPose = EstimateRefToTargetPose(m_estimatedTransformation);
}
Metrics::calibrationApplied.Push(!usingRelPose);
return true;
}
else {
return false;
}
}
-126
View File
@@ -1,126 +0,0 @@
#pragma once
#include <Eigen/Dense>
#include <openvr.h>
#include <vector>
#include <deque>
#include <iostream>
struct Pose
{
Eigen::Matrix3d rot;
Eigen::Vector3d trans;
Pose() { }
Pose(const Eigen::AffineCompact3d& transform) {
rot = transform.rotation();
trans = transform.translation();
}
Pose(vr::HmdMatrix34_t hmdMatrix)
{
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
rot(i, j) = hmdMatrix.m[i][j];
}
}
trans = Eigen::Vector3d(hmdMatrix.m[0][3], hmdMatrix.m[1][3], hmdMatrix.m[2][3]);
}
Pose(vr::HmdQuaternion_t rot, const double *trans) {
this->rot = Eigen::Matrix3d(Eigen::Quaterniond(rot.w, rot.x, rot.y, rot.z));
this->trans = Eigen::Vector3d(trans[0], trans[1], trans[2]);
}
Pose(double x, double y, double z) : trans(Eigen::Vector3d(x, y, z)) { }
Eigen::Matrix4d ToAffine() const {
Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
matrix(i, j) = rot(i, j);
}
matrix(i, 3) = trans(i);
}
return matrix;
}
};
struct Sample
{
Pose ref, target;
bool valid;
Sample() : valid(false) { }
Sample(Pose ref, Pose target) : valid(true), ref(ref), target(target) { }
};
class CalibrationCalc {
public:
static const double AxisVarianceThreshold;
bool enableStaticRecalibration;
const Eigen::AffineCompact3d Transformation() const
{
return m_estimatedTransformation;
}
const Eigen::Vector3d EulerRotation() const {
auto rot = m_estimatedTransformation.rotation();
return rot.eulerAngles(2, 1, 0) * 180.0 / EIGEN_PI;
}
bool isValid() const {
return m_isValid;
}
void PushSample(const Sample& sample);
void Clear();
bool ComputeOneshot();
bool ComputeIncremental(bool &lerp, double threshold);
size_t SampleCount() const {
return m_samples.size();
}
void ShiftSample() {
if (!m_samples.empty()) m_samples.pop_front();
}
CalibrationCalc() : m_isValid(false), m_calcCycle(0), enableStaticRecalibration(true) {}
// Debug fields
Eigen::Vector3d m_posOffset;
double m_axisVariance = 0.0;
long m_calcCycle;
private:
bool m_isValid;
Eigen::AffineCompact3d m_estimatedTransformation;
bool m_relativePosCalibrated = false;
/*
* This affine transform estimates the pose of the target within the reference device's local pose space.
* That is to say, it's given by transforming the target world pose by the inverse reference pose.
*/
Eigen::AffineCompact3d m_refToTargetPose = Eigen::AffineCompact3d::Identity();
std::deque<Sample> m_samples;
Eigen::Vector3d CalibrateRotation() const;
Eigen::Vector3d CalibrateTranslation(const Eigen::Matrix3d &rotation) const;
Eigen::AffineCompact3d ComputeCalibration() const;
double RetargetingErrorRMS(const Eigen::Vector3d& hmdToTargetPos, const Eigen::AffineCompact3d& calibration) const;
Eigen::Vector3d ComputeRefToTargetOffset(const Eigen::AffineCompact3d& calibration) const;
Eigen::Vector4d ComputeAxisVariance(const Eigen::AffineCompact3d& calibration) const;
bool ValidateCalibration(const Eigen::AffineCompact3d& calibration, double *errorOut = nullptr, Eigen::Vector3d* posOffsetV = nullptr);
void ComputeInstantOffset();
Eigen::AffineCompact3d EstimateRefToTargetPose(const Eigen::AffineCompact3d& calibration) const;
bool CalibrateByRelPose(Eigen::AffineCompact3d &out) const;
};
-383
View File
@@ -1,383 +0,0 @@
#include "stdafx.h"
#include <vector>
#include <implot/implot.h>
#include "CalibrationCalc.h"
#include "CalibrationMetrics.h"
#include "UserInterface.h"
// ImPlotPoint (*ImPlotGetter)(void* user_data, int idx);
namespace {
double refTime;
template<typename F>
ImPlotPoint VPIndexer(void* ptr, int idx) {
auto point = (*reinterpret_cast<const F*>(ptr))(idx);
point.x -= refTime;
return point;
}
template<typename F>
void PlotLineG(const char* name, const F& f, int points) {
const void* vp_f = &f;
if (points > 0) {
ImPlot::PlotLineG(name, VPIndexer<F>, const_cast<void*>(vp_f), points);
}
else {
double x = -INFINITY;
double y = 0;
ImPlot::PlotLine(name, &x, &y, 1);
}
}
template<typename F, typename G>
void PlotShadedG(const char* name, const F& data, const G& reference, int count) {
const void* vp_data = &data;
const void* vp_reference = &reference;
if (count > 0) {
ImPlot::PlotShadedG(name,
VPIndexer<F>, const_cast<void*>(vp_data),
VPIndexer<G>, const_cast<void*>(vp_reference),
count
);
}
else {
double x = -INFINITY;
double y = 0;
ImPlot::PlotShaded(name, &x, &y, &y, 1);
}
}
void PlotLineG(const char* name, const Metrics::TimeSeries<double>& ts) {
PlotLineG(name, [&](int index) {
const auto& p = ts[index];
return ImPlotPoint(p.first, p.second);
},
ts.size()
);
}
void PlotVector(const char* namePrefix, const Metrics::TimeSeries<Eigen::Vector3d>& ts) {
std::string name(namePrefix);
name += "X";
PlotLineG(name.c_str(), [&](int index) {
const auto& p = ts[index];
return ImPlotPoint(p.first, p.second(0));
}, ts.size());
name.pop_back();
name += "Y";
PlotLineG(name.c_str(), [&](int index) {
const auto& p = ts[index];
return ImPlotPoint(p.first, p.second(1));
}, ts.size());
name.pop_back();
name += "Z";
PlotLineG(name.c_str(), [&](int index) {
const auto& p = ts[index];
return ImPlotPoint(p.first, p.second(2));
}, ts.size());
}
double lastMouseX = -INFINITY;
bool wasHovered;
std::vector<double> calAppliedTimeBuffer, calByRelPoseTimeBuffer;
void PrepApplyTicks() {
calAppliedTimeBuffer.clear();
calByRelPoseTimeBuffer.clear();
for (auto t : Metrics::calibrationApplied.data()) {
if (t.second) {
calAppliedTimeBuffer.push_back(t.first - refTime);
}
else {
calByRelPoseTimeBuffer.push_back(t.first - refTime);
}
}
}
void AddApplyTicks() {
if (calAppliedTimeBuffer.empty()) {
double x = -INFINITY;
ImPlot::PlotVLines("##CalibrationAppliedTime", &x, 1);
} else {
ImPlot::PlotVLines("##CalibrationAppliedTime", &calAppliedTimeBuffer[0], (int)calAppliedTimeBuffer.size());
}
if (calByRelPoseTimeBuffer.empty()) {
double x = -INFINITY;
ImPlot::PlotVLines("##CalibrationAppliedTimeByRelPose", &x, 1);
}
else {
ImPlot::PlotVLines("##CalibrationAppliedTimeByRelPose", &calByRelPoseTimeBuffer[0], (int)calByRelPoseTimeBuffer.size());
}
ImPlot::SetNextLineStyle(ImVec4(0.5, 0.5, 1, 1));
ImPlot::PlotVLines("##TagLine", &lastMouseX, 1);
if (ImPlot::IsPlotHovered()) {
auto mousePos = ImPlot::GetPlotMousePos();
lastMouseX = mousePos.x;
wasHovered = true;
}
}
struct GraphInfo {
const char* name;
void (*callback)();
};
void SetupXAxis() {
ImPlot::SetupAxisLimits(ImAxis_X1, -Metrics::TimeSpan, 0, ImGuiCond_Always);
}
void G_PosOffset_RawComputed() {
if (ImPlot::BeginPlot("##posOffsetRawComputed")) {
ImPlot::SetupAxes(NULL, "mm", 0, ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_RangeFit);
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, -200, 200, ImGuiCond_Appearing);
AddApplyTicks();
PlotVector("", Metrics::posOffset_rawComputed);
ImPlot::EndPlot();
}
}
void G_PosOffset_CurrentCal() {
if (ImPlot::BeginPlot("##posOffsetCurrentCal")) {
ImPlot::SetupAxes(NULL, "mm", 0, ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_RangeFit);
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, -200, 200, ImGuiCond_Appearing);
AddApplyTicks();
PlotVector("", Metrics::posOffset_currentCal);
ImPlot::EndPlot();
}
}
void G_PosOffset_LastSample() {
if (ImPlot::BeginPlot("##posOffsetLastSample")) {
ImPlot::SetupAxes(NULL, "mm", 0, ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_RangeFit);
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, -200, 200, ImGuiCond_Appearing);
AddApplyTicks();
PlotVector("", Metrics::posOffset_lastSample);
ImPlot::EndPlot();
}
}
void G_PosOffset_ByRelPose() {
if (ImPlot::BeginPlot("##posOffsetByRelPose")) {
ImPlot::SetupAxes(NULL, "mm", 0, ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_RangeFit);
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, -200, 200, ImGuiCond_Appearing);
AddApplyTicks();
PlotVector("", Metrics::posOffset_byRelPose);
ImPlot::EndPlot();
}
}
void G_PosOffset_PosError() {
if (ImPlot::BeginPlot("##Position error")) {
ImPlot::SetupAxes(NULL, "mm (RMS)");
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 25, ImGuiCond_Appearing);
AddApplyTicks();
PlotLineG("Candidate", Metrics::error_rawComputed);
PlotLineG("Active", Metrics::error_currentCal);
PlotLineG("By Rel Pose", Metrics::error_byRelPose);
PlotLineG("CC Rel Pose", Metrics::error_currentCalRelPose);
ImPlot::EndPlot();
}
}
void G_ComputationTime() {
if (ImPlot::BeginPlot("##Computation Time", ImVec2(-1, 0), ImPlotFlags_NoLegend)) {
ImPlot::SetupAxes(NULL, "ms", 0, ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_RangeFit);
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 200, ImGuiCond_Appearing);
AddApplyTicks();
PlotLineG("Time", Metrics::computationTime);
ImPlot::EndPlot();
}
}
void G_AxisVariance() {
static bool firstrun = true;
static ImPlotColormap axisVarianceColormap;
if (firstrun) {
firstrun = false;
auto defaultFirst = ImPlot::GetColormapColor(0);
ImVec4 colors[] = {
ImPlot::GetColormapColor(0),
ImPlot::GetColormapColor(1),
{ 1, 0, 0, 1 },
{ 0, 1, 0, 1 },
{ 0.5, 0.5, 0.5, 1 },
};
axisVarianceColormap = ImPlot::AddColormap("AxisVarianceColormap", colors, sizeof(colors) / sizeof(colors[0]));
}
if (ImPlot::BeginPlot("##Axis variance", ImVec2(-1, 0), ImPlotFlags_NoLegend)) {
ImPlot::SetupAxes(NULL, NULL, 0, 0);
SetupXAxis();
ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 0.003, ImGuiCond_Always);
AddApplyTicks();
ImPlot::PushColormap(axisVarianceColormap);
ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.5f);
ImPlot::SetNextLineStyle(ImVec4(1, 0, 0, 1));
PlotShadedG("##VarianceLow",
[&](int index) {
auto p = Metrics::axisIndependence[index];
p.second = min(p.second, CalibrationCalc::AxisVarianceThreshold);
return ImPlotPoint(p.first, p.second);
},
[&](int index) {
auto p = Metrics::axisIndependence[index];
return ImPlotPoint(p.first, 0);
},
Metrics::axisIndependence.size()
);
ImPlot::SetNextLineStyle(ImVec4(0, 1, 0, 1));
PlotShadedG("##VarianceHigh",
[&](int index) {
auto p = Metrics::axisIndependence[index];
p.second = max(p.second, CalibrationCalc::AxisVarianceThreshold);
return ImPlotPoint(p.first, p.second);
},
[&](int index) {
auto p = Metrics::axisIndependence[index];
return ImPlotPoint(p.first, CalibrationCalc::AxisVarianceThreshold);
},
Metrics::axisIndependence.size()
);
PlotLineG("Datapoint", Metrics::axisIndependence);
ImPlot::PopStyleVar(1);
ImPlot::PopColormap(1);
ImPlot::EndPlot();
}
}
const struct GraphInfo graphs[] = {
{ "Position Error", G_PosOffset_PosError },
{ "Axis Variance", G_AxisVariance },
{ "Offset: Raw Computed", G_PosOffset_RawComputed },
{ "Offset: Current Calibration", G_PosOffset_CurrentCal },
{ "Offset: Last Sample", G_PosOffset_LastSample },
{ "Offset: By Rel Pose", G_PosOffset_ByRelPose },
{ "Processing time", G_ComputationTime }
};
const int N_GRAPHS = sizeof(graphs) / sizeof(graphs[0]);
}
void PushCalibrationApplyTime() {
Metrics::calibrationApplied.Push(true);
}
void ShowCalibrationDebug(int rows, int cols) {
static std::vector<int> curIndexes;
//ImGui::ShowDemoWindow();
//ImPlot::ShowDemoWindow();
double initMouseX = lastMouseX;
wasHovered = false;
for (int i = (int)curIndexes.size(); i < rows * cols; i++) {
curIndexes.push_back(i % N_GRAPHS);
}
auto avail = ImGui::GetContentRegionAvail();
auto bgCol = ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);
ImGui::PushStyleColor(ImGuiCol_TableRowBg, bgCol);
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, bgCol);
ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImVec4(0,0,0,0));
ImGui::SetNextWindowBgAlpha(1);
if (!ImGui::BeginChild("##CalibrationDebug", avail, false,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoTitleBar)) {
ImGui::EndChild();
return;
}
if (!ImGui::BeginTable("##CalibrationDebug", cols, ImGuiTableFlags_RowBg)) {
return;
}
double t = refTime = Metrics::timestamp();
PrepApplyTicks();
for (int r = 0; r < rows; r++) {
ImGui::TableNextRow();
for (int c = 0; c < cols; c++) {
int i = r * cols + c;
ImGui::TableSetColumnIndex(c);
ImGui::PushID(i);
ImGui::SetNextItemWidth(ImGui::GetColumnWidth());
if (ImGui::BeginCombo("", graphs[curIndexes[i]].name, 0)) {
for (int j = 0; j < N_GRAPHS; j++) {
bool isSelected = j == curIndexes[i];
if (ImGui::Selectable(graphs[j].name, isSelected)) {
curIndexes[i] = j;
}
if (isSelected) ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
graphs[curIndexes[i]].callback();
ImGui::PopID();
}
}
ImGui::EndTable();
ImGui::EndChild();
ImPlot::PopStyleColor(1);
ImGui::PopStyleColor(2);
if (!wasHovered) {
lastMouseX = -INFINITY;
}
if (lastMouseX != initMouseX) {
RequestImmediateRedraw();
}
}
@@ -1,215 +0,0 @@
#include "stdafx.h"
#include "CalibrationMetrics.h"
#include <shlobj_core.h>
#include <fstream>
#include <vector>
namespace Metrics {
double TimeSpan = 30, CurrentTime;
TimeSeries<Eigen::Vector3d> posOffset_rawComputed; // , rotOffset_rawComputed;
TimeSeries<Eigen::Vector3d> posOffset_currentCal; // , rotOffset_currentCal;
TimeSeries<Eigen::Vector3d> posOffset_lastSample; // , rotOffset_lastSample;
TimeSeries<Eigen::Vector3d> posOffset_byRelPose;
TimeSeries<double> error_rawComputed, error_currentCal, error_byRelPose, error_currentCalRelPose;
TimeSeries<double> axisIndependence;
TimeSeries<double> computationTime;
// true - full calibration, false - static calibration
TimeSeries<bool> calibrationApplied;
double timestamp() {
static long long ts_start = ~0LL;
LARGE_INTEGER ts, freq;
QueryPerformanceCounter(&ts);
QueryPerformanceFrequency(&freq);
if (ts_start == ~0LL) ts_start = ts.QuadPart;
ts.QuadPart -= ts_start;
return ts.QuadPart / (double)freq.QuadPart;
}
void RecordTimestamp() {
CurrentTime = timestamp();
}
bool enableLogs = false;
static std::ofstream logFile;
static bool logFileIsOpen = false;
static bool failedToOpenLogFile = false;
struct CsvField {
const char* name;
void (*writer)(std::ofstream& s);
};
#define TS_FIELD(n) \
{ #n, [](auto &s) { s << n.last(); } }
#define TS_VECTOR_FIELD(n) \
{ #n ".x", [](auto &s) { s << n.last()(0); } }, \
{ #n ".y", [](auto &s) { s << n.last()(1); } }, \
{ #n ".z", [](auto &s) { s << n.last()(2); } }
static const CsvField fields[] = {
{
"Timestamp",
[](auto& s) { s << CurrentTime; }
},
TS_VECTOR_FIELD(posOffset_rawComputed),
TS_VECTOR_FIELD(posOffset_currentCal),
TS_VECTOR_FIELD(posOffset_lastSample),
TS_VECTOR_FIELD(posOffset_byRelPose),
TS_FIELD(error_rawComputed),
TS_FIELD(error_currentCal),
TS_FIELD(error_byRelPose),
TS_FIELD(error_currentCalRelPose),
TS_FIELD(axisIndependence),
TS_FIELD(computationTime),
{
"calibrationApplied",
[](auto& s) {
if (calibrationApplied.lastTs() == CurrentTime) {
if (calibrationApplied.last()) {
s << "FULL";
}
else {
s << "STATIC";
}
}
}
}
};
static void ClearOldLogs(const std::wstring& path) {
std::wstring search_path = path + L"\\spacecal_log.*.txt";
WIN32_FIND_DATA find_data;
SYSTEMTIME st_now;
FILETIME ft_now;
GetSystemTime(&st_now);
SystemTimeToFileTime(&st_now, &ft_now);
ULARGE_INTEGER ft_tmp;
ft_tmp.HighPart = ft_now.dwHighDateTime;
ft_tmp.LowPart = ft_now.dwLowDateTime;
// one day ago
uint64_t limit = ft_tmp.QuadPart - (uint64_t)(24LL * 3600LL * 10LL * 1000LL * 1000LL);
HANDLE find_handle = FindFirstFile(search_path.c_str(), &find_data);
if (find_handle != INVALID_HANDLE_VALUE) {
do {
ft_tmp.HighPart = find_data.ftLastWriteTime.dwHighDateTime;
ft_tmp.LowPart = find_data.ftLastWriteTime.dwLowDateTime;
if (ft_tmp.QuadPart < limit) {
std::wstring file_path = path + L"\\" + find_data.cFileName;
DeleteFile(file_path.c_str());
}
} while (FindNextFile(find_handle, &find_data));
FindClose(find_handle);
}
}
static bool OpenLogFile() {
PWSTR RootPath = NULL;
if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, NULL, &RootPath)) {
CoTaskMemFree(RootPath);
return false;
}
std::wstring path(RootPath);
CoTaskMemFree(RootPath);
path += LR"(\OpenVR-SpaceCalibrator)";
if (CreateDirectoryW(path.c_str(), 0) == 0 && GetLastError() != ERROR_ALREADY_EXISTS) {
return false;
}
path += LR"(\Logs)";
if (CreateDirectoryW(path.c_str(), 0) == 0 && GetLastError() != ERROR_ALREADY_EXISTS) {
return false;
}
ClearOldLogs(path);
SYSTEMTIME now;
GetSystemTime(&now);
size_t dateBufLen = GetDateFormatW(LOCALE_USER_DEFAULT, 0, &now, L"yyyy-MM-dd", NULL, 0);
std::vector<WCHAR> dateBuf(dateBufLen);
if (!GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &now, L"yyyy-MM-dd", &dateBuf[0], dateBufLen, NULL)) return false;
size_t timeBufLen = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &now, L"HH-mm-ss", NULL, 0);
std::vector<WCHAR> timeBuf(timeBufLen);
if (!GetTimeFormatEx(LOCALE_NAME_INVARIANT, 0, &now, L"HH-mm-ss", &timeBuf[0], timeBufLen)) return false;
path += LR"(\spacecal_log.)";
path += &dateBuf[0];
path += L"T";
path += &timeBuf[0];
path += L".txt";
logFile.open(path);
if (logFile.fail()) {
return false;
}
for (int i = 0; i < sizeof fields / sizeof fields[0]; i++) {
if (i > 0) logFile << ",";
logFile << fields[i].name;
}
logFile << "\n";
logFileIsOpen = true;
return true;
}
static bool CheckLogOpen() {
if (!enableLogs) {
if (logFileIsOpen) {
logFile.close();
}
logFileIsOpen = false;
failedToOpenLogFile = false;
return false;
}
if (failedToOpenLogFile) return false;
if (!logFileIsOpen && !OpenLogFile()) {
failedToOpenLogFile = true;
return false;
}
return true;
}
void WriteLogAnnotation(const char *s) {
if (!CheckLogOpen()) return;
logFile << "# [" << timestamp() << "] " << s << "\n";
logFile.flush();
}
void WriteLogEntry() {
if (!CheckLogOpen()) return;
if (logFileIsOpen) {
for (int i = 0; i < sizeof fields / sizeof fields[0]; i++) {
if (i > 0) logFile << ",";
fields[i].writer(logFile);
}
logFile << "\n";
}
logFile.flush();
}
}
@@ -1,58 +0,0 @@
#pragma once
#include <deque>
#include <utility>
#include <Eigen/Dense>
namespace Metrics {
extern double TimeSpan, CurrentTime;
double timestamp();
void RecordTimestamp();
template<typename T>
class TimeSeries {
std::deque<std::pair<double, T>> Data;
public:
const std::deque<std::pair<double, T>> &data() const { return Data; }
void Push(const T& data) {
Data.push_back(std::make_pair(CurrentTime, data));
double cutoff = CurrentTime - TimeSpan;
while (!Data.empty() && (Data.front().first < cutoff || Data.size() > INT_MAX)) {
Data.pop_front();
}
}
int size() const { return (int)Data.size(); }
const std::pair<double, T>& operator[](int index) const { return Data[index]; }
const T& last() const {
static const T fallback;
return Data.size() > 0 ? Data.back().second : fallback;
}
const double lastTs() const {
return Data.size() > 0 ? Data.back().first : 0;
}
};
extern TimeSeries<Eigen::Vector3d> posOffset_rawComputed; // , rotOffset_rawComputed;
extern TimeSeries<Eigen::Vector3d> posOffset_currentCal; // , rotOffset_currentCal;
extern TimeSeries<Eigen::Vector3d> posOffset_lastSample; // , rotOffset_lastSample;
extern TimeSeries<Eigen::Vector3d> posOffset_byRelPose;
extern TimeSeries<double> error_rawComputed, error_currentCal, error_byRelPose, error_currentCalRelPose;
extern TimeSeries<double> axisIndependence;
extern TimeSeries<double> computationTime;
extern TimeSeries<bool> calibrationApplied;
extern bool enableLogs;
void WriteLogAnnotation(const char* s);
void WriteLogEntry();
}
-310
View File
@@ -1,310 +0,0 @@
#include "stdafx.h"
#include "Configuration.h"
#include <picojson.h>
#include <string>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <limits>
static picojson::array FloatArray(const float *buf, int numFloats)
{
picojson::array arr;
for (int i = 0; i < numFloats; i++)
arr.push_back(picojson::value(double(buf[i])));
return arr;
}
static void LoadFloatArray(const picojson::value &obj, float *buf, int numFloats)
{
if (!obj.is<picojson::array>())
throw std::runtime_error("expected array, got " + obj.to_str());
auto &arr = obj.get<picojson::array>();
if (arr.size() != numFloats)
throw std::runtime_error("wrong buffer size");
for (int i = 0; i < numFloats; i++)
buf[i] = (float) arr[i].get<double>();
}
static void LoadStandby(StandbyDevice& device, picojson::value& value) {
if (!value.is<picojson::object>()) return;
auto& obj = value.get<picojson::object>();
const auto &system = obj["tracking_system"];
if (system.is<std::string>()) device.trackingSystem = system.get<std::string>();
const auto& model = obj["model"];
if (model.is<std::string>()) device.model = model.get<std::string>();
const auto& serial = obj["serial"];
if (serial.is<std::string>()) device.serial = serial.get<std::string>();
}
static void VisitAlignmentParams(CalibrationContext& ctx, std::function<void(const char *, double&)> MapParam) {
#define P(s) MapParam(#s, ctx.alignmentSpeedParams.s)
P(align_speed_tiny);
P(align_speed_small);
P(align_speed_large);
P(thr_trans_tiny);
P(thr_trans_small);
P(thr_trans_large);
P(thr_rot_tiny);
P(thr_rot_small);
P(thr_rot_large);
// Convert to double and back
double tmp = ctx.continuousCalibrationThreshold;
MapParam("continuousCalibrationThreshold", tmp);
ctx.continuousCalibrationThreshold = (float)tmp;
}
static void LoadAlignmentParams(CalibrationContext& ctx, picojson::value& value) {
ctx.ResetConfig();
if (!value.is<picojson::object>()) return;
auto& obj = value.get<picojson::object>();
VisitAlignmentParams(ctx, [&](auto name, auto& param) {
const picojson::value& node = obj[name];
if (node.is<double>()) {
param = (float)node.get<double>();
}
});
}
static picojson::object SaveAlignmentParams(CalibrationContext& ctx) {
picojson::object obj;
VisitAlignmentParams(ctx, [&](auto name, auto& param) {
obj[name].set<double>(param);
});
return obj;
}
static void ParseProfile(CalibrationContext &ctx, std::istream &stream)
{
picojson::value v;
std::string err = picojson::parse(v, stream);
if (!err.empty())
throw std::runtime_error(err);
auto arr = v.get<picojson::array>();
if (arr.size() < 1)
throw std::runtime_error("no profiles in file");
auto obj = arr[0].get<picojson::object>();
LoadAlignmentParams(ctx, obj["alignment_params"]);
ctx.referenceTrackingSystem = obj["reference_tracking_system"].get<std::string>();
ctx.targetTrackingSystem = obj["target_tracking_system"].get<std::string>();
ctx.calibratedRotation(0) = obj["roll"].get<double>();
ctx.calibratedRotation(1) = obj["yaw"].get<double>();
ctx.calibratedRotation(2) = obj["pitch"].get<double>();
ctx.calibratedTranslation(0) = obj["x"].get<double>();
ctx.calibratedTranslation(1) = obj["y"].get<double>();
ctx.calibratedTranslation(2) = obj["z"].get<double>();
LoadStandby(ctx.referenceStandby, obj["reference_device"]);
LoadStandby(ctx.targetStandby, obj["target_device"]);
if (obj["autostart_continuous_calibration"].evaluate_as_boolean()) {
ctx.state = CalibrationState::ContinuousStandby;
}
ctx.quashTargetInContinuous = obj["quash_target_in_continuous"].evaluate_as_boolean();
if (obj["scale"].is<double>())
ctx.calibratedScale = obj["scale"].get<double>();
else
ctx.calibratedScale = 1.0;
if (obj["calibration_speed"].is<double>())
ctx.calibrationSpeed = (CalibrationContext::Speed)(int) obj["calibration_speed"].get<double>();
if (obj["chaperone"].is<picojson::object>())
{
auto chaperone = obj["chaperone"].get<picojson::object>();
ctx.chaperone.autoApply = chaperone["auto_apply"].get<bool>();
LoadFloatArray(chaperone["play_space_size"], ctx.chaperone.playSpaceSize.v, 2);
LoadFloatArray(
chaperone["standing_center"],
(float *) ctx.chaperone.standingCenter.m,
sizeof(ctx.chaperone.standingCenter.m) / sizeof(float)
);
if (!chaperone["geometry"].is<picojson::array>())
throw std::runtime_error("chaperone geometry is not an array");
auto &geometry = chaperone["geometry"].get<picojson::array>();
if (geometry.size() > 0)
{
ctx.chaperone.geometry.resize(geometry.size() * sizeof(float) / sizeof(ctx.chaperone.geometry[0]));
LoadFloatArray(chaperone["geometry"], (float *) ctx.chaperone.geometry.data(), geometry.size());
ctx.chaperone.valid = true;
}
}
ctx.validProfile = true;
}
static void WriteStandby(StandbyDevice& device, picojson::value& value) {
auto obj = picojson::object();
obj["tracking_system"].set<std::string>(device.trackingSystem);
obj["model"].set<std::string>(device.model);
obj["serial"].set<std::string>(device.serial);
value.set<picojson::object>(obj);
}
static void WriteProfile(CalibrationContext &ctx, std::ostream &out)
{
if (!ctx.validProfile)
return;
picojson::object profile;
profile["alignment_params"].set<picojson::object>(SaveAlignmentParams(ctx));
profile["reference_tracking_system"].set<std::string>(ctx.referenceTrackingSystem);
profile["target_tracking_system"].set<std::string>(ctx.targetTrackingSystem);
profile["roll"].set<double>(ctx.calibratedRotation(0));
profile["yaw"].set<double>(ctx.calibratedRotation(1));
profile["pitch"].set<double>(ctx.calibratedRotation(2));
profile["x"].set<double>(ctx.calibratedTranslation(0));
profile["y"].set<double>(ctx.calibratedTranslation(1));
profile["z"].set<double>(ctx.calibratedTranslation(2));
profile["scale"].set<double>(ctx.calibratedScale);
WriteStandby(ctx.referenceStandby, profile["reference_device"]);
WriteStandby(ctx.targetStandby, profile["target_device"]);
bool isInContinuousCalibrationMode = ctx.state == CalibrationState::Continuous || ctx.state == CalibrationState::ContinuousStandby;
profile["autostart_continuous_calibration"].set<bool>(isInContinuousCalibrationMode);
profile["quash_target_in_continuous"].set<bool>(ctx.quashTargetInContinuous);
double speed = (int) ctx.calibrationSpeed;
profile["calibration_speed"].set<double>(speed);
if (ctx.chaperone.valid)
{
picojson::object chaperone;
chaperone["auto_apply"].set<bool>(ctx.chaperone.autoApply);
chaperone["play_space_size"].set<picojson::array>(FloatArray(ctx.chaperone.playSpaceSize.v, 2));
chaperone["standing_center"].set<picojson::array>(FloatArray(
(float *) ctx.chaperone.standingCenter.m,
sizeof(ctx.chaperone.standingCenter.m) / sizeof(float)
));
chaperone["geometry"].set<picojson::array>(FloatArray(
(float *) ctx.chaperone.geometry.data(),
sizeof(ctx.chaperone.geometry[0]) / sizeof(float) * ctx.chaperone.geometry.size()
));
profile["chaperone"].set<picojson::object>(chaperone);
}
picojson::value profileV;
profileV.set<picojson::object>(profile);
picojson::array profiles;
profiles.push_back(profileV);
picojson::value profilesV;
profilesV.set<picojson::array>(profiles);
out << profilesV.serialize(true);
}
static void LogRegistryResult(LSTATUS result)
{
char *message;
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, result, LANG_USER_DEFAULT, (LPSTR)&message, 0, NULL);
std::cerr << "Opening registry key: " << message << std::endl;
}
static const char *RegistryKey = "Software\\OpenVR-SpaceCalibrator";
static std::string ReadRegistryKey()
{
DWORD size = 0;
auto result = RegGetValueA(HKEY_CURRENT_USER_LOCAL_SETTINGS, RegistryKey, "Config", RRF_RT_REG_SZ, 0, 0, &size);
if (result != ERROR_SUCCESS)
{
LogRegistryResult(result);
return "";
}
std::string str;
str.resize(size);
result = RegGetValueA(HKEY_CURRENT_USER_LOCAL_SETTINGS, RegistryKey, "Config", RRF_RT_REG_SZ, 0, &str[0], &size);
if (result != ERROR_SUCCESS)
{
LogRegistryResult(result);
return "";
}
str.resize(size - 1);
return str;
}
static void WriteRegistryKey(std::string str)
{
HKEY hkey;
auto result = RegCreateKeyExA(HKEY_CURRENT_USER_LOCAL_SETTINGS, RegistryKey, 0, REG_NONE, 0, KEY_ALL_ACCESS, 0, &hkey, 0);
if (result != ERROR_SUCCESS)
{
LogRegistryResult(result);
return;
}
DWORD size = str.size() + 1;
result = RegSetValueExA(hkey, "Config", 0, REG_SZ, reinterpret_cast<const BYTE*>(str.c_str()), size);
if (result != ERROR_SUCCESS)
LogRegistryResult(result);
RegCloseKey(hkey);
}
void LoadProfile(CalibrationContext &ctx)
{
ctx.validProfile = false;
auto str = ReadRegistryKey();
if (str == "")
{
std::cout << "Profile is empty" << std::endl;
ctx.Clear();
return;
}
try
{
std::stringstream io(str);
ParseProfile(ctx, io);
std::cout << "Loaded profile" << std::endl;
}
catch (const std::runtime_error &e)
{
std::cerr << "Error loading profile: " << e.what() << std::endl;
}
}
void SaveProfile(CalibrationContext &ctx)
{
std::cout << "Saving profile to registry" << std::endl;
std::stringstream io;
WriteProfile(ctx, io);
WriteRegistryKey(io.str());
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "Calibration.h"
void LoadProfile(CalibrationContext &ctx);
void SaveProfile(CalibrationContext &ctx);
File diff suppressed because it is too large. Load diff
-4
View File
@@ -1,4 +0,0 @@
#pragma once
const unsigned int DroidSans_compressed_size = 134345;
extern const unsigned int DroidSans_compressed_data[134348 / 4];
-1
View File
@@ -1 +0,0 @@
#pragma once
-93
View File
@@ -1,93 +0,0 @@
#include "stdafx.h"
#include "IPCClient.h"
#include <string>
static std::string LastErrorString(DWORD lastError)
{
LPSTR buffer = nullptr;
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL
);
std::string message(buffer, size);
LocalFree(buffer);
return message;
}
IPCClient::~IPCClient()
{
if (pipe && pipe != INVALID_HANDLE_VALUE)
CloseHandle(pipe);
}
void IPCClient::Connect()
{
LPTSTR pipeName = TEXT(OPENVR_SPACECALIBRATOR_PIPE_NAME);
WaitNamedPipe(pipeName, 1000);
pipe = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (pipe == INVALID_HANDLE_VALUE)
{
throw std::runtime_error("Space Calibrator driver unavailable. Make sure SteamVR is running, and the Space Calibrator addon is enabled in SteamVR settings.");
}
DWORD mode = PIPE_READMODE_MESSAGE;
if (!SetNamedPipeHandleState(pipe, &mode, 0, 0))
{
throw std::runtime_error("Couldn't set pipe mode. Error: " + LastErrorString(GetLastError()));
}
auto response = SendBlocking(protocol::Request(protocol::RequestHandshake));
if (response.type != protocol::ResponseHandshake || response.protocol.version != protocol::Version)
{
throw std::runtime_error(
"Incorrect driver version installed, try reinstalling OpenVR-SpaceCalibrator. (Client: " +
std::to_string(protocol::Version) +
", Driver: " +
std::to_string(response.protocol.version) +
")"
);
}
}
protocol::Response IPCClient::SendBlocking(const protocol::Request &request)
{
Send(request);
return Receive();
}
void IPCClient::Send(const protocol::Request &request)
{
DWORD bytesWritten;
BOOL success = WriteFile(pipe, &request, sizeof request, &bytesWritten, 0);
if (!success)
{
throw std::runtime_error("Error writing IPC request. Error: " + LastErrorString(GetLastError()));
}
}
protocol::Response IPCClient::Receive()
{
protocol::Response response(protocol::ResponseInvalid);
DWORD bytesRead;
BOOL success = ReadFile(pipe, &response, sizeof response, &bytesRead, 0);
if (!success)
{
DWORD lastError = GetLastError();
if (lastError != ERROR_MORE_DATA)
{
throw std::runtime_error("Error reading IPC response. Error: " + LastErrorString(lastError));
}
}
if (bytesRead != sizeof response)
{
throw std::runtime_error("Invalid IPC response with size " + std::to_string(bytesRead));
}
return response;
}
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include "../Protocol.h"
class IPCClient
{
public:
~IPCClient();
void Connect();
protocol::Response SendBlocking(const protocol::Request &request);
void Send(const protocol::Request &request);
protocol::Response Receive();
private:
HANDLE pipe = INVALID_HANDLE_VALUE;
};
-1
View File
@@ -1 +0,0 @@
#pragma once
@@ -1,533 +0,0 @@
#include "stdafx.h"
#include "Calibration.h"
#include "Configuration.h"
#include "EmbeddedFiles.h"
#include "UserInterface.h"
#include <imgui/imgui.h>
#include <imgui/imgui_internal.h>
#include <imgui/imgui_impl_glfw.h>
#include <imgui/imgui_impl_opengl3.h>
#include <implot/implot.h>
#include <GL/gl3w.h>
#include <GLFW/glfw3.h>
#include <openvr.h>
#include <direct.h>
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define OPENVR_APPLICATION_KEY "pushrax.SpaceCalibrator"
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
void CreateConsole()
{
static bool created = false;
if (!created)
{
AllocConsole();
FILE *file = nullptr;
freopen_s(&file, "CONIN$", "r", stdin);
freopen_s(&file, "CONOUT$", "w", stdout);
freopen_s(&file, "CONOUT$", "w", stderr);
created = true;
}
}
//#define DEBUG_LOGS
void GLFWErrorCallback(int error, const char* description)
{
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
}
void openGLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
fprintf(stderr, "OpenGL Debug %u: %.*s\n", id, length, message);
}
static void HandleCommandLine(LPWSTR lpCmdLine);
static GLFWwindow *glfwWindow = nullptr;
static vr::VROverlayHandle_t overlayMainHandle = 0, overlayThumbnailHandle = 0;
static GLuint fboHandle = 0, fboTextureHandle = 0;
static int fboTextureWidth = 0, fboTextureHeight = 0;
static char cwd[MAX_PATH];
void CreateGLFWWindow()
{
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, false);
#ifdef DEBUG_LOGS
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
#endif
fboTextureWidth = 1200;
fboTextureHeight = 800;
glfwWindow = glfwCreateWindow(fboTextureWidth, fboTextureHeight, "OpenVR-SpaceCalibrator", NULL, NULL);
if (!glfwWindow)
throw std::runtime_error("Failed to create window");
glfwMakeContextCurrent(glfwWindow);
glfwSwapInterval(1);
gl3wInit();
glfwIconifyWindow(glfwWindow);
#ifdef DEBUG_LOGS
glDebugMessageCallback(openGLDebugCallback, nullptr);
glEnable(GL_DEBUG_OUTPUT);
#endif
ImGui::CreateContext();
ImPlot::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
io.IniFilename = nullptr;
io.Fonts->AddFontFromMemoryCompressedTTF(DroidSans_compressed_data, DroidSans_compressed_size, 24.0f);
ImGui_ImplGlfw_InitForOpenGL(glfwWindow, true);
ImGui_ImplOpenGL3_Init("#version 330");
ImGui::StyleColorsDark();
glGenTextures(1, &fboTextureHandle);
glBindTexture(GL_TEXTURE_2D, fboTextureHandle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, fboTextureWidth, fboTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenFramebuffers(1, &fboHandle);
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextureHandle, 0);
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, drawBuffers);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
throw std::runtime_error("OpenGL framebuffer incomplete");
}
}
void TryCreateVROverlay()
{
if (overlayMainHandle || !vr::VROverlay())
return;
vr::VROverlayError error = vr::VROverlay()->CreateDashboardOverlay(
"pushrax.SpaceCalibrator", "Space Cal",
&overlayMainHandle, &overlayThumbnailHandle
);
if (error == vr::VROverlayError_KeyInUse)
{
throw std::runtime_error("Another instance of OpenVR Space Calibrator is already running");
}
else if (error != vr::VROverlayError_None)
{
throw std::runtime_error("Error creating VR overlay: " + std::string(vr::VROverlay()->GetOverlayErrorNameFromEnum(error)));
}
vr::VROverlay()->SetOverlayWidthInMeters(overlayMainHandle, 3.0f);
vr::VROverlay()->SetOverlayInputMethod(overlayMainHandle, vr::VROverlayInputMethod_Mouse);
vr::VROverlay()->SetOverlayFlag(overlayMainHandle, vr::VROverlayFlags_SendVRDiscreteScrollEvents, true);
std::string iconPath = cwd;
iconPath += "\\icon.png";
vr::VROverlay()->SetOverlayFromFile(overlayThumbnailHandle, iconPath.c_str());
}
void ActivateMultipleDrivers()
{
vr::EVRSettingsError vrSettingsError;
bool enabled = vr::VRSettings()->GetBool(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool, &vrSettingsError);
if (vrSettingsError != vr::VRSettingsError_None)
{
std::string err = "Could not read \"" + std::string(vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool) + "\" setting: "
+ vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError);
throw std::runtime_error(err);
}
if (!enabled)
{
vr::VRSettings()->SetBool(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool, true, &vrSettingsError);
if (vrSettingsError != vr::VRSettingsError_None)
{
std::string err = "Could not set \"" + std::string(vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool) + "\" setting: "
+ vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError);
throw std::runtime_error(err);
}
std::cerr << "Enabled \"" << vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool << "\" setting" << std::endl;
}
else
{
std::cerr << "\"" << vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool << "\" setting previously enabled" << std::endl;
}
}
void InitVR()
{
auto initError = vr::VRInitError_None;
vr::VR_Init(&initError, vr::VRApplication_Other);
if (initError != vr::VRInitError_None)
{
auto error = vr::VR_GetVRInitErrorAsEnglishDescription(initError);
throw std::runtime_error("OpenVR error:" + std::string(error));
}
if (!vr::VR_IsInterfaceVersionValid(vr::IVRSystem_Version))
{
throw std::runtime_error("OpenVR error: Outdated IVRSystem_Version");
}
else if (!vr::VR_IsInterfaceVersionValid(vr::IVRSettings_Version))
{
throw std::runtime_error("OpenVR error: Outdated IVRSettings_Version");
}
else if (!vr::VR_IsInterfaceVersionValid(vr::IVROverlay_Version))
{
throw std::runtime_error("OpenVR error: Outdated IVROverlay_Version");
}
ActivateMultipleDrivers();
}
static char textBuf[0x400];
static bool immediateRedraw;
void RequestImmediateRedraw() {
immediateRedraw = true;
}
void RunLoop()
{
while (!glfwWindowShouldClose(glfwWindow))
{
TryCreateVROverlay();
double time = glfwGetTime();
CalibrationTick(time);
bool dashboardVisible = false;
int width, height;
glfwGetFramebufferSize(glfwWindow, &width, &height);
if (overlayMainHandle && vr::VROverlay())
{
auto &io = ImGui::GetIO();
dashboardVisible = vr::VROverlay()->IsActiveDashboardOverlay(overlayMainHandle);
static bool keyboardOpen = false, keyboardJustClosed = false;
// After closing the keyboard, this code waits one frame for ImGui to pick up the new text from SetActiveText
// before clearing the active widget. Then it waits another frame before allowing the keyboard to open again,
// otherwise it will do so instantly since WantTextInput is still true on the second frame.
if (keyboardJustClosed && keyboardOpen)
{
ImGui::ClearActiveID();
keyboardOpen = false;
}
else if (keyboardJustClosed)
{
keyboardJustClosed = false;
}
else if (!io.WantTextInput)
{
// User might close the keyboard without hitting Done, so we unset the flag to allow it to open again.
keyboardOpen = false;
}
else if (io.WantTextInput && !keyboardOpen && !keyboardJustClosed)
{
int id = ImGui::GetActiveID();
auto textInfo = ImGui::GetInputTextState(id);
textBuf[0] = 0;
int len = WideCharToMultiByte(CP_ACP, 0, (LPCWCH)textInfo->TextW.Data, textInfo->TextW.Size, textBuf, sizeof(textBuf), NULL, NULL);
textBuf[min(len, sizeof(textBuf) - 1)] = 0;
uint32_t unFlags = 0; // EKeyboardFlags
vr::VROverlay()->ShowKeyboardForOverlay(
overlayMainHandle, vr::k_EGamepadTextInputModeNormal, vr::k_EGamepadTextInputLineModeSingleLine,
unFlags, "Space Calibrator Overlay", sizeof textBuf, textBuf, 0
);
keyboardOpen = true;
}
vr::VREvent_t vrEvent;
while (vr::VROverlay()->PollNextOverlayEvent(overlayMainHandle, &vrEvent, sizeof(vrEvent)))
{
switch (vrEvent.eventType) {
case vr::VREvent_MouseMove:
io.AddMousePosEvent(vrEvent.data.mouse.x, vrEvent.data.mouse.y);
break;
case vr::VREvent_MouseButtonDown:
io.AddMouseButtonEvent(vrEvent.data.mouse.button == vr::VRMouseButton_Left ? 0 : 1, true);
break;
case vr::VREvent_MouseButtonUp:
io.AddMouseButtonEvent(vrEvent.data.mouse.button == vr::VRMouseButton_Left ? 0 : 1, false);
break;
case vr::VREvent_ScrollDiscrete:
{
float x = vrEvent.data.scroll.xdelta * 360.0f * 8.0f;
float y = vrEvent.data.scroll.ydelta * 360.0f * 8.0f;
io.AddMouseWheelEvent(x, y);
break;
}
case vr::VREvent_KeyboardDone: {
vr::VROverlay()->GetKeyboardText(textBuf, sizeof textBuf);
int id = ImGui::GetActiveID();
auto textInfo = ImGui::GetInputTextState(id);
int bufSize = MultiByteToWideChar(CP_ACP, 0, textBuf, -1, NULL, 0);
textInfo->TextW.resize(bufSize);
MultiByteToWideChar(CP_ACP, 0, textBuf, -1, (LPWSTR)textInfo->TextW.Data, bufSize);
textInfo->CurLenW = bufSize;
textInfo->CurLenA = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)textInfo->TextW.Data, textInfo->TextW.Size, NULL, 0, NULL, NULL);
keyboardJustClosed = true;
break;
}
case vr::VREvent_Quit:
return;
}
}
}
auto &io = ImGui::GetIO();
io.DisplaySize = ImVec2((float) fboTextureWidth, (float) fboTextureHeight);
io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
io.ConfigFlags = io.ConfigFlags & ~ImGuiConfigFlags_NoMouseCursorChange;
if (dashboardVisible) {
io.ConfigFlags = io.ConfigFlags | ImGuiConfigFlags_NoMouseCursorChange;
}
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
BuildMainWindow(dashboardVisible);
ImGui::Render();
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
glViewport(0, 0, fboTextureWidth, fboTextureHeight);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (width && height)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboHandle);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glfwSwapBuffers(glfwWindow);
}
if (dashboardVisible)
{
vr::Texture_t vrTex;
vrTex.eType = vr::TextureType_OpenGL;
vrTex.eColorSpace = vr::ColorSpace_Auto;
vrTex.handle = (void *)
#if defined _WIN64 || defined _LP64
(uint64_t)
#endif
fboTextureHandle;
vr::HmdVector2_t mouseScale = { (float) fboTextureWidth, (float) fboTextureHeight };
vr::VROverlay()->SetOverlayTexture(overlayMainHandle, &vrTex);
vr::VROverlay()->SetOverlayMouseScale(overlayMainHandle, &mouseScale);
}
const double dashboardInterval = 1.0 / 90.0; // fps
double waitEventsTimeout = CalCtx.wantedUpdateInterval;
if (dashboardVisible && waitEventsTimeout > dashboardInterval)
waitEventsTimeout = dashboardInterval;
if (immediateRedraw) {
waitEventsTimeout = 0;
immediateRedraw = false;
}
glfwWaitEventsTimeout(waitEventsTimeout);
}
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
_getcwd(cwd, MAX_PATH);
HandleCommandLine(lpCmdLine);
#ifdef DEBUG_LOGS
CreateConsole();
#endif
if (!glfwInit())
{
MessageBox(nullptr, L"Failed to initialize GLFW", L"", 0);
return 0;
}
glfwSetErrorCallback(GLFWErrorCallback);
try {
InitVR();
CreateGLFWWindow();
InitCalibrator();
LoadProfile(CalCtx);
RunLoop();
vr::VR_Shutdown();
if (fboHandle)
glDeleteFramebuffers(1, &fboHandle);
if (fboTextureHandle)
glDeleteTextures(1, &fboTextureHandle);
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}
catch (std::runtime_error &e)
{
std::cerr << "Runtime error: " << e.what() << std::endl;
wchar_t message[1024];
swprintf(message, 1024, L"%hs", e.what());
MessageBox(nullptr, message, L"Runtime Error", 0);
}
if (glfwWindow)
glfwDestroyWindow(glfwWindow);
glfwTerminate();
return 0;
}
static void HandleCommandLine(LPWSTR lpCmdLine)
{
if (lstrcmp(lpCmdLine, L"-openvrpath") == 0)
{
auto vrErr = vr::VRInitError_None;
vr::VR_Init(&vrErr, vr::VRApplication_Utility);
if (vrErr == vr::VRInitError_None)
{
char cruntimePath[MAX_PATH] = { 0 };
unsigned int pathLen;
vr::VR_GetRuntimePath(cruntimePath, MAX_PATH, &pathLen);
printf("%s", cruntimePath);
vr::VR_Shutdown();
exit(0);
}
fprintf(stderr, "Failed to initialize OpenVR: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(vrErr));
vr::VR_Shutdown();
exit(-2);
}
else if (lstrcmp(lpCmdLine, L"-installmanifest") == 0)
{
auto vrErr = vr::VRInitError_None;
vr::VR_Init(&vrErr, vr::VRApplication_Utility);
if (vrErr == vr::VRInitError_None)
{
if (vr::VRApplications()->IsApplicationInstalled(OPENVR_APPLICATION_KEY))
{
char oldWd[MAX_PATH] = { 0 };
auto vrAppErr = vr::VRApplicationError_None;
vr::VRApplications()->GetApplicationPropertyString(OPENVR_APPLICATION_KEY, vr::VRApplicationProperty_WorkingDirectory_String, oldWd, MAX_PATH, &vrAppErr);
if (vrAppErr != vr::VRApplicationError_None)
{
fprintf(stderr, "Failed to get old working dir, skipping removal: %s\n", vr::VRApplications()->GetApplicationsErrorNameFromEnum(vrAppErr));
}
else
{
std::string manifestPath = oldWd;
manifestPath += "\\manifest.vrmanifest";
std::cout << "Removing old manifest path: " << manifestPath << std::endl;
vr::VRApplications()->RemoveApplicationManifest(manifestPath.c_str());
}
}
std::string manifestPath = cwd;
manifestPath += "\\manifest.vrmanifest";
std::cout << "Adding manifest path: " << manifestPath << std::endl;
auto vrAppErr = vr::VRApplications()->AddApplicationManifest(manifestPath.c_str());
if (vrAppErr != vr::VRApplicationError_None)
{
fprintf(stderr, "Failed to add manifest: %s\n", vr::VRApplications()->GetApplicationsErrorNameFromEnum(vrAppErr));
}
else
{
vr::VRApplications()->SetApplicationAutoLaunch(OPENVR_APPLICATION_KEY, true);
}
vr::VR_Shutdown();
exit(-2);
}
fprintf(stderr, "Failed to initialize OpenVR: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(vrErr));
vr::VR_Shutdown();
exit(-2);
}
else if (lstrcmp(lpCmdLine, L"-removemanifest") == 0)
{
auto vrErr = vr::VRInitError_None;
vr::VR_Init(&vrErr, vr::VRApplication_Utility);
if (vrErr == vr::VRInitError_None)
{
if (vr::VRApplications()->IsApplicationInstalled(OPENVR_APPLICATION_KEY))
{
std::string manifestPath = cwd;
manifestPath += "\\manifest.vrmanifest";
std::cout << "Removing manifest path: " << manifestPath << std::endl;
vr::VRApplications()->RemoveApplicationManifest(manifestPath.c_str());
}
vr::VR_Shutdown();
exit(0);
}
fprintf(stderr, "Failed to initialize OpenVR: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(vrErr));
vr::VR_Shutdown();
exit(-2);
}
else if (lstrcmp(lpCmdLine, L"-activatemultipledrivers") == 0)
{
int ret = -2;
auto vrErr = vr::VRInitError_None;
vr::VR_Init(&vrErr, vr::VRApplication_Utility);
if (vrErr == vr::VRInitError_None)
{
try
{
ActivateMultipleDrivers();
ret = 0;
}
catch (std::runtime_error &e)
{
std::cerr << e.what() << std::endl;
}
}
else
{
fprintf(stderr, "Failed to initialize OpenVR: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(vrErr));
}
vr::VR_Shutdown();
exit(ret);
}
}
Binary file not shown.
@@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{63BAE169-D595-4464-8E7E-50D6FD379348}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>OpenVRSpaceCalibrator</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\lib\imgui;..\lib\gl3w\include;..\lib\glfw\include;..\lib\openvr;..\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\lib\glfw\lib-vc2015;..\lib\openvr\lib\win64</AdditionalLibraryDirectories>
<AdditionalDependencies>opengl32.lib;glfw3.lib;openvr_api.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\lib\imgui;..\lib\gl3w\include;..\lib\glfw\include;..\lib\openvr;..\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\lib\glfw\lib-vc2015;..\lib\openvr\lib\win64</AdditionalLibraryDirectories>
<AdditionalDependencies>opengl32.lib;glfw3.lib;openvr_api.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\lib\implot\implot.h" />
<ClInclude Include="..\lib\implot\implot_internal.h" />
<ClInclude Include="..\Version.h" />
<ClInclude Include="Calibration.h" />
<ClInclude Include="CalibrationCalc.h" />
<ClInclude Include="CalibrationMetrics.h" />
<ClInclude Include="Configuration.h" />
<ClInclude Include="ContinuousCalibration.h" />
<ClInclude Include="EmbeddedFiles.h" />
<ClInclude Include="IPCClient.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="UserInterface.h" />
<ClInclude Include="VRState.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\gl3w\src\gl3w.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_demo.cpp" />
<ClCompile Include="..\lib\imgui\imgui_draw.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_impl_glfw.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_impl_opengl3.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_tables.cpp" />
<ClCompile Include="..\lib\imgui\imgui_widgets.cpp" />
<ClCompile Include="..\lib\implot\implot.cpp" />
<ClCompile Include="..\lib\implot\implot_demo.cpp" />
<ClCompile Include="..\lib\implot\implot_items.cpp" />
<ClCompile Include="Calibration.cpp" />
<ClCompile Include="CalibrationCalc.cpp" />
<ClCompile Include="CalibrationDebug.cpp" />
<ClCompile Include="CalibrationMetrics.cpp" />
<ClCompile Include="Configuration.cpp" />
<ClCompile Include="EmbeddedFiles.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="IPCClient.cpp" />
<ClCompile Include="OpenVR-SpaceCalibrator.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="UserInterface.cpp" />
<ClCompile Include="VRState.cpp" />
</ItemGroup>
<ItemGroup>
<Image Include="OpenVR-SpaceCalibrator.ico" />
<Image Include="small.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="OpenVR-SpaceCalibrator.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -1,151 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\ImGui">
<UniqueIdentifier>{948e4c13-6f2b-44e3-a8b7-1a12c79aeeb7}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\GL">
<UniqueIdentifier>{d156ae5c-9fe6-41f0-a34e-333dc7a6b2f8}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\ImGui\ImPlot">
<UniqueIdentifier>{7334a0fc-18cd-446d-b621-6dadb942fe6f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Calibration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EmbeddedFiles.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UserInterface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Configuration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IPCClient.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CalibrationCalc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\implot\implot.h">
<Filter>Source Files\ImGui\ImPlot</Filter>
</ClInclude>
<ClInclude Include="..\lib\implot\implot_internal.h">
<Filter>Source Files\ImGui\ImPlot</Filter>
</ClInclude>
<ClInclude Include="ContinuousCalibration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VRState.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CalibrationMetrics.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Calibration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_impl_glfw.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_impl_opengl3.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="..\lib\gl3w\src\gl3w.c">
<Filter>Source Files\GL</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_draw.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="EmbeddedFiles.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UserInterface.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Configuration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IPCClient.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="OpenVR-SpaceCalibrator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CalibrationCalc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_tables.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_widgets.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="..\lib\implot\implot.cpp">
<Filter>Source Files\ImGui\ImPlot</Filter>
</ClCompile>
<ClCompile Include="..\lib\implot\implot_demo.cpp">
<Filter>Source Files\ImGui\ImPlot</Filter>
</ClCompile>
<ClCompile Include="..\lib\implot\implot_items.cpp">
<Filter>Source Files\ImGui\ImPlot</Filter>
</ClCompile>
<ClCompile Include="..\lib\imgui\imgui_demo.cpp">
<Filter>Source Files\ImGui</Filter>
</ClCompile>
<ClCompile Include="CalibrationDebug.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VRState.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CalibrationMetrics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Image Include="small.ico">
<Filter>Resource Files</Filter>
</Image>
<Image Include="OpenVR-SpaceCalibrator.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="OpenVR-SpaceCalibrator.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>
Binary file not shown.
-766
View File
@@ -1,766 +0,0 @@
#include "stdafx.h"
#include "UserInterface.h"
#include "Calibration.h"
#include "Configuration.h"
#include "VRState.h"
#include "CalibrationMetrics.h"
#include "../Version.h"
#include <thread>
#include <string>
#include <vector>
#include <algorithm>
#include <imgui/imgui.h>
void TextWithWidth(const char *label, const char *text, float width);
VRState LoadVRState();
void BuildSystemSelection(const VRState &state);
void BuildDeviceSelections(const VRState &state);
void BuildProfileEditor();
void BuildMenu(bool runningInOverlay);
static const ImGuiWindowFlags bareWindowFlags =
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoCollapse;
void BuildContinuousCalDisplay();
void ShowVersionLine();
static bool runningInOverlay;
void BuildMainWindow(bool runningInOverlay_)
{
runningInOverlay = runningInOverlay_;
bool continuousCalibration = CalCtx.state == CalibrationState::Continuous || CalCtx.state == CalibrationState::ContinuousStandby;
auto &io = ImGui::GetIO();
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always);
if (!ImGui::Begin("OpenVRSpaceCalibrator", nullptr, bareWindowFlags))
{
ImGui::End();
return;
}
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImGui::GetStyleColorVec4(ImGuiCol_Button));
if (continuousCalibration) {
BuildContinuousCalDisplay();
}
else {
auto state = LoadVRState();
ImGui::BeginDisabled(CalCtx.state == CalibrationState::Continuous);
BuildSystemSelection(state);
BuildDeviceSelections(state);
ImGui::EndDisabled();
BuildMenu(runningInOverlay);
}
ShowVersionLine();
ImGui::PopStyleColor();
ImGui::End();
}
void ShowVersionLine() {
ImGui::SetNextWindowPos(ImVec2(10.0f, ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing()));
if (!ImGui::BeginChild("bottom line", ImVec2(ImGui::GetWindowWidth() - 20.0f, ImGui::GetFrameHeightWithSpacing() * 2), false)) {
ImGui::EndChild();
return;
}
ImGui::Text("OpenVR Space Calibrator v" SPACECAL_VERSION_STRING " - by tach/pushrax/bd_");
if (runningInOverlay)
{
ImGui::SameLine();
ImGui::Text("- close VR overlay to use mouse");
}
ImGui::EndChild();
}
void CCal_BasicInfo();
void CCal_AlignParams();
void BuildContinuousCalDisplay() {
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImGui::GetWindowSize());
ImGui::SetNextWindowBgAlpha(1);
if (!ImGui::Begin("Continuous Calibration", nullptr,
bareWindowFlags & ~ImGuiWindowFlags_NoTitleBar
)) {
ImGui::End();
return;
}
ImVec2 contentRegion;
contentRegion.x = ImGui::GetWindowContentRegionWidth();
contentRegion.y = ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() * 2.1;
if (!ImGui::BeginChild("CCalDisplayFrame", contentRegion, false)) {
ImGui::EndChild();
return;
}
if (ImGui::BeginTabBar("CCalTabs", 0)) {
if (ImGui::BeginTabItem("Status")) {
CCal_BasicInfo();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("More Graphs")) {
ShowCalibrationDebug(2, 3);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Alignment speeds")) {
CCal_AlignParams();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::EndChild();
ShowVersionLine();
ImGui::End();
}
static void ScaledDragFloat(const char* label, double& f, double scale, double min, double max, int flags = ImGuiSliderFlags_AlwaysClamp) {
float v = (float) (f * scale);
ImGui::SliderFloat(label, &v, (float)min, (float)max, "%1.2f", flags);
f = v / scale;
}
void CCal_AlignParams() {
if (ImGui::Button("Reset settings")) {
CalCtx.ResetConfig();
}
ImGui::SliderFloat("Recalibration threshold", &CalCtx.continuousCalibrationThreshold, 1.01f, 10.0f, "%1.1f", 0);
if (ImGui::IsItemHovered(0)) {
ImGui::SetTooltip("Controls how confident SpaceCalibrator must be in the new calibration before updating the calibration.\n"
"Higher values calibrate less frequently, and may be useful on systems with lots of tracker drift."
);
}
ImGui::Separator();
ImGui::TextWrapped(
"Calibration speeds: SpaceCalibrator uses up to three different speeds at which it drags the calibration back into "
"position when drift occurs. These settings control how far off the calibration should be before going back to low speed (for "
"Decel) or going to higher speeds (for Slow and Fast)."
);
if (ImGui::BeginTable("SpeedThresholds", 3, 0)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
ImGui::Text("Translation (mm)");
ImGui::TableSetColumnIndex(2);
ImGui::Text("Rotation (degrees)");
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Decel");
ImGui::TableSetColumnIndex(1);
ScaledDragFloat("##TransDecel", CalCtx.alignmentSpeedParams.thr_trans_tiny, 1000.0, 0, 20.0);
ImGui::TableSetColumnIndex(2);
ScaledDragFloat("##RotDecel", CalCtx.alignmentSpeedParams.thr_rot_tiny, 180.0 / EIGEN_PI, 0, 5.0);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Slow");
ImGui::TableSetColumnIndex(1);
ScaledDragFloat("##TransSlow", CalCtx.alignmentSpeedParams.thr_trans_small, 1000.0,
CalCtx.alignmentSpeedParams.thr_trans_tiny * 1000.0, 20.0);
ImGui::TableSetColumnIndex(2);
ScaledDragFloat("##RotSlow", CalCtx.alignmentSpeedParams.thr_rot_small, 180.0 / EIGEN_PI,
CalCtx.alignmentSpeedParams.thr_rot_tiny * (180.0 / EIGEN_PI), 10.0);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Fast");
ImGui::TableSetColumnIndex(1);
ScaledDragFloat("##TransFast", CalCtx.alignmentSpeedParams.thr_trans_large, 1000.0,
CalCtx.alignmentSpeedParams.thr_trans_small * 1000.0, 50.0);
ImGui::TableSetColumnIndex(2);
ScaledDragFloat("##RotFast", CalCtx.alignmentSpeedParams.thr_rot_large, 180.0 / EIGEN_PI,
CalCtx.alignmentSpeedParams.thr_rot_small * (180.0 / EIGEN_PI), 20.0);
ImGui::EndTable();
}
ImGui::Separator();
ImGui::Text("Alignment speeds");
ScaledDragFloat("Decel", CalCtx.alignmentSpeedParams.align_speed_tiny, 1.0, 0, 2.0, 0);
ScaledDragFloat("Slow", CalCtx.alignmentSpeedParams.align_speed_small, 1.0, 0, 2.0, 0);
ScaledDragFloat("Fast", CalCtx.alignmentSpeedParams.align_speed_large, 1.0, 0, 2.0, 0);
}
void CCal_BasicInfo() {
if (ImGui::BeginTable("DeviceInfo", 2, 0)) {
ImGui::TableSetupColumn("Reference device");
ImGui::TableSetupColumn("Target device");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::BeginGroup();
ImGui::Text("%s / %s / %s",
CalCtx.referenceStandby.trackingSystem.c_str(),
CalCtx.referenceStandby.model.c_str(),
CalCtx.referenceStandby.serial.c_str()
);
const char* status;
if (CalCtx.referenceID < 0) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFF000080);
status = "NOT FOUND";
}
else if (!CalCtx.ReferencePoseIsValid()) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFFFF0080);
status = "NOT TRACKING";
}
else {
status = "OK";
}
ImGui::Text("Status: %s", status);
ImGui::EndGroup();
ImGui::TableSetColumnIndex(1);
ImGui::BeginGroup();
ImGui::Text("%s / %s / %s",
CalCtx.targetStandby.trackingSystem.c_str(),
CalCtx.targetStandby.model.c_str(),
CalCtx.targetStandby.serial.c_str()
);
if (CalCtx.targetID < 0) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFF000080);
status = "NOT FOUND";
}
else if (!CalCtx.TargetPoseIsValid()) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFFFF0080);
status = "NOT TRACKING";
}
else {
status = "OK";
}
ImGui::Text("Status: %s", status);
ImGui::EndGroup();
ImGui::EndTable();
}
float width = ImGui::GetWindowContentRegionWidth(), scale = 1.0f;
if (ImGui::BeginTable("##CCal_Cancel", Metrics::enableLogs ? 3 : 2, 0, ImVec2(width * scale, ImGui::GetTextLineHeight() * 2))) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Button("Cancel Continuous Calibration", ImVec2(-FLT_MIN, 0.0f))) {
EndContinuousCalibration();
}
ImGui::TableSetColumnIndex(1);
if (ImGui::Button("Debug: Force break calibration", ImVec2(-FLT_MIN, 0.0f))) {
DebugApplyRandomOffset();
}
if (Metrics::enableLogs) {
ImGui::TableSetColumnIndex(2);
if (ImGui::Button("Debug: Mark logs", ImVec2(-FLT_MIN, 0.0f))) {
Metrics::WriteLogAnnotation("MARK LOGS");
}
}
ImGui::EndTable();
}
ImGui::Checkbox("Hide target device from application", &CalCtx.quashTargetInContinuous);
ImGui::SameLine();
ImGui::Checkbox("Enable static recalibration", &CalCtx.enableStaticRecalibration);
ImGui::SameLine();
ImGui::Checkbox("Enable debug logs", &Metrics::enableLogs);
// Status field...
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 1));
for (const auto& msg : CalCtx.messages) {
if (msg.type == CalibrationContext::Message::String) {
ImGui::TextWrapped("> %s", msg.str.c_str());
}
}
ImGui::PopStyleColor();
ShowCalibrationDebug(1, 3);
}
void BuildMenu(bool runningInOverlay)
{
auto &io = ImGui::GetIO();
ImGuiStyle &style = ImGui::GetStyle();
ImGui::Text("");
if (CalCtx.state == CalibrationState::None)
{
if (CalCtx.validProfile && !CalCtx.enabled)
{
ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1), "Reference (%s) HMD not detected, profile disabled", CalCtx.referenceTrackingSystem.c_str());
ImGui::Text("");
}
float width = ImGui::GetWindowContentRegionWidth(), scale = 1.0f;
if (CalCtx.validProfile)
{
width -= style.FramePadding.x * 4.0f;
scale = 1.0f / 4.0f;
}
if (ImGui::Button("Start Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
ImGui::OpenPopup("Calibration Progress");
StartCalibration();
}
ImGui::SameLine();
if (ImGui::Button("Continuous Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2))) {
StartContinuousCalibration();
}
if (CalCtx.validProfile)
{
ImGui::SameLine();
if (ImGui::Button("Edit Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
CalCtx.state = CalibrationState::Editing;
}
ImGui::SameLine();
if (ImGui::Button("Clear Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
CalCtx.Clear();
SaveProfile(CalCtx);
}
}
width = ImGui::GetWindowContentRegionWidth();
scale = 1.0f;
if (CalCtx.chaperone.valid)
{
width -= style.FramePadding.x * 2.0f;
scale = 0.5;
}
ImGui::Text("");
if (ImGui::Button("Copy Chaperone Bounds to profile", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
LoadChaperoneBounds();
SaveProfile(CalCtx);
}
if (CalCtx.chaperone.valid)
{
ImGui::SameLine();
if (ImGui::Button("Paste Chaperone Bounds", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
ApplyChaperoneBounds();
}
if (ImGui::Checkbox(" Paste Chaperone Bounds automatically when geometry resets", &CalCtx.chaperone.autoApply))
{
SaveProfile(CalCtx);
}
}
ImGui::Text("");
auto speed = CalCtx.calibrationSpeed;
ImGui::Columns(4, NULL, false);
ImGui::Text("Calibration Speed");
ImGui::NextColumn();
if (ImGui::RadioButton(" Fast ", speed == CalibrationContext::FAST))
CalCtx.calibrationSpeed = CalibrationContext::FAST;
ImGui::NextColumn();
if (ImGui::RadioButton(" Slow ", speed == CalibrationContext::SLOW))
CalCtx.calibrationSpeed = CalibrationContext::SLOW;
ImGui::NextColumn();
if (ImGui::RadioButton(" Very Slow ", speed == CalibrationContext::VERY_SLOW))
CalCtx.calibrationSpeed = CalibrationContext::VERY_SLOW;
ImGui::Columns(1);
}
else if (CalCtx.state == CalibrationState::Editing)
{
BuildProfileEditor();
if (ImGui::Button("Save Profile", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2)))
{
SaveProfile(CalCtx);
CalCtx.state = CalibrationState::None;
}
}
else
{
ImGui::Button("Calibration in progress...", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2));
}
ImGui::SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x - 40.0f, io.DisplaySize.y - 40.0f), ImGuiCond_Always);
if (ImGui::BeginPopupModal("Calibration Progress", nullptr, bareWindowFlags))
{
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImVec4(0, 0, 0, 1));
for (auto &message : CalCtx.messages)
{
switch (message.type)
{
case CalibrationContext::Message::String:
ImGui::TextWrapped(message.str.c_str());
break;
case CalibrationContext::Message::Progress:
float fraction = (float)message.progress / (float)message.target;
ImGui::Text("");
ImGui::ProgressBar(fraction, ImVec2(-1.0f, 0.0f), "");
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFontSize() - style.FramePadding.y * 2);
ImGui::Text(" %d%%", (int)(fraction * 100));
break;
}
}
ImGui::PopStyleColor();
if (CalCtx.state == CalibrationState::None)
{
ImGui::Text("");
if (ImGui::Button("Close", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2)))
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void BuildSystemSelection(const VRState &state)
{
if (state.trackingSystems.empty())
{
ImGui::Text("No tracked devices are present");
return;
}
ImGuiStyle &style = ImGui::GetStyle();
float paneWidth = ImGui::GetWindowContentRegionWidth() / 2 - style.FramePadding.x;
TextWithWidth("ReferenceSystemLabel", "Reference Space", paneWidth);
ImGui::SameLine();
TextWithWidth("TargetSystemLabel", "Target Space", paneWidth);
int currentReferenceSystem = -1;
int currentTargetSystem = -1;
int firstReferenceSystemNotTargetSystem = -1;
std::vector<const char *> referenceSystems;
for (auto &str : state.trackingSystems)
{
if (str == CalCtx.referenceTrackingSystem)
{
currentReferenceSystem = (int) referenceSystems.size();
}
else if (firstReferenceSystemNotTargetSystem == -1 && str != CalCtx.targetTrackingSystem)
{
firstReferenceSystemNotTargetSystem = (int) referenceSystems.size();
}
referenceSystems.push_back(str.c_str());
}
if (currentReferenceSystem == -1 && CalCtx.referenceTrackingSystem == "")
{
if (CalCtx.state == CalibrationState::ContinuousStandby) {
auto iter = std::find(state.trackingSystems.begin(), state.trackingSystems.end(), CalCtx.referenceStandby.trackingSystem);
if (iter != state.trackingSystems.end()) {
currentReferenceSystem = iter - state.trackingSystems.begin();
}
}
else {
currentReferenceSystem = firstReferenceSystemNotTargetSystem;
}
}
ImGui::PushItemWidth(paneWidth);
ImGui::Combo("##ReferenceTrackingSystem", &currentReferenceSystem, &referenceSystems[0], (int) referenceSystems.size());
if (currentReferenceSystem != -1 && currentReferenceSystem < (int) referenceSystems.size())
{
CalCtx.referenceTrackingSystem = std::string(referenceSystems[currentReferenceSystem]);
if (CalCtx.referenceTrackingSystem == CalCtx.targetTrackingSystem)
CalCtx.targetTrackingSystem = "";
}
if (CalCtx.targetTrackingSystem == "") {
if (CalCtx.state == CalibrationState::ContinuousStandby) {
auto iter = std::find(state.trackingSystems.begin(), state.trackingSystems.end(), CalCtx.targetStandby.trackingSystem);
if (iter != state.trackingSystems.end()) {
currentTargetSystem = iter - state.trackingSystems.begin();
}
}
else {
currentTargetSystem = 0;
}
}
std::vector<const char *> targetSystems;
for (auto &str : state.trackingSystems)
{
if (str != CalCtx.referenceTrackingSystem)
{
if (str != "" && str == CalCtx.targetTrackingSystem)
currentTargetSystem = (int) targetSystems.size();
targetSystems.push_back(str.c_str());
}
}
ImGui::SameLine();
ImGui::Combo("##TargetTrackingSystem", &currentTargetSystem, &targetSystems[0], (int) targetSystems.size());
if (currentTargetSystem != -1 && currentTargetSystem < targetSystems.size())
{
CalCtx.targetTrackingSystem = std::string(targetSystems[currentTargetSystem]);
}
ImGui::PopItemWidth();
}
void AppendSeparated(std::string &buffer, const std::string &suffix)
{
if (!buffer.empty())
buffer += " | ";
buffer += suffix;
}
std::string LabelString(const VRDevice &device)
{
std::string label;
/*if (device.controllerRole == vr::TrackedControllerRole_LeftHand)
label = "Left Controller";
else if (device.controllerRole == vr::TrackedControllerRole_RightHand)
label = "Right Controller";
else if (device.deviceClass == vr::TrackedDeviceClass_Controller)
label = "Controller";
else if (device.deviceClass == vr::TrackedDeviceClass_HMD)
label = "HMD";
else if (device.deviceClass == vr::TrackedDeviceClass_GenericTracker)
label = "Tracker";*/
AppendSeparated(label, device.model);
AppendSeparated(label, device.serial);
return label;
}
std::string LabelString(const StandbyDevice& device) {
std::string label("< ");
label += device.model;
AppendSeparated(label, device.serial);
label += " >";
return label;
}
void BuildDeviceSelection(const VRState &state, int &initialSelected, const std::string &system, StandbyDevice &standbyDevice)
{
int selected = initialSelected;
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1), "Devices from: %s", system.c_str());
if (selected != -1)
{
bool matched = false;
for (auto &device : state.devices)
{
if (device.trackingSystem != system)
continue;
if (selected == device.id)
{
matched = true;
break;
}
}
if (!matched)
{
// Device is no longer present.
selected = -1;
}
}
bool standby = CalCtx.state == CalibrationState::ContinuousStandby;
if (selected == -1 && !standby)
{
for (auto &device : state.devices)
{
if (device.trackingSystem != system)
continue;
if (device.controllerRole == vr::TrackedControllerRole_LeftHand)
{
selected = device.id;
break;
}
}
if (selected == -1) {
for (auto& device : state.devices)
{
if (device.trackingSystem != system)
continue;
selected = device.id;
break;
}
}
}
if (selected == -1 && standby) {
bool present = false;
for (auto& device : state.devices)
{
if (device.trackingSystem != system)
continue;
if (standbyDevice.model != device.model) continue;
if (standbyDevice.serial != device.serial) continue;
present = true;
break;
}
if (!present) {
auto label = LabelString(standbyDevice);
ImGui::Selectable(label.c_str(), true);
}
}
for (auto &device : state.devices)
{
if (device.trackingSystem != system)
continue;
auto label = LabelString(device);
if (ImGui::Selectable(label.c_str(), selected == device.id)) {
selected = device.id;
}
}
if (selected != initialSelected) {
const auto& device = std::find_if(state.devices.begin(), state.devices.end(), [&](const auto& d) { return d.id == selected; });
if (device == state.devices.end()) return;
initialSelected = selected;
standbyDevice.trackingSystem = system;
standbyDevice.model = device->model;
standbyDevice.serial = device->serial;
}
}
void BuildDeviceSelections(const VRState &state)
{
ImGuiStyle &style = ImGui::GetStyle();
ImVec2 paneSize(ImGui::GetWindowContentRegionWidth() / 2 - style.FramePadding.x, ImGui::GetTextLineHeightWithSpacing() * 5 + style.ItemSpacing.y * 4);
ImGui::BeginChild("left device pane", paneSize, true);
BuildDeviceSelection(state, CalCtx.referenceID, CalCtx.referenceTrackingSystem, CalCtx.referenceStandby);
ImGui::EndChild();
ImGui::SameLine();
ImGui::BeginChild("right device pane", paneSize, true);
BuildDeviceSelection(state, CalCtx.targetID, CalCtx.targetTrackingSystem, CalCtx.targetStandby);
ImGui::EndChild();
if (ImGui::Button("Identify selected devices (blinks LED or vibrates)", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeightWithSpacing() + 4.0f)))
{
for (unsigned i = 0; i < 100; ++i)
{
vr::VRSystem()->TriggerHapticPulse(CalCtx.targetID, 0, 2000);
vr::VRSystem()->TriggerHapticPulse(CalCtx.referenceID, 0, 2000);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
}
VRState LoadVRState() {
VRState state = VRState::Load();
auto& trackingSystems = state.trackingSystems;
// Inject entries for continuous calibration targets which have yet to load
if (CalCtx.state == CalibrationState::ContinuousStandby) {
auto existing = std::find(trackingSystems.begin(), trackingSystems.end(), CalCtx.referenceTrackingSystem);
if (existing == trackingSystems.end()) {
trackingSystems.push_back(CalCtx.referenceTrackingSystem);
}
existing = std::find(trackingSystems.begin(), trackingSystems.end(), CalCtx.targetTrackingSystem);
if (existing == trackingSystems.end()) {
trackingSystems.push_back(CalCtx.targetTrackingSystem);
}
}
return state;
}
void BuildProfileEditor()
{
ImGuiStyle &style = ImGui::GetStyle();
float width = ImGui::GetWindowContentRegionWidth() / 3.0f - style.FramePadding.x;
float widthF = width - style.FramePadding.x;
TextWithWidth("YawLabel", "Yaw", width);
ImGui::SameLine();
TextWithWidth("PitchLabel", "Pitch", width);
ImGui::SameLine();
TextWithWidth("RollLabel", "Roll", width);
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##Yaw", &CalCtx.calibratedRotation(1), 0.1, 1.0, "%.8f");
ImGui::SameLine();
ImGui::InputDouble("##Pitch", &CalCtx.calibratedRotation(2), 0.1, 1.0, "%.8f");
ImGui::SameLine();
ImGui::InputDouble("##Roll", &CalCtx.calibratedRotation(0), 0.1, 1.0, "%.8f");
TextWithWidth("XLabel", "X", width);
ImGui::SameLine();
TextWithWidth("YLabel", "Y", width);
ImGui::SameLine();
TextWithWidth("ZLabel", "Z", width);
ImGui::InputDouble("##X", &CalCtx.calibratedTranslation(0), 1.0, 10.0, "%.8f");
ImGui::SameLine();
ImGui::InputDouble("##Y", &CalCtx.calibratedTranslation(1), 1.0, 10.0, "%.8f");
ImGui::SameLine();
ImGui::InputDouble("##Z", &CalCtx.calibratedTranslation(2), 1.0, 10.0, "%.8f");
TextWithWidth("ScaleLabel", "Scale", width);
ImGui::InputDouble("##Scale", &CalCtx.calibratedScale, 0.0001, 0.01, "%.8f");
ImGui::PopItemWidth();
}
void TextWithWidth(const char *label, const char *text, float width)
{
ImGui::BeginChild(label, ImVec2(width, ImGui::GetTextLineHeightWithSpacing()));
ImGui::Text(text);
ImGui::EndChild();
}
-4
View File
@@ -1,4 +0,0 @@
#pragma once
void BuildMainWindow(bool runningInOverlay);
void RequestImmediateRedraw();
-72
View File
@@ -1,72 +0,0 @@
#include "stdafx.h"
#include "VRState.h"
VRState VRState::Load()
{
VRState state;
auto& trackingSystems = state.trackingSystems;
char buffer[vr::k_unMaxPropertyStringSize];
for (uint32_t id = 0; id < vr::k_unMaxTrackedDeviceCount; ++id)
{
vr::ETrackedPropertyError err = vr::TrackedProp_Success;
auto deviceClass = vr::VRSystem()->GetTrackedDeviceClass(id);
if (deviceClass == vr::TrackedDeviceClass_Invalid)
continue;
if (deviceClass != vr::TrackedDeviceClass_TrackingReference)
{
vr::VRSystem()->GetStringTrackedDeviceProperty(id, vr::Prop_TrackingSystemName_String, buffer, vr::k_unMaxPropertyStringSize, &err);
if (err == vr::TrackedProp_Success)
{
std::string system(buffer);
auto existing = std::find(trackingSystems.begin(), trackingSystems.end(), system);
if (existing != trackingSystems.end())
{
if (deviceClass == vr::TrackedDeviceClass_HMD)
{
trackingSystems.erase(existing);
trackingSystems.insert(trackingSystems.begin(), system);
}
}
else
{
trackingSystems.push_back(system);
}
VRDevice device;
device.id = id;
device.deviceClass = deviceClass;
device.trackingSystem = system;
vr::VRSystem()->GetStringTrackedDeviceProperty(id, vr::Prop_ModelNumber_String, buffer, vr::k_unMaxPropertyStringSize, &err);
device.model = std::string(buffer);
vr::VRSystem()->GetStringTrackedDeviceProperty(id, vr::Prop_SerialNumber_String, buffer, vr::k_unMaxPropertyStringSize, &err);
device.serial = std::string(buffer);
device.controllerRole = (vr::ETrackedControllerRole)vr::VRSystem()->GetInt32TrackedDeviceProperty(id, vr::Prop_ControllerRoleHint_Int32, &err);
state.devices.push_back(device);
}
else
{
printf("failed to get tracking system name for id %d\n", id);
}
}
}
return state;
}
int VRState::FindDevice(const std::string& trackingSystem, const std::string& model, const std::string& serial) const {
for (int i = 0; i < devices.size(); i++) {
const auto& device = devices[i];
if (device.trackingSystem == trackingSystem && device.model == model && device.serial == serial) return device.id;
}
return -1;
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <openvr.h>
struct VRDevice
{
int id = -1;
vr::TrackedDeviceClass deviceClass;
std::string model = "";
std::string serial = "";
std::string trackingSystem = "";
vr::ETrackedControllerRole controllerRole = vr::TrackedControllerRole_Invalid;
};
struct VRState
{
std::vector<std::string> trackingSystems;
std::vector<VRDevice> devices;
int FindDevice(const std::string& trackingSystem, const std::string& model, const std::string& serial) const;
static VRState Load();
};
-6
View File
@@ -1,6 +0,0 @@
// stdafx.cpp : source file that includes just the standard includes
// OpenVR-SpaceCalibrator.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#define EIGEN_MPL2_ONLY
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <iostream>
@@ -1,4 +0,0 @@
{
"driver_01spacecalibrator" : {
}
}
-27
View File
@@ -1,27 +0,0 @@
#include "Hooking.h"
std::map<std::string, IHook *> IHook::hooks;
bool IHook::Exists(const std::string &name)
{
return hooks.find(name) != hooks.end();
}
void IHook::Register(IHook *hook)
{
hooks[hook->name] = hook;
}
void IHook::Unregister(IHook *hook)
{
hooks.erase(hook->name);
}
void IHook::DestroyAll()
{
for (auto &hook : hooks)
{
hook.second->Destroy();
}
hooks.clear();
}
-75
View File
@@ -1,75 +0,0 @@
#pragma once
#include "Logging.h"
#include <MinHook.h>
#include <map>
#include <string>
class IHook
{
public:
const std::string name;
IHook(const std::string &name) : name(name) { }
virtual ~IHook() { }
virtual void Destroy() = 0;
static bool Exists(const std::string &name);
static void Register(IHook *hook);
static void Unregister(IHook *hook);
static void DestroyAll();
private:
static std::map<std::string, IHook *> hooks;
};
template<class FuncType> class Hook : public IHook
{
public:
FuncType originalFunc = nullptr;
Hook(const std::string &name) : IHook(name) { }
bool CreateHookInObjectVTable(void *object, int vtableOffset, void *detourFunction)
{
// For virtual objects, VC++ adds a pointer to the vtable as the first member.
// To access the vtable, we simply dereference the object.
void **vtable = *((void ***)object);
// The vtable itself is an array of pointers to member functions,
// in the order they were declared in.
targetFunc = vtable[vtableOffset];
auto err = MH_CreateHook(targetFunc, detourFunction, (LPVOID *)&originalFunc);
if (err != MH_OK)
{
LOG("Failed to create hook for %s, error: %s", name.c_str(), MH_StatusToString(err));
return false;
}
err = MH_EnableHook(targetFunc);
if (err != MH_OK)
{
LOG("Failed to enable hook for %s, error: %s", name.c_str(), MH_StatusToString(err));
MH_RemoveHook(targetFunc);
return false;
}
LOG("Enabled hook for %s", name.c_str());
enabled = true;
return true;
}
void Destroy()
{
if (enabled)
{
MH_RemoveHook(targetFunc);
enabled = false;
}
}
private:
bool enabled = false;
void* targetFunc = nullptr;
};
-230
View File
@@ -1,230 +0,0 @@
#include "IPCServer.h"
#include "Logging.h"
#include "ServerTrackedDeviceProvider.h"
void IPCServer::HandleRequest(const protocol::Request &request, protocol::Response &response)
{
switch (request.type)
{
case protocol::RequestHandshake:
response.type = protocol::ResponseHandshake;
response.protocol.version = protocol::Version;
break;
case protocol::RequestSetDeviceTransform:
driver->SetDeviceTransform(request.setDeviceTransform);
response.type = protocol::ResponseSuccess;
break;
case protocol::RequestDebugOffset:
driver->HandleApplyRandomOffset();
response.type = protocol::ResponseSuccess;
break;
case protocol::RequestSetAlignmentSpeedParams:
driver->HandleSetAlignmentSpeedParams(request.setAlignmentSpeedParams);
response.type = protocol::ResponseSuccess;
break;
default:
LOG("Invalid IPC request: %d", request.type);
break;
}
}
IPCServer::~IPCServer()
{
Stop();
}
void IPCServer::Run()
{
mainThread = std::thread(RunThread, this);
}
void IPCServer::Stop()
{
TRACE("IPCServer::Stop()");
if (!running)
return;
stop = true;
SetEvent(connectEvent);
mainThread.join();
running = false;
TRACE("IPCServer::Stop() finished");
}
IPCServer::PipeInstance *IPCServer::CreatePipeInstance(HANDLE pipe)
{
auto pipeInst = new PipeInstance;
pipeInst->pipe = pipe;
pipeInst->server = this;
pipes.insert(pipeInst);
return pipeInst;
}
void IPCServer::ClosePipeInstance(PipeInstance *pipeInst)
{
DisconnectNamedPipe(pipeInst->pipe);
CloseHandle(pipeInst->pipe);
pipes.erase(pipeInst);
delete pipeInst;
}
void IPCServer::RunThread(IPCServer *_this)
{
_this->running = true;
LPTSTR pipeName = TEXT(OPENVR_SPACECALIBRATOR_PIPE_NAME);
HANDLE connectEvent = _this->connectEvent = CreateEvent(0, TRUE, TRUE, 0);
if (!connectEvent)
{
LOG("CreateEvent failed in RunThread. Error: %d", GetLastError());
return;
}
OVERLAPPED connectOverlap;
connectOverlap.hEvent = connectEvent;
HANDLE nextPipe;
BOOL connectPending = CreateAndConnectInstance(&connectOverlap, nextPipe);
while (!_this->stop)
{
DWORD wait = WaitForSingleObjectEx(connectEvent, INFINITE, TRUE);
if (_this->stop)
{
break;
}
else if (wait == 0)
{
// When connectPending is false, the last call to CreateAndConnectInstance
// picked up a connected client and triggered this event, so we can simply
// create a new pipe instance for it. If true, the client was still pending
// connection when CreateAndConnectInstance returned, so this event was triggered
// internally and we need to flush out the result, or something like that.
if (connectPending)
{
DWORD bytesConnect;
BOOL success = GetOverlappedResult(nextPipe, &connectOverlap, &bytesConnect, FALSE);
if (!success)
{
LOG("GetOverlappedResult failed in RunThread. Error: %d", GetLastError());
return;
}
}
LOG("IPC client connected");
auto pipeInst = _this->CreatePipeInstance(nextPipe);
CompletedWriteCallback(0, sizeof protocol::Response, (LPOVERLAPPED) pipeInst);
connectPending = CreateAndConnectInstance(&connectOverlap, nextPipe);
}
else if (wait != WAIT_IO_COMPLETION)
{
printf("WaitForSingleObjectEx failed in RunThread. Error %d", GetLastError());
return;
}
}
for (auto &pipeInst : _this->pipes)
{
_this->ClosePipeInstance(pipeInst);
}
_this->pipes.clear();
}
BOOL IPCServer::CreateAndConnectInstance(LPOVERLAPPED overlap, HANDLE &pipe)
{
pipe = CreateNamedPipe(
TEXT(OPENVR_SPACECALIBRATOR_PIPE_NAME),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
sizeof protocol::Request,
sizeof protocol::Response,
1000,
0
);
if (pipe == INVALID_HANDLE_VALUE)
{
LOG("CreateNamedPipe failed. Error: %d", GetLastError());
return FALSE;
}
ConnectNamedPipe(pipe, overlap);
switch(GetLastError())
{
case ERROR_IO_PENDING:
// Mark a pending connection by returning true, and when the connection
// completes an event will trigger automatically.
return TRUE;
case ERROR_PIPE_CONNECTED:
// Signal the event loop that a client is connected.
if (SetEvent(overlap->hEvent))
return FALSE;
}
LOG("ConnectNamedPipe failed. Error: %d", GetLastError());
return FALSE;
}
void IPCServer::CompletedReadCallback(DWORD err, DWORD bytesRead, LPOVERLAPPED overlap)
{
PipeInstance *pipeInst = (PipeInstance *) overlap;
BOOL success = FALSE;
if (err == 0 && bytesRead > 0)
{
pipeInst->server->HandleRequest(pipeInst->request, pipeInst->response);
success = WriteFileEx(
pipeInst->pipe,
&pipeInst->response,
sizeof protocol::Response,
overlap,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteCallback
);
}
if (!success)
{
if (err == ERROR_BROKEN_PIPE)
{
LOG("IPC client disconnecting normally");
}
else
{
LOG("IPC client disconnecting due to error (via CompletedReadCallback), error: %d, bytesRead: %d", err, bytesRead);
}
pipeInst->server->ClosePipeInstance(pipeInst);
}
}
void IPCServer::CompletedWriteCallback(DWORD err, DWORD bytesWritten, LPOVERLAPPED overlap)
{
PipeInstance *pipeInst = (PipeInstance *) overlap;
BOOL success = FALSE;
if (err == 0 && bytesWritten == sizeof protocol::Response)
{
success = ReadFileEx(
pipeInst->pipe,
&pipeInst->request,
sizeof protocol::Request,
overlap,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadCallback
);
}
if (!success)
{
LOG("IPC client disconnecting due to error (via CompletedWriteCallback), error: %d, bytesWritten: %d", err, bytesWritten);
pipeInst->server->ClosePipeInstance(pipeInst);
}
}
-53
View File
@@ -1,53 +0,0 @@
#pragma once
#include "../Protocol.h"
#include <thread>
#include <set>
#include <mutex>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class ServerTrackedDeviceProvider;
class IPCServer
{
public:
IPCServer(ServerTrackedDeviceProvider *driver) : driver(driver) { }
~IPCServer();
void Run();
void Stop();
private:
void HandleRequest(const protocol::Request &request, protocol::Response &response);
struct PipeInstance
{
OVERLAPPED overlap; // Used by the API
HANDLE pipe;
IPCServer *server;
protocol::Request request;
protocol::Response response;
};
PipeInstance *CreatePipeInstance(HANDLE pipe);
void ClosePipeInstance(PipeInstance *pipeInst);
static void RunThread(IPCServer *_this);
static BOOL CreateAndConnectInstance(LPOVERLAPPED overlap, HANDLE &pipe);
static void WINAPI CompletedReadCallback(DWORD err, DWORD bytesRead, LPOVERLAPPED overlap);
static void WINAPI CompletedWriteCallback(DWORD err, DWORD bytesWritten, LPOVERLAPPED overlap);
std::thread mainThread;
bool running = false;
bool stop = false;
std::set<PipeInstance *> pipes;
HANDLE connectEvent;
ServerTrackedDeviceProvider *driver;
};
@@ -1,83 +0,0 @@
#include "Logging.h"
#include "Hooking.h"
#include "InterfaceHookInjector.h"
#include "ServerTrackedDeviceProvider.h"
static ServerTrackedDeviceProvider *Driver = nullptr;
static Hook<void*(*)(vr::IVRDriverContext *, const char *, vr::EVRInitError *)>
GetGenericInterfaceHook("IVRDriverContext::GetGenericInterface");
static Hook<void(*)(vr::IVRServerDriverHost *, uint32_t, const vr::DriverPose_t &, uint32_t)>
TrackedDevicePoseUpdatedHook005("IVRServerDriverHost005::TrackedDevicePoseUpdated");
static Hook<void(*)(vr::IVRServerDriverHost *, uint32_t, const vr::DriverPose_t &, uint32_t)>
TrackedDevicePoseUpdatedHook006("IVRServerDriverHost006::TrackedDevicePoseUpdated");
static void DetourTrackedDevicePoseUpdated005(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize)
{
//TRACE("ServerTrackedDeviceProvider::DetourTrackedDevicePoseUpdated(%d)", unWhichDevice);
auto pose = newPose;
if (Driver->HandleDevicePoseUpdated(unWhichDevice, pose))
{
TrackedDevicePoseUpdatedHook005.originalFunc(_this, unWhichDevice, pose, unPoseStructSize);
}
}
static void DetourTrackedDevicePoseUpdated006(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize)
{
//TRACE("ServerTrackedDeviceProvider::DetourTrackedDevicePoseUpdated(%d)", unWhichDevice);
auto pose = newPose;
if (Driver->HandleDevicePoseUpdated(unWhichDevice, pose))
{
TrackedDevicePoseUpdatedHook006.originalFunc(_this, unWhichDevice, pose, unPoseStructSize);
}
}
static void *DetourGetGenericInterface(vr::IVRDriverContext *_this, const char *pchInterfaceVersion, vr::EVRInitError *peError)
{
TRACE("ServerTrackedDeviceProvider::DetourGetGenericInterface(%s)", pchInterfaceVersion);
auto originalInterface = GetGenericInterfaceHook.originalFunc(_this, pchInterfaceVersion, peError);
std::string iface(pchInterfaceVersion);
if (iface == "IVRServerDriverHost_005")
{
if (!IHook::Exists(TrackedDevicePoseUpdatedHook005.name))
{
TrackedDevicePoseUpdatedHook005.CreateHookInObjectVTable(originalInterface, 1, &DetourTrackedDevicePoseUpdated005);
IHook::Register(&TrackedDevicePoseUpdatedHook005);
}
}
else if (iface == "IVRServerDriverHost_006")
{
if (!IHook::Exists(TrackedDevicePoseUpdatedHook006.name))
{
TrackedDevicePoseUpdatedHook006.CreateHookInObjectVTable(originalInterface, 1, &DetourTrackedDevicePoseUpdated006);
IHook::Register(&TrackedDevicePoseUpdatedHook006);
}
}
return originalInterface;
}
void InjectHooks(ServerTrackedDeviceProvider *driver, vr::IVRDriverContext *pDriverContext)
{
Driver = driver;
auto err = MH_Initialize();
if (err == MH_OK)
{
GetGenericInterfaceHook.CreateHookInObjectVTable(pDriverContext, 0, &DetourGetGenericInterface);
IHook::Register(&GetGenericInterfaceHook);
}
else
{
LOG("MH_Initialize error: %s", MH_StatusToString(err));
}
}
void DisableHooks()
{
IHook::DestroyAll();
MH_Uninitialize();
}
@@ -1,10 +0,0 @@
#pragma once
#include <openvr_driver.h>
class ServerTrackedDeviceProvider;
static void DetourTrackedDevicePoseUpdated(vr::IVRServerDriverHost * _this, uint32_t unWhichDevice, const vr::DriverPose_t & newPose, uint32_t unPoseStructSize);
void InjectHooks(ServerTrackedDeviceProvider *driver, vr::IVRDriverContext *pDriverContext);
void DisableHooks();
@@ -1,50 +0,0 @@
#pragma once
#define EIGEN_MPL2_ONLY
#include <Eigen/Dense>
/**
* Contains an isometric transformation, represented as the pair of a rotation quaternion and translation vector.
* The translation is applied to the left of the quaternion.
*/
struct IsoTransform {
Eigen::Quaterniond rotation;
Eigen::Vector3d translation;
IsoTransform() : rotation(Eigen::Quaterniond::Identity()), translation(Eigen::Vector3d::Zero()) {}
IsoTransform(const Eigen::Quaterniond &rot) : rotation(rot), translation(Eigen::Vector3d::Zero()) {}
IsoTransform(const Eigen::Vector3d &trans) : rotation(Eigen::Quaterniond::Identity()) {}
IsoTransform(const Eigen::Quaterniond& rot, const Eigen::Vector3d& trans) : rotation(rot), translation(trans) {}
void pretranslate(const Eigen::Vector3d& t) {
translation += t;
}
/**
* Interpolates between this transform and target. The position of localPoint after transformation will smoothly
* lerp between (this * localPoint) and (target * localPoint), despite rotation occurring around it.
*/
IsoTransform interpolateAround(double lerp, const IsoTransform& target, const Eigen::Vector3d& localPoint) const;
};
inline IsoTransform operator*(const IsoTransform& a, const IsoTransform& b) {
// tA * rA * tB * rB = tA * (trans(rA * tB)) * rA * rB
auto rot = a.rotation * b.rotation;
Eigen::Vector3d trans = a.translation + Eigen::Isometry3d(a.rotation) * b.translation;
return IsoTransform(rot, trans);
}
inline Eigen::Vector3d operator*(const IsoTransform& a, const Eigen::Vector3d& p) {
return a.translation + Eigen::Isometry3d(a.rotation) * p;
}
inline IsoTransform IsoTransform::interpolateAround(double lerp, const IsoTransform& target, const Eigen::Vector3d& localPoint) const {
auto initialPos = (*this) * localPoint;
Eigen::Vector3d finalPos = initialPos * (1 - lerp) + (target * localPoint) * lerp;
auto newRotation = rotation.slerp(lerp, target.rotation);
Eigen::Vector3d newTranslation = finalPos - Eigen::Isometry3d(newRotation) * localPoint;
return IsoTransform(newRotation, newTranslation);
}
-28
View File
@@ -1,28 +0,0 @@
#define _CRT_SECURE_NO_DEPRECATE
#include "Logging.h"
#include <chrono>
FILE *LogFile;
void OpenLogFile()
{
LogFile = fopen("space_calibrator_driver.log", "a");
if (LogFile == nullptr)
{
LogFile = stderr;
}
}
tm TimeForLog()
{
auto now = std::chrono::system_clock::now();
auto nowTime = std::chrono::system_clock::to_time_t(now);
tm value;
auto tm = localtime_s(&value, &nowTime);
return value;
}
void LogFlush()
{
fflush(LogFile);
}
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#include <cstdio>
#include <ctime>
extern FILE *LogFile;
void OpenLogFile();
tm TimeForLog();
void LogFlush();
#ifndef LOG
#define LOG(fmt, ...) do { \
tm logNow = TimeForLog(); \
fprintf(LogFile, "[%02d:%02d:%02d] " fmt "\n", logNow.tm_hour, logNow.tm_min, logNow.tm_sec, __VA_ARGS__); \
LogFlush(); \
} while (0)
#endif
#define TRACE(...) {}
#ifndef TRACE
#define TRACE LOG
#endif
@@ -1,31 +0,0 @@
#include "OpenVR-SpaceCalibratorDriver.h"
#include "ServerTrackedDeviceProvider.h"
#include "VRWatchdogProvider.h"
#include "Logging.h"
#include <cstdio>
#include <cstring>
#include <openvr_driver.h>
OPENVRSPACECALIBRATORDRIVER_API void *HmdDriverFactory(const char *pInterfaceName, int *pReturnCode)
{
TRACE("HmdDriverFactory(%s)", pInterfaceName);
static ServerTrackedDeviceProvider server;
static VRWatchdogProvider watchdog;
if (std::strcmp(vr::IServerTrackedDeviceProvider_Version, pInterfaceName) == 0)
{
return &server;
}
else if (std::strcmp(vr::IVRWatchdogProvider_Version, pInterfaceName) == 0)
{
return &watchdog;
}
if (pReturnCode)
{
*pReturnCode = vr::VRInitError_Init_InterfaceNotFound;
}
return nullptr;
}
@@ -1,7 +0,0 @@
#pragma once
#ifdef OPENVRSPACECALIBRATORDRIVER_EXPORTS
#define OPENVRSPACECALIBRATORDRIVER_API extern "C" __declspec(dllexport)
#else
#define OPENVRSPACECALIBRATORDRIVER_API extern "C" __declspec(dllimport)
#endif
@@ -1,126 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A61324AD-CE32-46D1-A95E-7E28A6D8CCA7}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>OpenVRSpaceCalibratorDriver</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetName>driver_01spacecalibrator</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetName>driver_01spacecalibrator</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;OPENVRSPACECALIBRATORDRIVER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\lib;..\lib\openvr;..\lib\MinHook\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\lib\MinHook\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;OPENVRSPACECALIBRATORDRIVER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\lib;..\lib\openvr;..\lib\MinHook\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\lib\MinHook\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\Protocol.h" />
<ClInclude Include="Hooking.h" />
<ClInclude Include="InterfaceHookInjector.h" />
<ClInclude Include="IPCServer.h" />
<ClInclude Include="IsometryTransform.h" />
<ClInclude Include="Logging.h" />
<ClInclude Include="OpenVR-SpaceCalibratorDriver.h" />
<ClInclude Include="ServerTrackedDeviceProvider.h" />
<ClInclude Include="VRWatchdogProvider.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Hooking.cpp" />
<ClCompile Include="InterfaceHookInjector.cpp" />
<ClCompile Include="IPCServer.cpp" />
<ClCompile Include="Logging.cpp" />
<ClCompile Include="OpenVR-SpaceCalibratorDriver.cpp" />
<ClCompile Include="ServerTrackedDeviceProvider.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\lib\minhook\build\VC16\libMinHook.vcxproj">
<Project>{f142a341-5ee0-442d-a15f-98ae9b48dbae}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="OpenVR-SpaceCalibratorDriver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Logging.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ServerTrackedDeviceProvider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VRWatchdogProvider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IPCServer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Protocol.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Hooking.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="InterfaceHookInjector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IsometryTransform.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="OpenVR-SpaceCalibratorDriver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Logging.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ServerTrackedDeviceProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IPCServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Hooking.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="InterfaceHookInjector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
@@ -1,256 +0,0 @@
#include "ServerTrackedDeviceProvider.h"
#include "Logging.h"
#include "InterfaceHookInjector.h"
#include "IsometryTransform.h"
#include <random>
vr::EVRInitError ServerTrackedDeviceProvider::Init(vr::IVRDriverContext *pDriverContext)
{
TRACE("ServerTrackedDeviceProvider::Init()");
VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext);
memset(transforms, 0, vr::k_unMaxTrackedDeviceCount * sizeof DeviceTransform);
memset(&alignmentSpeedParams, 0, sizeof alignmentSpeedParams);
alignmentSpeedParams.thr_rot_tiny = 0.1f * (EIGEN_PI / 180.0f);
alignmentSpeedParams.thr_rot_small = 1.0f * (EIGEN_PI / 180.0f);
alignmentSpeedParams.thr_rot_large = 5.0f * (EIGEN_PI / 180.0f);
alignmentSpeedParams.thr_trans_tiny = 0.1f / 1000.0; // mm
alignmentSpeedParams.thr_trans_small = 1.0f / 1000.0; // mm
alignmentSpeedParams.thr_trans_large = 20.0f / 1000.0; // mm
alignmentSpeedParams.align_speed_tiny = 0.05f;
alignmentSpeedParams.align_speed_small = 0.2f;
alignmentSpeedParams.align_speed_large = 2.0f;
InjectHooks(this, pDriverContext);
server.Run();
shmem.Create(OPENVR_SPACECALIBRATOR_SHMEM_NAME);
debugTransform = Eigen::Vector3d::Zero();
debugRotation = Eigen::Quaterniond::Identity();
return vr::VRInitError_None;
}
void ServerTrackedDeviceProvider::Cleanup()
{
TRACE("ServerTrackedDeviceProvider::Cleanup()");
server.Stop();
shmem.Close();
DisableHooks();
VR_CLEANUP_SERVER_DRIVER_CONTEXT();
}
namespace {
vr::HmdQuaternion_t convert(const Eigen::Quaterniond& q) {
vr::HmdQuaternion_t result;
result.w = q.w();
result.x = q.x();
result.y = q.y();
result.z = q.z();
return result;
}
vr::HmdVector3_t convert(const Eigen::Vector3d& v) {
vr::HmdVector3_t result;
result.v[0] = (float) v.x();
result.v[1] = (float) v.y();
result.v[2] = (float) v.z();
return result;
}
Eigen::Quaterniond convert(const vr::HmdQuaternion_t& q) {
return Eigen::Quaterniond(q.w, q.x, q.y, q.z);
}
Eigen::Vector3d convert(const vr::HmdVector3d_t& v) {
return Eigen::Vector3d(v.v[0], v.v[1], v.v[2]);
}
Eigen::Vector3d convert(const double* arr) {
return Eigen::Vector3d(arr[0], arr[1], arr[2]);
}
IsoTransform toIsoWorldTransform(const vr::DriverPose_t& pose) {
Eigen::Quaterniond rot(pose.qWorldFromDriverRotation.w, pose.qWorldFromDriverRotation.x, pose.qWorldFromDriverRotation.y, pose.qWorldFromDriverRotation.z);
Eigen::Vector3d trans(pose.vecWorldFromDriverTranslation[0], pose.vecWorldFromDriverTranslation[1], pose.vecWorldFromDriverTranslation[2]);
return IsoTransform(rot, trans);
}
IsoTransform toIsoPose(const vr::DriverPose_t& pose) {
auto worldXform = toIsoWorldTransform(pose);
Eigen::Quaterniond rot(pose.qRotation.w, pose.qRotation.x, pose.qRotation.y, pose.qRotation.z);
Eigen::Vector3d trans(pose.vecPosition[0], pose.vecPosition[1], pose.vecPosition[2]);
return worldXform * IsoTransform(rot, trans);
}
}
/**
* This function heuristically evaluates the amount of drift between the src and target playspace transforms,
* evaluated centered on the `pose` device transform. This is then used to control the speed of realignment.
*/
ServerTrackedDeviceProvider::DeltaSize ServerTrackedDeviceProvider::GetTransformDeltaSize(
DeltaSize prior_delta,
const IsoTransform& deviceWorldPose,
const IsoTransform& src,
const IsoTransform& target
) const {
const auto src_pose = src * deviceWorldPose;
const auto target_pose = target * deviceWorldPose;
const auto trans_delta = (src_pose.translation - target_pose.translation).squaredNorm();
const auto rot_delta = src_pose.rotation.angularDistance(target_pose.rotation);
DeltaSize trans_level, rot_level;
if (trans_delta > alignmentSpeedParams.thr_trans_large) trans_level = DeltaSize::LARGE;
else if (trans_delta > alignmentSpeedParams.thr_trans_small) trans_level = DeltaSize::SMALL;
else trans_level = DeltaSize::TINY;
if (rot_delta > alignmentSpeedParams.thr_rot_large) rot_level = DeltaSize::LARGE;
else if (rot_delta > alignmentSpeedParams.thr_rot_small) rot_level = DeltaSize::SMALL;
else rot_level = DeltaSize::TINY;
if (trans_level == DeltaSize::TINY && rot_level == DeltaSize::TINY) return DeltaSize::TINY;
else return max(prior_delta, max(trans_level, rot_level));
}
double ServerTrackedDeviceProvider::GetTransformRate(DeltaSize delta) const {
switch (delta) {
case DeltaSize::TINY: return alignmentSpeedParams.align_speed_tiny;
case DeltaSize::SMALL: return alignmentSpeedParams.align_speed_small;
default: return alignmentSpeedParams.align_speed_large;
}
}
/**
* Smoothly interpolates the device active transform towards the target transform.
*/
void ServerTrackedDeviceProvider::BlendTransform(DeviceTransform& device, const IsoTransform &deviceWorldPose) const {
LARGE_INTEGER timestamp, freq;
QueryPerformanceCounter(&timestamp);
QueryPerformanceFrequency(&freq);
double lerp = (timestamp.QuadPart - device.lastPoll.QuadPart) / (double)freq.QuadPart;
device.lastPoll = timestamp;
lerp *= GetTransformRate(device.currentRate);
if (lerp > 1.0)
lerp = 1.0;
if (lerp < 0 || isnan(lerp))
lerp = 0;
device.transform = device.transform.interpolateAround(lerp, device.targetTransform, deviceWorldPose.translation);
}
void ServerTrackedDeviceProvider::ApplyTransform(DeviceTransform& device, vr::DriverPose_t& devicePose) const {
auto deviceWorldTransform = toIsoWorldTransform(devicePose);
deviceWorldTransform = device.transform * deviceWorldTransform;
devicePose.vecWorldFromDriverTranslation[0] = deviceWorldTransform.translation(0);
devicePose.vecWorldFromDriverTranslation[1] = deviceWorldTransform.translation(1);
devicePose.vecWorldFromDriverTranslation[2] = deviceWorldTransform.translation(2);
devicePose.qWorldFromDriverRotation = convert(deviceWorldTransform.rotation);
}
inline vr::HmdQuaternion_t operator*(const vr::HmdQuaternion_t &lhs, const vr::HmdQuaternion_t &rhs) {
return {
(lhs.w * rhs.w) - (lhs.x * rhs.x) - (lhs.y * rhs.y) - (lhs.z * rhs.z),
(lhs.w * rhs.x) + (lhs.x * rhs.w) + (lhs.y * rhs.z) - (lhs.z * rhs.y),
(lhs.w * rhs.y) + (lhs.y * rhs.w) + (lhs.z * rhs.x) - (lhs.x * rhs.z),
(lhs.w * rhs.z) + (lhs.z * rhs.w) + (lhs.x * rhs.y) - (lhs.y * rhs.x)
};
}
inline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const double(&vector)[3]) {
vr::HmdQuaternion_t vectorQuat = { 0.0, vector[0], vector[1] , vector[2] };
vr::HmdQuaternion_t conjugate = { quat.w, -quat.x, -quat.y, -quat.z };
auto rotatedVectorQuat = quat * vectorQuat * conjugate;
return { rotatedVectorQuat.x, rotatedVectorQuat.y, rotatedVectorQuat.z };
}
void ServerTrackedDeviceProvider::SetDeviceTransform(const protocol::SetDeviceTransform& newTransform)
{
auto &tf = transforms[newTransform.openVRID];
tf.enabled = newTransform.enabled;
if (newTransform.updateTranslation) {
tf.targetTransform.translation = convert(newTransform.translation);
if (!newTransform.lerp) {
tf.transform.translation = tf.targetTransform.translation;
}
}
if (newTransform.updateRotation) {
tf.targetTransform.rotation = convert(newTransform.rotation);
if (!newTransform.lerp) {
tf.transform.rotation = tf.targetTransform.rotation;
}
}
if (newTransform.updateScale)
tf.scale = newTransform.scale;
tf.quash = newTransform.quash;
}
bool ServerTrackedDeviceProvider::HandleDevicePoseUpdated(uint32_t openVRID, vr::DriverPose_t &pose)
{
// Apply debug pose before anything else
if (openVRID > 0) {
auto dbgPos = convert(pose.vecPosition) + debugTransform;
auto dbgRot = convert(pose.qRotation) * debugRotation;
pose.qRotation = convert(dbgRot);
pose.vecPosition[0] = dbgPos(0);
pose.vecPosition[1] = dbgPos(1);
pose.vecPosition[2] = dbgPos(2);
}
shmem.SetPose(openVRID, pose);
auto& tf = transforms[openVRID];
if (tf.quash) {
pose.vecPosition[0] = -pose.vecWorldFromDriverTranslation[0];
pose.vecPosition[1] = -pose.vecWorldFromDriverTranslation[1] + 9001; // put it 9001m above the origin
pose.vecPosition[2] = -pose.vecWorldFromDriverTranslation[2];
} else if (tf.enabled)
{
pose.vecPosition[0] *= tf.scale;
pose.vecPosition[1] *= tf.scale;
pose.vecPosition[2] *= tf.scale;
auto deviceWorldPose = toIsoPose(pose);
tf.currentRate = GetTransformDeltaSize(tf.currentRate, deviceWorldPose, tf.transform, tf.targetTransform);
double lerp = GetTransformRate(tf.currentRate);
BlendTransform(tf, deviceWorldPose);
ApplyTransform(tf, pose);
}
return true;
}
void ServerTrackedDeviceProvider::HandleApplyRandomOffset() {
std::random_device gen;
std::uniform_real_distribution<double> d(-1, 1);
auto init = Eigen::Vector3d(d(gen), d(gen), d(gen));
auto posOffset = init * 0.25f;
debugTransform = posOffset;
debugRotation = Eigen::Quaterniond::Identity();
std::ostringstream oss;
oss << "Applied random offset: " << posOffset << " from init " << init << std::endl;
LOG("%s", oss.str().c_str());
}
@@ -1,91 +0,0 @@
#pragma once
#define EIGEN_MPL2_ONLY
#include "IPCServer.h"
#include "../Protocol.h"
#include "IsometryTransform.h"
#include <Eigen/Dense>
#include <openvr_driver.h>
class ServerTrackedDeviceProvider : public vr::IServerTrackedDeviceProvider
{
public:
////// Start vr::IServerTrackedDeviceProvider functions
/** initializes the driver. This will be called before any other methods are called. */
virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext) override;
/** cleans up the driver right before it is unloaded */
virtual void Cleanup() override;
/** Returns the version of the ITrackedDeviceServerDriver interface used by this driver */
virtual const char * const *GetInterfaceVersions() { return vr::k_InterfaceVersions; }
/** Allows the driver do to some work in the main loop of the server. */
virtual void RunFrame() { }
/** Returns true if the driver wants to block Standby mode. */
virtual bool ShouldBlockStandbyMode() { return false; }
/** Called when the system is entering Standby mode. The driver should switch itself into whatever sort of low-power
* state it has. */
virtual void EnterStandby() { }
/** Called when the system is leaving Standby mode. The driver should switch itself back to
full operation. */
virtual void LeaveStandby() { }
////// End vr::IServerTrackedDeviceProvider functions
ServerTrackedDeviceProvider() : server(this) { }
void SetDeviceTransform(const protocol::SetDeviceTransform &newTransform);
bool HandleDevicePoseUpdated(uint32_t openVRID, vr::DriverPose_t &pose);
void HandleApplyRandomOffset();
void HandleSetAlignmentSpeedParams(const protocol::AlignmentSpeedParams params) {
alignmentSpeedParams = params;
}
private:
IPCServer server;
protocol::DriverPoseShmem shmem;
enum DeltaSize {
TINY,
SMALL,
LARGE
};
struct DeviceTransform
{
bool enabled = false;
bool quash = false;
IsoTransform transform, targetTransform;
double scale;
LARGE_INTEGER lastPoll;
DeltaSize currentRate = DeltaSize::TINY;
};
DeviceTransform transforms[vr::k_unMaxTrackedDeviceCount];
Eigen::Vector3d debugTransform;
Eigen::Quaterniond debugRotation;
DeltaSize currentDeltaSpeed[vr::k_unMaxTrackedDeviceCount];
protocol::AlignmentSpeedParams alignmentSpeedParams;
DeltaSize GetTransformDeltaSize(
DeltaSize prior_delta,
const IsoTransform& deviceWorldPose,
const IsoTransform& src,
const IsoTransform& target
) const;
double GetTransformRate(DeltaSize delta) const;
void BlendTransform(DeviceTransform& device, const IsoTransform& deviceWorldPose) const;
void ApplyTransform(DeviceTransform& device, vr::DriverPose_t& devicePose) const;
};
@@ -1,19 +0,0 @@
#pragma once
#include <openvr_driver.h>
class VRWatchdogProvider : public vr::IVRWatchdogProvider
{
/** initializes the driver in watchdog mode. */
virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext)
{
VR_INIT_WATCHDOG_DRIVER_CONTEXT(pDriverContext);
return vr::VRInitError_None;
}
/** cleans up the driver right before it is unloaded */
virtual void Cleanup()
{
VR_CLEANUP_WATCHDOG_DRIVER_CONTEXT()
}
};
-25
View File
@@ -1,25 +0,0 @@
#include "Logging.h"
#include "../Version.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <cstdio>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
OpenLogFile();
LOG("OpenVR-SpaceCalibratorDriver " SPACECAL_VERSION_STRING " loaded");
break;
case DLL_PROCESS_DETACH:
LOG("OpenVR-SpaceCalibratorDriver unloaded");
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
-362
View File
@@ -1,362 +0,0 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include <atomic>
#include <stdexcept>
#include <functional>
#ifndef _OPENVR_API
#include <openvr_driver.h>
#endif
#define OPENVR_SPACECALIBRATOR_PIPE_NAME "\\\\.\\pipe\\OpenVRSpaceCalibratorDriver"
#define OPENVR_SPACECALIBRATOR_SHMEM_NAME "OpenVRSpaceCalibratorPoseMemoryV1"
#ifdef _OPENVR_API
namespace vr {
// We can't include openvr_driver.h as it will result in multiple definition of some structures.
// However, we need to share driver-specific structures with the client application, so duplicate them
// here.
struct DriverPose_t
{
/* Time offset of this pose, in seconds from the actual time of the pose,
* relative to the time of the PoseUpdated() call made by the driver.
*/
double poseTimeOffset;
/* Generally, the pose maintained by a driver
* is in an inertial coordinate system different
* from the world system of x+ right, y+ up, z+ back.
* Also, the driver is not usually tracking the "head" position,
* but instead an internal IMU or another reference point in the HMD.
* The following two transforms transform positions and orientations
* to app world space from driver world space,
* and to HMD head space from driver local body space.
*
* We maintain the driver pose state in its internal coordinate system,
* so we can do the pose prediction math without having to
* use angular acceleration. A driver's angular acceleration is generally not measured,
* and is instead calculated from successive samples of angular velocity.
* This leads to a noisy angular acceleration values, which are also
* lagged due to the filtering required to reduce noise to an acceptable level.
*/
vr::HmdQuaternion_t qWorldFromDriverRotation;
double vecWorldFromDriverTranslation[3];
vr::HmdQuaternion_t qDriverFromHeadRotation;
double vecDriverFromHeadTranslation[3];
/* State of driver pose, in meters and radians. */
/* Position of the driver tracking reference in driver world space
* +[0] (x) is right
* +[1] (y) is up
* -[2] (z) is forward
*/
double vecPosition[3];
/* Velocity of the pose in meters/second */
double vecVelocity[3];
/* Acceleration of the pose in meters/second */
double vecAcceleration[3];
/* Orientation of the tracker, represented as a quaternion */
vr::HmdQuaternion_t qRotation;
/* Angular velocity of the pose in axis-angle
* representation. The direction is the angle of
* rotation and the magnitude is the angle around
* that axis in radians/second. */
double vecAngularVelocity[3];
/* Angular acceleration of the pose in axis-angle
* representation. The direction is the angle of
* rotation and the magnitude is the angle around
* that axis in radians/second^2. */
double vecAngularAcceleration[3];
ETrackingResult result;
bool poseIsValid;
bool willDriftInYaw;
bool shouldApplyHeadModel;
bool deviceIsConnected;
};
}
#endif
namespace protocol
{
const uint32_t Version = 4;
enum RequestType
{
RequestInvalid,
RequestHandshake,
RequestSetDeviceTransform,
RequestSetAlignmentSpeedParams,
RequestDebugOffset
};
enum ResponseType
{
ResponseInvalid,
ResponseHandshake,
ResponseSuccess,
};
struct Protocol
{
uint32_t version = Version;
};
struct AlignmentSpeedParams
{
/**
* The threshold at which we adjust the alignment speed based on the position offset
* between current and target calibrations. Generally, we increase the speed if we go
* above small/large, and decrease it only once it's under tiny.
*
* These values are expressed as distance squared
*/
double thr_trans_tiny, thr_trans_small, thr_trans_large;
/**
* Similar thresholds for rotation offsets, in radians
*/
double thr_rot_tiny, thr_rot_small, thr_rot_large;
/**
* The speed of alignment, expressed as a lerp/slerp factor. 1 will blend most of the way in <1 second.
* (We actually do a lerp(s * delta_t) where s is the speed factor here)
*/
double align_speed_tiny, align_speed_small, align_speed_large;
};
struct SetDeviceTransform
{
uint32_t openVRID;
bool enabled;
bool updateTranslation;
bool updateRotation;
bool updateScale;
vr::HmdVector3d_t translation;
vr::HmdQuaternion_t rotation;
double scale;
bool lerp;
bool quash;
SetDeviceTransform(uint32_t id, bool enabled) :
openVRID(id), enabled(enabled), updateTranslation(false), updateRotation(false), updateScale(false), lerp(false), quash(false) { }
SetDeviceTransform(uint32_t id, bool enabled, vr::HmdVector3d_t translation) :
openVRID(id), enabled(enabled), updateTranslation(true), updateRotation(false), updateScale(false), translation(translation), lerp(false), quash(false) { }
SetDeviceTransform(uint32_t id, bool enabled, vr::HmdQuaternion_t rotation) :
openVRID(id), enabled(enabled), updateTranslation(false), updateRotation(true), updateScale(false), rotation(rotation), lerp(false), quash(false) { }
SetDeviceTransform(uint32_t id, bool enabled, double scale) :
openVRID(id), enabled(enabled), updateTranslation(false), updateRotation(false), updateScale(true), scale(scale), lerp(false), quash(false) { }
SetDeviceTransform(uint32_t id, bool enabled, vr::HmdVector3d_t translation, vr::HmdQuaternion_t rotation) :
openVRID(id), enabled(enabled), updateTranslation(true), updateRotation(true), updateScale(false), translation(translation), rotation(rotation), lerp(false), quash(false) { }
SetDeviceTransform(uint32_t id, bool enabled, vr::HmdVector3d_t translation, vr::HmdQuaternion_t rotation, double scale) :
openVRID(id), enabled(enabled), updateTranslation(true), updateRotation(true), updateScale(true), translation(translation), rotation(rotation), scale(scale), lerp(false), quash(false) { }
};
struct Request
{
RequestType type;
union {
SetDeviceTransform setDeviceTransform;
AlignmentSpeedParams setAlignmentSpeedParams;
};
Request() : type(RequestInvalid) { }
Request(RequestType type) : type(type) { }
Request(AlignmentSpeedParams params) : type(RequestType::RequestSetAlignmentSpeedParams), setAlignmentSpeedParams(params) {}
};
struct Response
{
ResponseType type;
union {
Protocol protocol;
};
Response() : type(ResponseInvalid) { }
Response(ResponseType type) : type(type) { }
};
class DriverPoseShmem {
public:
struct AugmentedPose {
LARGE_INTEGER sample_time;
int deviceId;
vr::DriverPose_t pose;
};
private:
static const uint32_t SYNC_ACTIVE_POSE_B = 0x80000000;
static const uint32_t BUFFERED_SAMPLES = 64 * 1024;
struct ShmemData {
std::atomic<uint64_t> index;
AugmentedPose poses[BUFFERED_SAMPLES];
};
private:
HANDLE hMapFile;
ShmemData* pData;
uint64_t cursor;
AugmentedPose lastPose[vr::k_unMaxTrackedDeviceCount];
std::string LastErrorString(DWORD lastError)
{
LPSTR buffer = nullptr;
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL
);
std::string message(buffer, size);
LocalFree(buffer);
return message;
}
public:
operator bool() const {
return pData != nullptr;
}
bool operator!() const {
return pData == nullptr;
}
DriverPoseShmem() {
hMapFile = INVALID_HANDLE_VALUE;
pData = nullptr;
cursor = 0;
}
~DriverPoseShmem() {
Close();
}
void Close() {
if (pData) UnmapViewOfFile(pData);
if (hMapFile) CloseHandle(hMapFile);
}
bool Create(LPCSTR segment_name) {
Close();
hMapFile = CreateFileMappingA(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
sizeof(ShmemData),
segment_name
);
if (!hMapFile) return false;
pData = reinterpret_cast<ShmemData*>(MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof(ShmemData)
));
return !!pData;
}
void Open(LPCSTR segment_name) {
Close();
hMapFile = OpenFileMappingA(
FILE_MAP_ALL_ACCESS,
FALSE,
segment_name
);
if (!hMapFile) {
throw std::runtime_error("Failed to open pose data shared memory segment: " + LastErrorString(GetLastError()));
}
pData = reinterpret_cast<ShmemData*>(MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof(ShmemData)
));
if (!pData) {
throw std::runtime_error("Failed to map pose data shared memory segment: " + LastErrorString(GetLastError()));
}
char tmp[256];
snprintf(tmp, sizeof tmp, "Opened shmem segment: %p\n", pData);
OutputDebugStringA(tmp);
}
void ReadNewPoses(std::function<void(AugmentedPose const&)> cb) {
if (!pData) throw std::runtime_error("Not open");
uint64_t cur_index = pData->index.load(std::memory_order_acquire);
if (cur_index < cursor || cur_index - cursor > BUFFERED_SAMPLES / 2) {
if (cur_index < BUFFERED_SAMPLES / 2)
cursor = cur_index;
else
cursor = cur_index - BUFFERED_SAMPLES / 2;
}
while (cursor < cur_index) {
cb(pData->poses[cursor % BUFFERED_SAMPLES]);
cursor++;
}
std::atomic_thread_fence(std::memory_order_release);
}
bool GetPose(int index, vr::DriverPose_t& pose, LARGE_INTEGER *pSampleTime = NULL) {
ReadNewPoses([this](AugmentedPose const& pose) {
if (pose.pose.poseIsValid) {
this->lastPose[pose.deviceId] = pose;
}
});
if (index >= 0 && index < vr::k_unMaxTrackedDeviceCount) {
pose = lastPose[index].pose;
if (pSampleTime) *pSampleTime = lastPose[index].sample_time;
return true;
}
}
void SetPose(int index, const vr::DriverPose_t& pose) {
if (index >= vr::k_unMaxTrackedDeviceCount) return;
if (pData == nullptr) return;
AugmentedPose augPose;
augPose.deviceId = index;
augPose.pose = pose;
QueryPerformanceCounter(&augPose.sample_time);
uint64_t cur_index = pData->index.load(std::memory_order_relaxed) + 1;
pData->poses[cur_index % BUFFERED_SAMPLES] = augPose;
pData->index.store(cur_index, std::memory_order_release);
}
};
}
+57 -34
View File
@@ -1,52 +1,75 @@
# OpenVR Space Calibrator
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/hyblocker/OpenVR-SpaceCalibrator/blob/develop/.github/logo_light.png?raw=true">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/hyblocker/OpenVR-SpaceCalibrator/blob/develop/.github/logo_dark.png?raw=true">
<img alt="Space Calibrator" src="https://github.com/hyblocker/OpenVR-SpaceCalibrator/blob/develop/.github/logo.png?raw=true">
</picture>
This helps you use tracked VR devices from one company with any other. It aligns multiple tracking systems with a quick calibration step. It may not work for your setup, but there are many cases that work to a degree, and some work very well.
This program is designed to allow you to synchronise multiple playspaces with one another in SteamVR. This fork of Space Calibrator (spacecal) also supports [continuous calibration](#continuous-calibration).
- Rift CV1 x Vive devices: works very well with the v2 (blue logo) trackers, v1 trackers (grey logo, not in production) have major interference issues in the IR spectrum, controller wands (both gen) and Index controllers work very well.
- Rift S, Quest, Windows MR, other SLAM inside-out tracked HMDs x Vive devices: works very well when you aren't moving around the room far (e.g. Beat Saber) but a lot of walking around causes a nontrivial amount of drift between systems. Your results may vary depending on your space. It's possible some of this can be fixed in software with a better calibration algorithm.
- Quest wireless streaming is particularly bad right now and requires frequent recalibration, but it does work for a short time until one of many factors causes it to drift. With wireless devices, moving slowly when calibrating and using the Slow or Very Slow calibration modes is effective at reducing the initial error.
- Any non-Rift HMD x Touch controllers: does not work, the Oculus driver requires the HMD is a Rift. It's theoretically possible to work around this in software but as far as I know it hasn't been done as it would require a fair amount of reverse engineering effort.
Continuous calibration is a tracking mode which automatically aligns playspaces together, using a tracker on the headset.
There is a community of a few thousand on [**Discord**](https://discord.gg/m7g2Wyj) and a newer community on [**Reddit**](https://www.reddit.com/r/MixedVR/). You may find the answer to your question in [the **wiki**](https://github.com/pushrax/OpenVR-SpaceCalibrator/wiki).
This version of Space Calibrator 2.0 has been rewritten from the ground up for improved robustness, QOL improvements and less tracking issues, amongst others. For a list of differences compared with other versions, please see [the features list](#features)
A quick video of how this works using an old version (~v0.3) is available at https://www.youtube.com/watch?v=W3TnQd9JMl4. The user interface has been upgraded since then; the calibration is now done via a SteamVR dashboard menu, and there's much more configurability.
## Installing
### Install
### Steam
Before following the directions below, download and run the installer for the [latest release](https://github.com/pushrax/OpenVR-SpaceCalibrator/releases). This will automatically set up SteamVR for use with multiple tracking systems (`activateMultipleDrivers: true`). There are many guides that say you need to edit the SteamVR config manually. You do not.
> [!NOTE]
> **Space Calibrator is also available on Steam.**
### Usage
You may find [Space Calibrator on Steam here](https://s.team/a/3368750).
Once Space Calibrator has a calibration, it works in the background to keep your devices configured correctly. Since v0.8, everything aside from creating the calibration is automated.
### From GitHub
### Calibration
To install Space Calibrator, please get the latest installer from the downloads page, and install it. Make sure that you have:
- Installed [Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe).
- Installed SteamVR and run it at least once with a VR headset connected.
- SteamVR is not running before you run the installer. If SteamVR is running the installer will not be able to install Space Calibrator correctly.
As part of first time setup, or when you make a change to your space (e.g. move a sensor), and occasionally as the calibration drifts over time (consumer VR tracking isn't perfectly stable), you'll need to run a calibration:
## Calibration
1. Copy the chaperone/guardian bounds from your HMD's play space. This doesn't need to be run if your HMD's play space hasn't changed since last time you copied it. __Example:__ if you're using the Rift with Vive trackers and you bump a Vive lighthouse, or if the calibration has just drifted a little, you likely don't need to run this step, but if you bump an Oculus sensor you will (after running Oculus guardian setup again).
1. Run SteamVR, with only devices from your HMD's tracking system powered on. __Example:__ for Rift with Vive trackers, don't turn on the trackers yet.
2. Confirm your chaperone/guardian is set up with the walls in the right place. If you change it later, you need to run step again.
3. Open SPACE CAL in the SteamVR dashboard overlay.
4. Click `Copy Chaperone Bounds to profile`
If you do not wish to use continuous calibration, you will have to use regular calibration. This means that every so often you will have to sync your headset's playspace with your tracker's playspace.
2. Calibrate devices.
1. Open SteamVR if you haven't already. Turn on some or all your devices.
2. Open SPACE CAL in the SteamVR dashboard overlay.
3. Select one device from the reference space on the left and one device from the target space on the right. If you turned on multiple devices from one space and can't tell which one is selected, click "Identify selected devices" to blink an LED or vibrate it. __Example:__ for Rift with Vive trackers, you'll see the Touch controllers on the left, and Vive trackers on the right. __Pro tip:__ if you turn on just one Vive tracker, you don't have to figure out which one is selected.
4. Hold these two devices in one hand, like they're glued together. If they slip, calibration won't work as well.
5. Click `Start Calibration`
6. Move and rotate your hand around slowly a few times, like you're calibrating the compass on your phone. You want to sample as many orientations as possible.
7. Done! A profile will be saved automatically. If you haven't already, turn on all your devices. Space Calibrator will automatically apply the calibration to devices as they turn on.
To calibrate:
1. Copy the chaperone/guardian bounds from your HMD's play space
> You will only have to do this once. Connect your VR headset and start SteamVR. Then go to space calibrator's window (it will be minimised), and click the "Copy Chaperone" button.
### Calibration outside VR
2. Open the SteamVR dashboard. At the bottom, click on the Space Calibrator icon.
3. In the Space Calibrator overlay, you'll see two lists at the top. On the left `Reference Space` column, select the controller you'll be calibrating along (e.g. Quest controller, Pico controller). On the right `Target Space`, select your SteamVR tracker (e.g. Vive Ultimate Tracker, Vive Tracker 3.0, Vive Ultimate Tracker). You can use the Identify button to make the controllers blink and tracker LEDs flash to see if you've selected the correct ones.
4. Click the "Start calibration" button, and start calibrating.
You can calibrate without using the dashboard overlay by unminimizing Space Calibrator after opening SteamVR (it starts minimized). This is required if you're calibrating for a lone HMD without any devices in its tracking system.
## Continuous Calibration
### Compiling your own build
> [!IMPORTANT]
> **A tracker attached on your headset is required for this.**
Open `OpenVR-SpaceCalibrator.sln` in Visual Studio 2017 and build. There are no external dependencies.
To enable continuous calibration mode, first select your headset on the left column, then the tracker on your headset on the right column. Once you've done so, click `Start Calibration`, and click cancel. Then click `Continuous Calibration` to enable continuous calibration.
### The math
1. Start SteamVR with the VR headset you wish to use.
2. Turn on **ONLY** the tracker which is attached on the VR headset.
3. Select the VR headset and tracker and calibrate.
4. Turn on your other devices.
5. You should see them line up with you as you after moving around your playspace for a bit for an initial calibration.
See [math.pdf](https://github.com/pushrax/OpenVR-SpaceCalibrator/blob/master/math.pdf) for details.
If you have some ideas for how to improve the calibration process, let me know!
## Features
This version has been rewritten from scratch. It shares little code with the original repository but keeps similar ideas.
Major features:
- The UI has been reworked substantially to improve UX. The goal is to reduce the need of tutorials and have the app explain how to calibrate and what one may do to improve calibrations directly in app rather than elsewhere.
- Calibrations are now more streamlined. The calibration logic has been simplified to attempt minimising the chances of erroneous data being injected into a calibration sequence yielding poor calibrations.
- Calibrations now require you to move around by default. The app will ignore data without enough movement to minimise the chances of poor calibrations arising due to a lack of movement.
- Continuous calibration has been improved to reduce the frequency of mis-calibrations as much as possible.
- Relative calibrations. The aim is to re-formulate how a calibration is stored so that it is now relative to your headset, meaning that if your headset drifts your trackers would along with it, hiding the drift entirely.
- Made calibrating require a minimum amount of movement to ensure that a calibration is valid. This is to improve the success rate of most calibrations.
- Robust logging. Logs are saved at `%APPDATA%/space-calibrator/logs` on Windows, and `~/.local/share/space-calibrator/logs` on Linux.
- Settings are saved to a JSON file at `%APPDATA%/space-calibrator/config.json` on Windows, and `~/.local/share/space-calibrator/config.json` on Linux.
- Linux support. The codebase can now be compiled for Linux x64 amd arm64. Official support assumes Steam Runtime 4, compatibility with other distributions is NOT guaranteed. This is not thoroughly tested but contributions / bug reports are appreciated.
- Space Calibration now supports translations. The app will default to showing text in your system language, and you may override it from the Settings page. For guidance regarding contributing translations please see [TRANSLATING.md](https://github.com/hyblocker/OpenVR-SpaceCalibrator/blob/develop/TRANSLATING.md)
- The UI renderer has been upgraded and now supports either OpenGL, DirectX11 (on Windows only) or Vulkan 1.3. The app will default to DirectX11 on Windows and Vulkan on Linux. This is to reduce issues for end users on buggy GPU drivers causing the app to fail to launch. You can override the renderer by passing `--renderer <opengl|dx11|vulkan>` as launch arguments.
- Base Station management has been integrated into the app to allow you to control them without 3rd party software.
- More things that aren't mentioned here.
## Help
If you need help with setting up this program, please check the [wiki](https://github.com/pushrax/OpenVR-SpaceCalibrator/wiki), or join the [Discord server](https://discord.gg/ja3WgNjC3z).
+72
View File
@@ -0,0 +1,72 @@
# Translating
The ground truth translation will always be available at `assets/en_GB.json`. If you notice that something is different there when compared to the language you're translating please use the `en_GB` file as reference.
Translations are authored in JSON. They're a key value pair of IDs to the text:
```json
{
"app_title": "{0} {1}", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"app_title_vr": "{0} {1} - close VR overlay to use mouse", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"tracking_system_no_systems": "No tracked devices present. Please turn on a device to continue.", /* Should never be hit but you never know */
"ipc_unavailable": "Failed to connect to Space Calibrator SteamVR driver. Please verify file integrity in Properties -> Installed Files, or re-install the app.",
"steamvr_unavailable": "Failed to connect to SteamVR.",
"vr_troubleshooting_hmd_not_found": "Please connect your VR headset and launch SteamVR first.",
"vr_troubleshooting_connect_steamlink": "Please open SteamLink on your VR headset and connect to SteamVR first.",
"vr_troubleshooting_generic": "Please connect your VR headset and launch SteamVR first. ({0} {1})", /* 0 : SteamVR init error (eg 108), 1 : SteamVR init error string (eg Headset not connected) - this string is provided by Steam. */
// space here refers to a tracking space / tracking system.
"reference_space": "Reference space",
"target_space": "Target space",
"select_reference_device": "Select reference device",
"select_target_device": "Select target device"
}
```
The format supports comments, which are usually used to inform you of what numbered arguments are.
Text formatting may use `{N}`, where `N` is a numbered argument so that you can re-order text to make grammatical sense in your language of choice.
For example, you can write: `Arg0: {0} arg1: {1}` ; and itll appear as `Arg0: foo arg1: bar` or `Arg0: {1} arg1: {0}` which appears as `Arg0: bar arg1: foo`.
You can experiment with translations by making a `json` file matching a locale string at `<SPACECAL-DIR>/assets/lang/en_GB.json`
Missing strings will fall back to their respective English strings. For example, if you are editing `it.json` and forgot to add the string `reference_space`, the app will show the English value to the user.
You should also translate the languages block at the bottom of the file. This section is used to present the name of the language to the user in the language selection dropdown in the current language and the language itself. For example, if the user currently has Japanese selected, the entry for French would read as `フランス語 (Français)` in the dropdown.
The supported languages right now are:
- `en_GB.json` (maintained by me, consider it ground truth)
- `en_US.json` English (US)
- `fr.json` French
- `it.json` Italian
- `de.json` German
- `nl.json` Dutch (missing)
- `es_ES.json` Spanish (Spain)
- `es_US.json` Spanish (Latin America) (missing)
- `da.json` Danish (missing)
- `sv.json` Swedish (missing)
- `fi.json` Finnish (missing)
- `no.json` Norwegian
- `bg.json` Bulgarian (missing)
- `pl.json` Polish
- `cs.json` Czech (missing)
- `el.json` Greek (missing)
- `hu.json` Hungarian
- `pt_PT.json` Portuguese (Portugal) (missing)
- `pt_BR.json` Portuguese (Brazil) (missing)
- `ro.json` Romanian (missing)
- `ru.json` Russian
- `tr.json` Turkish (missing)
- `uk.json` Ukrainian (missing)
- `zh_HANS.json` Chinese (Simplified)
- `zh_HANT.json` Chinese (Traditional) (missing)
- `ja.json` Japanese
- `ko.json` Korean
- `th.json` Thai (missing)
- `vi.json` Vietnamese (missing)
These files must live at `<SPACECAL-DIR>/assets/lang/<LANGUAGE>.json`.
Languages with `(missing)` have not been translated yet. If you know the language, feel free to make a translation. Note that machine generated translations (eg AI, Google Translate) will be rejected indiscriminately.
-3
View File
@@ -1,3 +0,0 @@
#pragma once
#define SPACECAL_VERSION_STRING "1.4-bd_-af-r3"
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2016 The M+ Project Authors.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

+278
View File
@@ -0,0 +1,278 @@
{
"app_title": "{0} {1}", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"app_title_vr": "{0} {1} - VR-Overlay schließen, um die Maus zu verwenden", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"tracking_system_no_systems": "Keine Geräte vorhanden. Bitte schalte ein Gerät ein, um fortzufahren", /* Should never be hit but you never know */
"error_unknown_catastrophic": "Etwas ist katastrophal schiefgelaufen! Bitte melde dies dem Entwickler auf GitHub oder in den Steam-Diskussionen, damit es behoben werden kann.",
"ipc_unavailable": "Die Verbindung zum Space Calibrator SteamVR-Treiber konnte nicht hergestellt werden. Bitte überprüfe die Dateiintegrität unter „Eigenschaften“ > „Installierte Dateien“ > „Softwaredateien auf Fehler überprüfen“ oder installiere die App neu.",
"steamvr_unavailable": "Verbindungsfehler mit SteamVR.",
"vr_troubleshooting_hmd_not_found": "Bitte verbinde erst dein VR-Headset und starte SteamVR.",
"vr_troubleshooting_connect_steamlink": "Bitte erst SteamLink auf dem VR-Headset öffnen und mit SteamVR verbinden.",
"vr_troubleshooting_generic": "Bitte verbinde erst dein VR-Headset und starte SteamVR. ({0} {1})", /* 0 : SteamVR init error (eg 108), 1 : SteamVR init error string (eg Headset not connected) - this string is provided by Steam. */
"calibration_title": "Kalibrierung",
"calibration_count_none": "Keine Kalibrierung. Bitte starte eine neue Kalibrierung.", // Shown when there are no calibrations active
"calibration_new": "Neue Kalibrierung",
"calibration_delete": "Lösche Kalibrierung",
"calibration_next": "Nächste Kalibrierung",
"calibration_previous": "Vorherige Kalibrierung",
"calibration_status_none": "Keine aktive Kalibrierung",
"calibration_status_none_description": "Bitte wähle Geräte aus, um mit der Kalibrierung zu starten.",
"calibration_status_edit": "Editiere Kalibrierung",
"calibration_status_edit_description": "Aktuelle Kalibrierung wird bearbeitet.",
"calibration_status_continuous": "Kontinuierliche Kalibrierung",
"calibration_status_continuous_description": "Du hast eine aktive Kalibrierung. Verwende einen befestigten Tracker, um alle paar Sekunden automatisch neu zu kalibrieren.",
"calibration_status_standard": "Standard-Kalibrierung",
"calibration_status_standard_description": "Du hast eine aktive Kalibrierung. Bei Auftreten von Drift musst du eventuell neu kalibrieren.",
// space here refers to a tracking space / tracking system.
"reference_device": "Referenzgerät",
"reference_device_description": "Das Gerät, mit dem du kalibrierst, meistens dein VR-Headset oder ein Kontroller.",
"target_device": "Zielgerät",
"target_device_description": "Dieses Gerät wird genutzt, um deine Tracker oder Kontroller auszurichten.",
"select_reference_device": "Referenzgerät auswählen",
"select_target_device": "Zielgerät auswählen",
"identify_selected_devices": "Ausgewählte Geräte identifizieren (LED blinkt oder vibriert)",
"identify_auto_detect_devices": "Geräte automatisch erkennen",
"calibration_error_reference_hmd_missing": "Zielgerät vom Tracking-System \"{0}\" konnte nicht gefunden werden. Du musst eventuell neu kalibrieren.", /* 0 : tracking system name, eg SteamVR Tracking*/
"calibration_warning_stage_tracking_disabled": "Stage Tracking ist deaktiviert! Für ein besseres Ergebniss öffne auf deinem VR-Headset das Virtual-Desktop-Menü, geh auf den Reiter „Streaming“ und aktiviere „Spielfläche zentrieren (Stage Tracking)“.",
"calibration_warning_stage_tracking_disabled_ignore_button": "Nicht mehr anzeigen",
"calibration_action_start": "Standard-Kalibrierung",
"calibration_action_clear": "Kalibrierung zurücksetzen",
"calibration_action_continuous": "Kontinuierliche Kalibrierung",
"calibration_action_edit": "Kalibrierung bearbeiten",
"save_calibration_profile": "Profil speichern",
"calibration_progress": "Kalibrierungsfortschritt",
"calibration_info_move_around_insufficient_samples": "Halte die ausgewählten Geräte fest zusammen und geh durch deinen Spielbereich, während du sie sanft in Form einer Achter-Bewegung drehst.",
"calibration_speed": "Kalibrierungsgeschwindigkeit",
"calibration_speed_description": "Wie lange die Kalibrierung dauert. Langsamere Kalibrierungen sind meistens genauer.",
"calibration_speed_fast": "Schnell",
"calibration_speed_slow": "Langsam",
"calibration_speed_very_slow": "Sehr langsam",
/* Editing calibrations */
"edit_calibration_rotation": "Rotation",
"edit_calibration_position": "Position (m)",
"edit_calibration_yaw": "Yaw",
"edit_calibration_pitch": "Pitch",
"edit_calibration_roll": "Roll",
"edit_calibration_x": "X",
"edit_calibration_y": "Y",
"edit_calibration_z": "Z",
"edit_calibration_scale": "Skalierung (m)",
"continuous_hide_tracker": "Zielgerät vor Apps verstecken",
"continuous_relative_calibration": "Relative Kalibrierung aktivieren",
"continuous_relative_calibration_description": "Macht die Kalibrierung relativ zum Referenzgerät (meistens dein Headset), sodass sie sich in jedem Frame aktualisiert, ohne dass du neu kalibrieren musst. Das ERFORDERT eine kontinuierliche Kalibrierung.",
/* HMD names for Virtual Desktop */
/* Virtual desktop lies that it's a Quest 2/3 on most headsets, so we have to substitute the real HMD name ourselves. you generally should
NOT have to change these! but some headsets have different names in certain regions (eg PFD MR is YVR MR in China) */
"virtual_desktop_hmd_none": "Keines", /* should never appear but you never know*/
"virtual_desktop_hmd_gearvr": "Samsung Gear VR",
"virtual_desktop_hmd_oculus_go": "Oculus Go",
"virtual_desktop_hmd_oculus_quest_1": "Oculus Quest",
"virtual_desktop_hmd_oculus_quest_2": "Oculus Quest 2",
"virtual_desktop_hmd_meta_quest_pro": "Meta Quest Pro",
"virtual_desktop_hmd_pico_4": "Pico 4",
"virtual_desktop_hmd_meta_quest_3": "Meta Quest 3",
"virtual_desktop_hmd_meta_quest_3s": "Meta Quest 3S",
"virtual_desktop_hmd_pico_4_ultra": "Pico 4 Ultra",
"virtual_desktop_hmd_samsung_galaxy_xr": "Samsung Galaxy XR",
"virtual_desktop_hmd_play_for_dream_mr": "Play For Dream MR",
/* vertical tabs on the left */
"tab_page_calibration": "Kalibrierung",
"tab_page_graphs": "Diagramme",
"tab_page_base_station_management": "Basisstationen",
"tab_page_settings": "Einstellungen",
"tab_page_debug": "Debug",
"tab_page_learn": "Anleitung",
"tab_page_about": "Über",
/* graphs */
"graphs_title": "Diagramme",
/* base stations */
"base_stations_title": "Basisstationen-Verwaltung",
"base_stations_no_bluetooth": "Kein gültiger Bluetooth-Adapter gefunden. Bitte schließe ein BLE-fähiges Bluetooth-Gerät an, um die Basisstationen-Verwaltung zu nutzen.",
"base_stations_none_found": "Keine Basisstationen erkannt. Falls du welche hast, stelle sicher, dass sie eingeschaltet sind.",
"base_stations_header_info": "{0}", // 0: station ID (LHB XXXXXXXX) / nickname
"base_stations_warning_collision": "Kanal-Kollision erkannt! Das führt zu Tracking-Problemen. Jede deiner Basisstationen sollte einen eigenen Kanal nutzen.",
"base_stations_state_sleep": "Ruhezustand",
"base_stations_state_standby": "Standby",
"base_stations_state_active": "Aktiv",
"base_stations_state_unknown": "Unbekannt",
"base_stations_state_faulty": "Fehlerhaft",
"base_stations_channel_select_label": "Wähle einen Basisstationen-Kanal:",
"base_stations_active_channel": "Kanal {0}", // 0: channel number (1-15) ; eg Channel 12
"base_stations_nickname": "Name",
/* actions */
"base_stations_action_wake_all": "Alle aufwecken",
"base_stations_action_sleep_all": "Alle in den Ruhezustand versetzen",
"base_stations_action_fix_collisions": "Kanal-Kollisionen beheben",
"base_stations_btn_wake": "Aufwecken",
"base_stations_btn_standby": "Standby",
"base_stations_btn_sleep": "Ruhezustand",
"base_stations_btn_edit": "Bearbeiten",
"base_stations_btn_save": "Speichern",
"base_station_power_management": "Energieverwaltung der Basisstationen",
"base_stations_power_mgmt_enable": "Energieverwaltung aktivieren *",
"base_stations_power_mgmt_enable_description_standby": "Eingeschaltete Basisstationen wechseln in den Standby-Modus, wenn SteamVR ausgeschaltet wird und wachen wieder auf, sobald SteamVR startet.",
"base_stations_power_mgmt_enable_description_sleep": "Eingeschaltete Basisstationen wechseln in den Ruhezustand, wenn SteamVR ausgeschaltet wird und wachen wieder auf, sobald SteamVR startet.",
"base_stations_power_mgmt_on_startup": "Basisstationen beim Start von SteamVR einschalten",
"base_stations_power_mgmt_on_startup_description": "Ausgeschaltete Basisstationen wachen auf, sobald SteamVR startet.",
"base_stations_power_mgmt_on_shutdown": "Basisstationen beim Beenden von SteamVR ausschalten",
"base_stations_power_mgmt_on_shutdown_description": "Eingeschaltete Basisstationen werden ausgeschaltet, wenn SteamVR ausgeschaltet wird.",
"base_stations_power_mgmt_standby": "Standby",
"base_stations_power_mgmt_standby_description": "Basisstationen schalten nur ihre Laser aus, um schneller wieder aufzuwachen, wenn SteamVR startet. Das ist bei Basisstation-1.0-Modellen und einigen 2.0-Modellen nicht verfügbar – in diesen Fällen wird der Ruhezustand genutzt.",
"base_stations_power_mgmt_sleep": "Ruhezustand",
"base_stations_power_mgmt_sleep_description": "Basisstationen schalten ihre Laser und Motoren komplett aus.",
"base_stations_power_mgmt_warning": "* NICHT EMPFOHLEN für gemeinsam genutzte Spielbereiche mit mehreren SteamVR-Nutzern.",
/* settings */
"settings_title": "Einstellungen",
"settings_advanced": "Erweiterte Einstellungen",
"settings_advanced_description": "Zeigt zusätzliche Einstellungen und Steuerelemente zur Feinabstimmung für erfahrene Nutzer an.",
"settings_calibrate_motion_vectors": "Bewegungsvektoren kalibrieren",
"settings_calibrate_motion_vectors_description": "Verbessert die Bewegungsvorhersage für ein direkteres Tracking. Kann sich auf physikintensive Spiele auswirken.",
"settings_autofix_playspace_jumps": "Spielflächen-Sprünge automatisch korrigieren",
"settings_autofix_playspace_jumps_description": "Versucht plötzliche Sprünge beim Spielflächen-Tracking automatisch zu erkennen und zu korrigieren.",
"settings_enforce_minimum_rotation_variance": "Minimale Rotationsvarianz erzwingen",
"settings_enforce_minimum_rotation_variance_description": "Verwirft automatisch Messwerte mit zu ähnlicher Drehung, um die Kalibrierungsqualität zu verbessern.",
"settings_locale": "Sprache",
"settings_reset": "Alle Einstellungen zurücksetzen",
"settings_reset_description": "Setzt alle Einstellungen auf die Standardwerte zurück. Das ist dauerhaft und kann nicht rückgängig gemacht werden.",
"settings_reset_confirm": "Diese Änderung ist dauerhaft und kann nicht rückgängig gemacht werden. Bist du sicher, dass du fortfahren möchtest?",
"settings_reset_yes": "Ja",
"settings_reset_no": "Nein",
/* about */
"about_title": "Über",
"about_description": "Space Calibrator ist ein kostenloses Open-Source-Tool, mit dem du Geräte verschiedener Hersteller zusammen mit SteamVR nutzen kannst.",
"about_view_source_info": "Du kannst dir den Quellcode auf dem GitHub-Repository ansehen.",
"about_contributors_title": "Mitwirkende",
"about_contributors_description": "Space Calibrator wurde mit der Hilfe folgender Personen möglich gemacht:",
"about_translators_title": "Übersetzer",
"about_translators_description": "Space Calibrator wurde von folgenden Personen übersetzt:",
"about_licenses_title": "Lizenzen",
"about_licenses_description": "Dieses Programm steht unter der MIT-Lizenz. Andere Abhängigkeiten stehen unter anderen Lizenzen. Bitte schau dir die Lizenzen unten für weitere Informationen an.",
"about_link_github": "GitHub",
"about_link_discord": "Discord-Server",
"about_link_logs_dir": "Log-Ordner",
/* self-troubleshooting */
/* found another driver loaded in SteamVR -> legacy ipc connection successful */
"troubleshoot_diagnosis_legacy_ipc_found_title": "Eine andere Instanz von Space Calibrator ist bereits vorhanden",
"troubleshoot_diagnosis_legacy_ipc_found_description": "Eine widersprüchliche Version von Space Calibrator läuft gerade. Du musst sie deinstallieren, um diese Anwendung nutzen zu können. Möchtest du sie automatisch entfernen?",
/* found legacy overlay key registered (non steam) */
"troubleshoot_diagnosis_legacy_overlay_found_title": "Eine andere Instanz von Space Calibrator ist bereits vorhanden",
"troubleshoot_diagnosis_legacy_overlay_found_description": "Eine andere Version von Space Calibrator ist derzeit installiert. Möchtest du sie entfernen?",
"troubleshoot_action_yes": "Ja",
"troubleshoot_action_no": "Nein",
"troubleshoot_action_quit": "Beenden",
/* tutorial */
"learn_title": "Anleitung",
"learn_card_standard_calibration": "Standard-Kalibrierung",
"learn_card_standard_calibration_description": "Lerne, wie du ein Gerätepaar kalibrierst.",
"learn_card_continuous_calibration": "Kontinuierliche Kalibrierung",
"learn_card_continuous_calibration_description": "Kalibriere regelmäßig nach, um Drift während der Session entgegenzuwirken.",
"learn_card_base_station_management": "Basisstationen-Verwaltung",
"learn_card_base_station_management_description": "Steuere deine SteamVR-Basisstationen drahtlos.",
"learn_card_ui_tour": "UI-Tour",
"learn_card_ui_tour_description": "Ein Überblick über die Benutzeroberfläche von Space Calibrator.",
"learn_action_back": "Zurück",
/* these are kinda like a messed up wiki because its inlined into the app like this, oh well lol */
"learn_page_standard_desc": "Die Standard-Kalibrierung richtet zwei Tracking-Systeme aufeinander aus, damit deine VR-Geräte im selben physischen Spielbereich zusammenarbeiten.",
"learn_page_standard_select_devices_title": "Wähle deine Geräte aus",
"learn_page_standard_select_devices_desc": "Bevor es losgeht, muss die App wissen, welche zwei Geräte du kalibrieren möchtest.",
"learn_page_standard_ref_device": "Referenzgerät: Das Gerät, an dem ausgerichtet wird – meistens ein Kontroller.",
"learn_page_standard_target_device": "Zielgerät: Das noch nicht ausgerichtete Gerät, das du kalibrierst – typischerweise ein Tracker oder Ähnliches.",
"learn_page_standard_perform_title": "Kalibrierung durchführen",
"learn_page_standard_perform_step1": "Halte beide Geräte fest zusammen in einer Hand. Halte sie so fest wie möglich, damit sie sich während des Kalibrierungsvorgangs nicht bewegen.",
"learn_page_standard_perform_step2": "Klicke auf die Schaltfläche „Standard-Kalibrierung“, um mit dem Kalibrieren zu beginnen.",
"learn_page_standard_perform_step3": "Bewege dich in deinem Raum, während du die Geräte in einer Achter-Bewegung drehst, wie unten abgebildet.",
"learn_page_standard_speeds_title": "Kalibrierungsgeschwindigkeiten",
"learn_page_standard_speeds_desc": "Du kannst optional die Kalibrierungsgeschwindigkeit ändern, um festzulegen, wie viel Bewegung nötig ist. Mehr Bewegung führt meist zu einer besseren Qualität.",
"learn_page_standard_speed_fast": "Schnell: Dauert nicht so lange, kann aber zu einer schlechteren Kalibrierung führen.",
"learn_page_standard_speed_slow": "Langsam / Sehr langsam: Dauert länger, führt aber meistens zu genaueren Kalibrierungen.",
"learn_page_standard_completion_note": "Sobald der Fortschrittsbalken voll ist, sollten deine Geräte in VR sofort ausgerichtet sein. Falls nicht, versuche es einfach noch einmal.",
"learn_page_standard_drift_title": "Ein Hinweis zum Drift",
"learn_page_standard_drift_desc": "Mit der Zeit können deine Geräte driften, wodurch die Kalibrierung nicht mehr stimmt. In dem Fall musst du neu kalibrieren. Wenn das häufig passiert, solltest du die kontinuierliche Kalibrierung ausprobieren.",
"learn_page_continuous_desc1": "Wenn du merkst, dass sich deine Geräte beim Spielen langsam verschieben, verdrehen oder aus der Position driften, ist die kontinuierliche Kalibrierung die Lösung dafür.",
"learn_page_continuous_desc2": "Anstatt dich dazu zu zwingen, ständig anzuhalten und manuell nachzukalibrieren, hält die kontinuierliche Kalibrierung dein Tracking im Hintergrund automatisch synchron.",
"learn_page_continuous_note_title": "Hinweis",
"learn_page_continuous_note_desc": "Für diese Funktion WIRD ein am Headset befestigter, eigener Tracker benötigt.",
"learn_page_continuous_desc3": "Die kontinuierliche Kalibrierung versucht, deine Geräte ständig nachzukalibrieren, um jeglichen Drift zu beseitigen. Sobald sie eingerichtet ist, musst du dir meist keine Gedanken mehr darüber machen.",
"learn_page_continuous_setup_instructions": "Um die kontinuierliche Kalibrierung einzurichten, wähle zuerst deine Geräte wie auf der Seite „Standard-Kalibrierung“ beschrieben aus. Wähle dein Headset als Referenzgerät und den Kalibrierungs-Tracker als Zielgerät. Klicke dann auf die Schaltfläche „Kontinuierliche Kalibrierung“, um zu starten. Es kann etwas dauern, bis die erste Kalibrierung abgeschlossen ist – bewege dich also ein wenig, damit die App dein Tracking erfassen kann!",
"learn_page_continuous_should_use_title": "Solltest du die kontinuierliche Kalibrierung nutzen?",
"learn_page_continuous_should_use_standard": "Nutze die Standard-Kalibrierung, wenn du keinen zusätzlichen Tracker am Headset hast, keine sehr langen VR-Sessions machst oder es dir nichts ausmacht, bei Drift ab und zu manuell nachzukalibrieren.",
"learn_page_continuous_should_use_continuous": "Nutze die kontinuierliche Kalibrierung, wenn du dich praktisch nie wieder um Drift kümmern willst. Das wird empfohlen, wenn du viel Zeit in VR verbringst oder im Laufe der Zeit starken Drift bemerkst.",
"learn_page_continuous_physical_setup_title": "Physisches Setup (Tracker am Headset befestigen)",
"learn_page_continuous_physical_setup_desc1": "Für die kontinuierliche Kalibrierung benötigst du einen eigenen Tracker, der an deinem VR-Headset angebracht ist. Je nach Headset und Tracker gibt es dafür verschiedene Halterungen. Im Internet findest du oft passende 3D-Druckmodelle oder du fragst in der Community nach Erfahrungen.",
"learn_page_continuous_physical_setup_desc2": "Einmal montiert, sollte der Tracker fest am Headset sitzen. Er darf nicht wackeln, sich verdrehen oder schwingen. Je stabiler die Halterung, desto genauer und gleichmäßiger ist die Kalibrierung.",
"learn_page_basestation_desc": "Space Calibrator ermöglicht dir die Steuerung von SteamVR-Basisstationen, wenn du einen kompatiblen BLE-Bluetooth-Adapter hast.",
"learn_page_basestation_1_title": "Basisstation 1.0-Modelle",
"learn_page_basestation_1_desc": "Du kannst diese Modelle nur ein- oder ausschalten. Das drahtlose Einstellen der Kanäle wird nicht unterstützt – dafür musst du die physische Taste auf der Rückseite der Basisstation drücken.",
"learn_page_basestation_2_title": "Basisstation 2.0-Modelle",
"learn_page_basestation_2_desc1": "Diese von Valve und HTC hergestellten Modelle bieten eine bessere Unterstützung für drahtlose Steuerung als 1.0-Basisstationen. Du kannst sie ein- und ausschalten sowie ihren Kanal festlegen. 2.0-Basisstationen müssen jeweils auf einem eigenen Kanal laufen, sonst funktionieren sie nicht. Space Calibrator hilft dir beim Beheben, falls es zu Konflikten kommt.",
"learn_page_basestation_2_desc2": "Um den Kanal einer 2.0-Basisstation zu ändern, gehe auf den Reiter „Basisstationen“, klicke bei der gewünschten Basisstation auf Bearbeiten und wähle den gewünschten Kanal (grün markiert).",
"learn_page_basestation_auto_power_title": "Automatische Energieverwaltung",
"learn_page_basestation_auto_power_desc": "Unter „Einstellungen“ kannst du die automatische Basisstationen-Verwaltung aktivieren. Dadurch werden deine Basisstationen eingeschaltet, wenn Space Calibrator startet, und ausgeschaltet, wenn du SteamVR beendest (während Space Calibrator läuft). Hinweis: Wenn du Space Calibrator vor SteamVR manuell schließt, werden die Basisstationen nicht automatisch ausgeschaltet.",
"learn_page_ui_tour_desc": "Diese Benutzeroberfläche ist in mehrere Reiter unterteilt, die jeweils verschiedene Funktionen enthalten.",
"learn_page_ui_tour_calibration_heading": "Reiter „Kalibrierung“",
"learn_page_ui_tour_cal_desc1": "Der Reiter „Kalibrierung“ enthält alle Einstellungen rund um die Kalibrierung. Hier steuerst du deine Kalibrierungen.",
"learn_page_ui_tour_cal_desc2": "Oben siehst du ein Feld, das anzeigt, ob eine Kalibrierung aktiv ist und wenn ja, welcher Typ.",
"learn_page_ui_tour_cal_desc3": "Darunter wählst du aus, mit welchen Geräten kalibriert werden soll. Wähle zuerst die Tracking-Systeme aus. Wähle dann im linken Bereich das Gerät, an dem ausgerichtet wird, und im rechten Bereich das Gerät, das kalibriert werden soll.",
"learn_page_ui_tour_cal_desc4": "Darunter befinden sich die Schaltflächen zum Starten der Kalibrierung. Wenn du unsicher bist, welche Methode du nutzen sollst, lies dir die Seiten zu standard und kontinuierlicher Kalibrierung durch.",
"learn_page_ui_tour_cal_desc5": "Am Ende des Kalibrierungs-Reiters findest du weitere Optionen. Einige dieser Optionen werden erst sichtbar, wenn die erweiterten Einstellungen aktiviert sind.",
"learn_page_ui_tour_base_stations_heading": "Reiter „Basisstationen“",
"learn_page_ui_tour_bs_desc": "Hier kannst du mit einem unterstützten BLE-Bluetooth-Adapter Basisstationen in deinem Raum steuern. Nach der Ersteinrichtung musst du hier normalerweise selten etwas ändern.",
"learn_page_ui_tour_settings_heading": "Reiter „Einstellungen“",
"learn_page_ui_tour_settings_desc1": "Hier kannst du globale Einstellungen für das Programm festlegen. Dazu gehören erweiterte Einstellungen, die Basisstationen-Verwaltung und die Sprache der Benutzeroberfläche.",
"learn_page_ui_tour_settings_desc2": "Manche Funktionen in dieser App erfordern das Aktivieren der erweiterten Einstellungen. Aktiviere diese nur, wenn du weißt, was du tust.",
/* Names of the various languages one may select. Shown in settings */
"languages_en_GB": "Englisch (UK)",
"languages_en_US": "Englisch (US)",
"languages_fr": "Französisch",
"languages_it": "Italienisch",
"languages_de": "Deutsch",
"languages_nl": "Niederländisch",
"languages_es_ES": "Spanisch (Spanien)",
"languages_es_US": "Spanisch (Lateinamerika)",
"languages_da": "Dänisch",
"languages_sv": "Schwedisch",
"languages_fi": "Finnisch",
"languages_no": "Norwegisch",
"languages_bg": "Bulgarisch",
"languages_pl": "Polnisch",
"languages_cs": "Tschechisch",
"languages_el": "Griechisch",
"languages_hu": "Ungarisch",
"languages_pt_PT": "Portugiesisch (Portugal)",
"languages_pt_BR": "Portugiesisch (Brasilien)",
"languages_ro": "Rumänisch",
"languages_ru": "Russisch",
"languages_tr": "Türkisch",
"languages_uk": "Ukrainisch",
"languages_zh_HANS": "Chinesisch (Vereinfacht)",
"languages_zh_HANT": "Chinesisch (Traditionell)",
"languages_ja": "Japanisch",
"languages_ko": "Koreanisch",
"languages_th": "Thailändisch",
"languages_vi": "Vietnamesisch",
"languages_system": "Systemsprache"
}
+280
View File
@@ -0,0 +1,280 @@
{
"app_title": "{0} {1}", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"app_title_vr": "{0} {1} - close VR overlay to use mouse", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"tracking_system_no_systems": "No tracked devices present. Please turn on a device to continue.", /* Should never be hit but you never know */
"error_unknown_catastrophic": "Something went catastrophically wrong! Please report this to the developer either on GitHub or Steam Discussions so that this can be addressed.",
"ipc_unavailable": "Failed to connect to the Space Calibrator SteamVR driver. Please verify file integrity in Properties -> Installed Files, or re-install the app.",
"steamvr_unavailable": "Failed to connect to SteamVR.",
"vr_troubleshooting_hmd_not_found": "Please connect your VR headset and launch SteamVR first.",
"vr_troubleshooting_connect_steamlink": "Please open SteamLink on your VR headset and connect to SteamVR first.",
"vr_troubleshooting_generic": "Please connect your VR headset and launch SteamVR first. ({0} {1})", /* 0 : SteamVR init error (eg 108), 1 : SteamVR init error string (eg Headset not connected) - this string is provided by Steam. */
"ipc_unvailable_auto_fix": "Try to fix",
"ipc_unvailable_auto_fix_note_reboot": "Please restart your computer for the changes to take affect.",
"calibration_title": "Calibration",
"calibration_count_none": "No Calibration. Please create a new calibration to get started.", // Shown when there are no calibrations active
"calibration_new": "New Calibration",
"calibration_delete": "Delete Calibration",
"calibration_next": "Next Calibration",
"calibration_previous": "Previous Calibration",
"calibration_status_none": "No Calibration active",
"calibration_status_none_description": "Please select a pair of devices and calibrate to get started.",
"calibration_status_edit": "Editing calibration",
"calibration_status_edit_description": "Modifying the current calibration.",
"calibration_status_continuous": "Continuous Calibration",
"calibration_status_continuous_description": "You have an active calibration. Uses a mounted tracker to re-calibrate every few seconds automatically.",
"calibration_status_standard": "Standard Calibration",
"calibration_status_standard_description": "You have an active calibration. You may need to re-calibrate if drift occurs.",
// space here refers to a tracking space / tracking system.
"reference_device": "Reference device",
"reference_device_description": "This device is what you are calibrating with, usually your VR headset or a controller.",
"target_device": "Target device",
"target_device_description": "This device will be used to line up your trackers or controllers.",
"select_reference_device": "Select reference device",
"select_target_device": "Select target device",
"identify_selected_devices": "Identify selected devices (blinks LED or vibrates)",
"identify_auto_detect_devices": "Auto-detect devices",
"calibration_error_reference_hmd_missing": "Unable to find target device from tracking system \"{0}\". You may need to re-calibrate.", /* 0 : tracking system name, eg SteamVR Tracking*/
"calibration_warning_stage_tracking_disabled": "Stage tracking is disabled! For a better experience, on your Quest open the Virtual Desktop menu, go to the Streaming tab and enable \"Center to play area (Stage Tracking)\".",
"calibration_warning_stage_tracking_disabled_ignore_button": "Do not show this again",
"calibration_action_start": "Standard Calibration",
"calibration_action_clear": "Clear Calibration",
"calibration_action_continuous": "Continuous Calibration",
"calibration_action_edit": "Edit Calibration",
"save_calibration_profile": "Save Profile",
"calibration_progress": "Calibration Progress",
"calibration_info_move_around_insufficient_samples": "Hold your selected devices together, then walk around your playspace while gently rotating them in figure-eight patterns.",
"calibration_speed": "Calibration Speed",
"calibration_speed_description": "How long it takes to calibrate. Slower calibrations tend to be more accurate.",
"calibration_speed_fast": "Fast",
"calibration_speed_slow": "Slow",
"calibration_speed_very_slow": "Very Slow",
/* Editing calibrations */
"edit_calibration_rotation": "Rotation",
"edit_calibration_position": "Position (m)",
"edit_calibration_yaw": "Yaw",
"edit_calibration_pitch": "Pitch",
"edit_calibration_roll": "Roll",
"edit_calibration_x": "X",
"edit_calibration_y": "Y",
"edit_calibration_z": "Z",
"edit_calibration_scale": "Scale (m)",
"continuous_hide_tracker": "Hide target device from apps",
"continuous_relative_calibration": "Enable relative calibration",
"continuous_relative_calibration_description": "Makes the calibration relative to the reference device (typically your headset) so that it updates every frame without needing to re-calibrate. This REQUIRES Continuous Calibration.",
/* HMD names for Virtual Desktop */
/* Virtual desktop lies that it's a Quest 2/3 on most headsets, so we have to substitute the real HMD name ourselves. you generally should
NOT have to change these! but some headsets have different names in certain regions (eg PFD MR is YVR MR in China) */
"virtual_desktop_hmd_none": "None", /* should never appear but you never know*/
"virtual_desktop_hmd_gearvr": "Samsung Gear VR",
"virtual_desktop_hmd_oculus_go": "Oculus Go",
"virtual_desktop_hmd_oculus_quest_1": "Oculus Quest",
"virtual_desktop_hmd_oculus_quest_2": "Oculus Quest 2",
"virtual_desktop_hmd_meta_quest_pro": "Meta Quest Pro",
"virtual_desktop_hmd_pico_4": "Pico 4",
"virtual_desktop_hmd_meta_quest_3": "Meta Quest 3",
"virtual_desktop_hmd_meta_quest_3s": "Meta Quest 3S",
"virtual_desktop_hmd_pico_4_ultra": "Pico 4 Ultra",
"virtual_desktop_hmd_samsung_galaxy_xr": "Samsung Galaxy XR",
"virtual_desktop_hmd_play_for_dream_mr": "Play For Dream MR",
/* vertical tabs on the left */
"tab_page_calibration": "Calibration",
"tab_page_graphs": "Graphs",
"tab_page_base_station_management": "Base Stations",
"tab_page_settings": "Settings",
"tab_page_debug": "Debug",
"tab_page_learn": "Learn",
"tab_page_about": "About",
/* graphs */
"graphs_title": "Graphs",
/* base stations */
"base_stations_title": "Base Station Management",
"base_stations_no_bluetooth": "Failed to find a valid Bluetooth adapter. Please connect a BLE capable Bluetooth device to be able to use Base Station Management.",
"base_stations_none_found": "No Base Stations detected. If you have any, make sure they are plugged in.",
"base_stations_header_info": "{0}", // 0: station ID (LHB XXXXXXXX) / nickname
"base_stations_warning_collision": "Channel collision detected! This will cause tracking issues. Your Base Stations should each use a unique channel.",
"base_stations_state_sleep": "Asleep",
"base_stations_state_standby": "Standby",
"base_stations_state_active": "Active",
"base_stations_state_unknown": "Unknown",
"base_stations_state_faulty": "Faulty",
"base_stations_channel_select_label": "Select a Base Station channel:",
"base_stations_active_channel": "Channel {0}", // 0: channel number (1-15) ; eg Channel 12
"base_stations_nickname": "Nickname",
/* actions */
"base_stations_action_wake_all": "Wake All",
"base_stations_action_sleep_all": "Sleep All",
"base_stations_action_fix_collisions": "Fix Channel Collisions",
"base_stations_btn_wake": "Wake",
"base_stations_btn_standby": "Standby",
"base_stations_btn_sleep": "Sleep",
"base_stations_btn_edit": "Edit",
"base_stations_btn_save": "Save",
"base_station_power_management": "Base Station Power Management",
"base_stations_power_mgmt_enable": "Enable power management *",
"base_stations_power_mgmt_enable_description_standby": "Powered Base Stations go into standby mode when SteamVR is not in use, and wake up again when SteamVR starts.",
"base_stations_power_mgmt_enable_description_sleep": "Powered Base Stations go into sleep mode when SteamVR is not in use, and wake up again when SteamVR starts.",
"base_stations_power_mgmt_on_startup": "Turn on Base Stations on SteamVR startup",
"base_stations_power_mgmt_on_startup_description": "Unpowered Base Stations wake up when SteamVR starts.",
"base_stations_power_mgmt_on_shutdown": "Turn off Base Stations on SteamVR shutdown",
"base_stations_power_mgmt_on_shutdown_description": "Powered Base Stations are powered down when SteamVR is not in use.",
"base_stations_power_mgmt_standby": "Standby",
"base_stations_power_mgmt_standby_description": "Base Stations will power off only their lasers for a faster wake up when SteamVR starts. This is unavailable on Base Station 1.0 models and some Base Station 2.0 models, and sleep will be used in these cases.",
"base_stations_power_mgmt_sleep": "Sleep",
"base_stations_power_mgmt_sleep_description": "Base Stations will power off their lasers and motors.",
"base_stations_power_mgmt_warning": "* NOT RECOMMENDED for shared tracking spaces with multiple SteamVR users.",
/* settings */
"settings_title": "Settings",
"settings_advanced": "Advanced settings",
"settings_advanced_description": "Shows extra settings and fine-tuning controls for power users.",
"settings_calibrate_motion_vectors": "Calibrate motion vectors",
"settings_calibrate_motion_vectors_description": "Improves movement prediction for more responsive tracking. May affect physics-heavy games.",
"settings_autofix_playspace_jumps": "Auto-correct playspace jumps",
"settings_autofix_playspace_jumps_description": "Attempts to automatically detect and correct sudden jumps in playspace tracking.",
"settings_enforce_minimum_rotation_variance": "Enforce minimum rotation variance",
"settings_enforce_minimum_rotation_variance_description": "Automatically rejects samples that are rotationally similar to improve calibration quality.",
"settings_locale": "Language",
"settings_reset": "Reset all settings",
"settings_reset_description": "Resets all settings to default. This is permanent and cannot be undone.",
"settings_reset_confirm": "This change is permanent and cannot be undone. Are you sure you want to continue?",
"settings_reset_yes": "Yes",
"settings_reset_no": "No",
/* about */
"about_title": "About",
"about_description": "Space Calibrator is a free and open source tool to allow you to use multiple vendors' devices together with SteamVR.",
"about_view_source_info": "You may view the source code on the GitHub repository.",
"about_contributors_title": "Contributors",
"about_contributors_description": "Space Calibrator is made possible with the help of the following people:",
"about_translators_title": "Translators",
"about_translators_description": "Space Calibrator has been translated by the following people:",
"about_licenses_title": "Licenses",
"about_licenses_description": "This program is licensed under the MIT license. Other dependencies are licensed under other licenses, please view the licenses below for more information.",
"about_link_github": "GitHub",
"about_link_discord": "Discord Server",
"about_link_logs_dir": "Logs folder",
/* self-troubleshooting */
/* found another driver loaded in SteamVR -> legacy ipc connection successful */
"troubleshoot_diagnosis_legacy_ipc_found_title": "Another instance of Space Calibrator is already present",
"troubleshoot_diagnosis_legacy_ipc_found_description": "A conflicting version of Space Calibrator is currently running. You will have to uninstall it to be able to use this application. Would you like to automatically remove it?",
/* found legacy overlay key registered (non steam) */
"troubleshoot_diagnosis_legacy_overlay_found_title": "Another instance of Space Calibrator is already present",
"troubleshoot_diagnosis_legacy_overlay_found_description": "Another version of Space Calibrator is currently installed. Would you like to remove it?",
"troubleshoot_action_yes": "Yes",
"troubleshoot_action_no": "No",
"troubleshoot_action_quit": "Quit",
/* tutorial */
"learn_title": "Learn",
"learn_card_standard_calibration": "Standard Calibration",
"learn_card_standard_calibration_description": "Learn how to calibrate a pair of devices.",
"learn_card_continuous_calibration": "Continuous Calibration",
"learn_card_continuous_calibration_description": "Calibrate frequently to counteract drift mid-session.",
"learn_card_base_station_management": "Base Station Management",
"learn_card_base_station_management_description": "Wirelessly control your SteamVR Base Stations.",
"learn_card_ui_tour": "UI Tour",
"learn_card_ui_tour_description": "An overview of the Space Calibrator interface.",
"learn_action_back": "Back",
/* these are kinda like a messed up wiki because its inlined into the app like this, oh well lol */
"learn_page_standard_desc": "Standard Calibration aligns two tracking systems together so that your VR devices work together in the same physical playspace.",
"learn_page_standard_select_devices_title": "Select your devices",
"learn_page_standard_select_devices_desc": "Before starting, the app needs to know which two devices you want to align.",
"learn_page_standard_ref_device": "Reference Device: This device is what you're calibrating to, usually a controller.",
"learn_page_standard_target_device": "Target Device: The unaligned device you're calibrating with, typically a tracker or similar.",
"learn_page_standard_perform_title": "Perform the Calibration",
"learn_page_standard_perform_step1": "Hold both devices together firmly in one hand. Hold them as tight as you can so that they do not move during the calibration process.",
"learn_page_standard_perform_step2": "Click on the Standard Calibration button to begin calibrating.",
"learn_page_standard_perform_step3": "Move around your room while moving your devices in a figure eight, as illustrated below.",
"learn_page_standard_speeds_title": "Calibration Speeds",
"learn_page_standard_speeds_desc": "You can optionally alter the calibration speed to affect how much movement is required. More movement tends to improve calibration quality.",
"learn_page_standard_speed_fast": "Fast: Takes less time to calibrate, but may result in poorer quality calibrations.",
"learn_page_standard_speed_slow": "Slow / Very Slow: Takes longer to calibrate, and tends to yield more accurate calibrations.",
"learn_page_standard_completion_note": "Once the progress bar fills up, your devices should instantly line up in VR. If this doesn't happen, try calibrating again.",
"learn_page_standard_drift_title": "A Note on Drift",
"learn_page_standard_drift_desc": "Over time, your devices may drift, breaking the calibration. If this happens, you will have to re-calibrate to correct it. If you have to do this frequently, you should consider using Continuous Calibration.",
"learn_page_continuous_desc1": "If you notice your devices slowly shifting, rotating, or drifting out of place while playing, Continuous Calibration is a solution to these issues.",
"learn_page_continuous_desc2": "Instead of forcing you to stop and manually calibrate every so often, Continuous Calibration automatically keeps your tracking synced in the background while you play.",
"learn_page_continuous_note_title": "Note",
"learn_page_continuous_note_desc": "A dedicated tracker mounted on your headset IS required to use this feature.",
"learn_page_continuous_desc3": "Continuous Calibration attempts to re-calibrate your devices frequently to eliminate any and all drift. It tends to make calibration something you never think about once it's set up.",
"learn_page_continuous_setup_instructions": "To setup Continuous Calibration, first select your devices as explained in the Standard Calibration page. You will want your headset selected as the reference device, and the calibration tracker as the target device. Then click the Continuous Calibration button to start calibrating. It may take a bit of time until the initial calibration is completed, so move around to let the app learn about your tracking!",
"learn_page_continuous_should_use_title": "Should you use Continuous Calibration?",
"learn_page_continuous_should_use_standard": "Use Standard Calibration if you do not have an extra tracker to mount on your headset, do not spend long sessions in VR, or don't mind re-calibrating every so often whenever drift happens.",
"learn_page_continuous_should_use_continuous": "Use Continuous Calibration if you want to practically never think about drift again. This is recommended if you spend a lot of time in VR or notice significant amounts of drift over time.",
"learn_page_continuous_physical_setup_title": "Physical setup (mounting the tracker onto your headset)",
"learn_page_continuous_physical_setup_desc1": "To use Continuous Calibration you will need a dedicated tracker attached onto your VR headset. There are various mounts depending on which specific VR headset and tracker you are using. You can typically find 3D models on the internet you can print to get started with, or you can ask around in the community to see what other people did.",
"learn_page_continuous_physical_setup_desc2": "Once mounted, the tracker should be securely attached to the headset. It should not wobble, swivel or shake. The better the mounting the more consistent calibrations should be.",
"learn_page_basestation_desc": "Space Calibrator allows you to control SteamVR Base Stations if you have a compatible BLE Bluetooth adapter available.",
"learn_page_basestation_1_title": "Base Station 1.0 Models",
"learn_page_basestation_1_desc": "You can only turn these on or off. Setting channels wirelessly is unsupported, and you will have to press the physical button on the back of the Base Station to alternate between channels.",
"learn_page_basestation_2_title": "Base Station 2.0 Models",
"learn_page_basestation_2_desc1": "Manufactured by both Valve and HTC, these have better support for wireless management than 1.0 Base Stations. With these you're able to turn them on and off, and also set their channel. 2.0 Base Stations must each be on a unique channel, otherwise they will not work. Space Calibrator will offer to help you fix this if it happens.",
"learn_page_basestation_2_desc2": "To change the channel of a 2.0 Base Station, navigate to the Base Station Management tab, select the edit button for the Base Station you wish to control, and click on the desired channel (marked in green).",
"learn_page_basestation_auto_power_title": "Automatic Power Management",
"learn_page_basestation_auto_power_desc": "You can go to the Settings tab to enable automatic Base Station Management, which will turn on your Base Stations when Space Calibrator is started, and turn them off when you close SteamVR (with Space Calibrator running). Note that manually closing Space Calibrator before closing SteamVR will NOT turn off any Base Stations automatically.",
"learn_page_ui_tour_desc": "This interface is divided into several tabs each containing various things.",
"learn_page_ui_tour_calibration_heading": "Calibration Tab",
"learn_page_ui_tour_cal_desc1": "The Calibration tab contains controls relating to calibrations. You control your calibrations here.",
"learn_page_ui_tour_cal_desc2": "At the top, you will see a card showing you whether you have a calibration active, and if so, which type.",
"learn_page_ui_tour_cal_desc3": "Below that, you can select what devices to calibrate with. First you should select the tracking systems you wish to calibrate. Then, on the left pane you should select what you're going to calibrate with. And on the right pane you should select what you're going to calibrate.",
"learn_page_ui_tour_cal_desc4": "Below that are the buttons to perform a calibration. If you are unsure which calibration you should do, please read the pages talking about Standard and Continuous Calibration to learn more.",
"learn_page_ui_tour_cal_desc5": "At the bottom of the Calibration tab you will find several options to control the calibration. A number of these settings require advanced settings to be visible.",
"learn_page_ui_tour_base_stations_heading": "Base Station Management Tab",
"learn_page_ui_tour_bs_desc": "Here you can control any Base Stations in your room with a supported BLE Bluetooth adapter. You should typically not have to touch this beyond initial setup.",
"learn_page_ui_tour_settings_heading": "Settings Tab",
"learn_page_ui_tour_settings_desc1": "Here you may set some global settings for the program. These include advanced settings, Base Station Management and UI language.",
"learn_page_ui_tour_settings_desc2": "Some features in this app require Advanced Settings to be enabled. You should only enable this if you know what you're doing.",
/* Names of the various languages one may select. Shown in settings */
"languages_en_GB": "English (UK)",
"languages_en_US": "English (US)",
"languages_fr": "French",
"languages_it": "Italian",
"languages_de": "German",
"languages_nl": "Dutch",
"languages_es_ES": "Spanish (Spain)",
"languages_es_US": "Spanish (Latin America)",
"languages_da": "Danish",
"languages_sv": "Swedish",
"languages_fi": "Finnish",
"languages_no": "Norwegian",
"languages_bg": "Bulgarian",
"languages_pl": "Polish",
"languages_cs": "Czech",
"languages_el": "Greek",
"languages_hu": "Hungarian",
"languages_pt_PT": "Portuguese (Portugal)",
"languages_pt_BR": "Portuguese (Brazil)",
"languages_ro": "Romanian",
"languages_ru": "Russian",
"languages_tr": "Turkish",
"languages_uk": "Ukrainian",
"languages_zh_HANS": "Chinese (Simplified)",
"languages_zh_HANT": "Chinese (Traditional)",
"languages_ja": "Japanese",
"languages_ko": "Korean",
"languages_th": "Thai",
"languages_vi": "Vietnamese",
"languages_system": "System Language"
}
+278
View File
@@ -0,0 +1,278 @@
{
"app_title": "{0} {1}", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"app_title_vr": "{0} {1} - close VR overlay to use mouse", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"tracking_system_no_systems": "No tracked devices present. Please turn on a device to continue.", /* Should never be hit but you never know */
"error_unknown_catastrophic": "Something went catastrophically wrong! Please report this to the developer either on GitHub or Steam Discussions so that this can be addressed.",
"ipc_unavailable": "Failed to connect to the Space Calibrator SteamVR driver. Please verify file integrity in Properties -> Installed Files, or reinstall the app.",
"steamvr_unavailable": "Failed to connect to SteamVR.",
"vr_troubleshooting_hmd_not_found": "Please connect your VR headset and launch SteamVR first.",
"vr_troubleshooting_connect_steamlink": "Please open SteamLink on your VR headset and connect to SteamVR first.",
"vr_troubleshooting_generic": "Please connect your VR headset and launch SteamVR first. ({0} {1})", /* 0 : SteamVR init error (eg 108), 1 : SteamVR init error string (eg Headset not connected) - this string is provided by Steam. */
"calibration_title": "Calibration",
"calibration_count_none": "No Calibration. Please create a new calibration to get started.", // Shown when there are no calibrations active
"calibration_new": "New Calibration",
"calibration_delete": "Delete Calibration",
"calibration_next": "Next Calibration",
"calibration_previous": "Previous Calibration",
"calibration_status_none": "No Calibration active",
"calibration_status_none_description": "Please select a pair of devices and calibrate to get started.",
"calibration_status_edit": "Editing calibration",
"calibration_status_edit_description": "Modifying the current calibration.",
"calibration_status_continuous": "Continuous Calibration",
"calibration_status_continuous_description": "You have an active calibration. Uses a mounted tracker to recalibrate every few seconds automatically.",
"calibration_status_standard": "Standard Calibration",
"calibration_status_standard_description": "You have an active calibration. You may need to recalibrate if drift occurs.",
// space here refers to a tracking space / tracking system.
"reference_device": "Reference device",
"reference_device_description": "This device is what you are calibrating with, usually your VR headset or a controller.",
"target_device": "Target device",
"target_device_description": "This device will be used to line up your trackers or controllers.",
"select_reference_device": "Select reference device",
"select_target_device": "Select target device",
"identify_selected_devices": "Identify selected devices (blinks LED or vibrates)",
"identify_auto_detect_devices": "Auto-detect devices",
"calibration_error_reference_hmd_missing": "Unable to find target device from tracking system \"{0}\". You may need to recalibrate.", /* 0 : tracking system name, eg SteamVR Tracking*/
"calibration_warning_stage_tracking_disabled": "Stage tracking is disabled! For a better experience, on your Quest open the Virtual Desktop menu, go to the Streaming tab and enable \"Center to play area (Stage Tracking)\".",
"calibration_warning_stage_tracking_disabled_ignore_button": "Do not show this again",
"calibration_action_start": "Standard Calibration",
"calibration_action_clear": "Clear Calibration",
"calibration_action_continuous": "Continuous Calibration",
"calibration_action_edit": "Edit Calibration",
"save_calibration_profile": "Save Profile",
"calibration_progress": "Calibration Progress",
"calibration_info_move_around_insufficient_samples": "Hold your selected devices together, then walk around your playspace while gently rotating them in figure-eight patterns.",
"calibration_speed": "Calibration Speed",
"calibration_speed_description": "How long it takes to calibrate. Slower calibrations tend to be more accurate.",
"calibration_speed_fast": "Fast",
"calibration_speed_slow": "Slow",
"calibration_speed_very_slow": "Very Slow",
/* Editing calibrations */
"edit_calibration_rotation": "Rotation",
"edit_calibration_position": "Position (m)",
"edit_calibration_yaw": "Yaw",
"edit_calibration_pitch": "Pitch",
"edit_calibration_roll": "Roll",
"edit_calibration_x": "X",
"edit_calibration_y": "Y",
"edit_calibration_z": "Z",
"edit_calibration_scale": "Scale (m)",
"continuous_hide_tracker": "Hide target device from apps",
"continuous_relative_calibration": "Enable relative calibration",
"continuous_relative_calibration_description": "Makes the calibration relative to the reference device (typically your headset) so that it updates every frame without needing to recalibrate. This REQUIRES Continuous Calibration.",
/* HMD names for Virtual Desktop */
/* Virtual desktop lies that it's a Quest 2/3 on most headsets, so we have to substitute the real HMD name ourselves. you generally should
NOT have to change these! but some headsets have different names in certain regions (eg PFD MR is YVR MR in China) */
"virtual_desktop_hmd_none": "None", /* should never appear but you never know*/
"virtual_desktop_hmd_gearvr": "Samsung Gear VR",
"virtual_desktop_hmd_oculus_go": "Oculus Go",
"virtual_desktop_hmd_oculus_quest_1": "Oculus Quest",
"virtual_desktop_hmd_oculus_quest_2": "Oculus Quest 2",
"virtual_desktop_hmd_meta_quest_pro": "Meta Quest Pro",
"virtual_desktop_hmd_pico_4": "Pico 4",
"virtual_desktop_hmd_meta_quest_3": "Meta Quest 3",
"virtual_desktop_hmd_meta_quest_3s": "Meta Quest 3S",
"virtual_desktop_hmd_pico_4_ultra": "Pico 4 Ultra",
"virtual_desktop_hmd_samsung_galaxy_xr": "Samsung Galaxy XR",
"virtual_desktop_hmd_play_for_dream_mr": "Play For Dream MR",
/* vertical tabs on the left */
"tab_page_calibration": "Calibration",
"tab_page_graphs": "Graphs",
"tab_page_base_station_management": "Base Stations",
"tab_page_settings": "Settings",
"tab_page_debug": "Debug",
"tab_page_learn": "Learn",
"tab_page_about": "About",
/* graphs */
"graphs_title": "Graphs",
/* base stations */
"base_stations_title": "Base Station Management",
"base_stations_no_bluetooth": "Failed to find a valid Bluetooth adapter. Please connect a BLE capable Bluetooth device to be able to use Base Station Management.",
"base_stations_none_found": "No Base Stations detected. If you have any, make sure they are plugged in.",
"base_stations_header_info": "{0}", // 0: station ID (LHB XXXXXXXX) / nickname
"base_stations_warning_collision": "Channel collision detected! This will cause tracking issues. Your Base Stations should each use a unique channel.",
"base_stations_state_sleep": "Asleep",
"base_stations_state_standby": "Standby",
"base_stations_state_active": "Active",
"base_stations_state_unknown": "Unknown",
"base_stations_state_faulty": "Faulty",
"base_stations_channel_select_label": "Select a Base Station channel:",
"base_stations_active_channel": "Channel {0}", // 0: channel number (1-15) ; eg Channel 12
"base_stations_nickname": "Nickname",
/* actions */
"base_stations_action_wake_all": "Wake All",
"base_stations_action_sleep_all": "Sleep All",
"base_stations_action_fix_collisions": "Fix Channel Collisions",
"base_stations_btn_wake": "Wake",
"base_stations_btn_standby": "Standby",
"base_stations_btn_sleep": "Sleep",
"base_stations_btn_edit": "Edit",
"base_stations_btn_save": "Save",
"base_station_power_management": "Base Station Power Management",
"base_stations_power_mgmt_enable": "Enable power management *",
"base_stations_power_mgmt_enable_description_standby": "Powered Base Stations go into standby mode when SteamVR is not in use, and wake up again when SteamVR starts.",
"base_stations_power_mgmt_enable_description_sleep": "Powered Base Stations go into sleep mode when SteamVR is not in use, and wake up again when SteamVR starts.",
"base_stations_power_mgmt_on_startup": "Turn on Base Stations on SteamVR startup",
"base_stations_power_mgmt_on_startup_description": "Unpowered Base Stations wake up when SteamVR starts.",
"base_stations_power_mgmt_on_shutdown": "Turn off Base Stations on SteamVR shutdown",
"base_stations_power_mgmt_on_shutdown_description": "Powered Base Stations are powered down when SteamVR is not in use.",
"base_stations_power_mgmt_standby": "Standby",
"base_stations_power_mgmt_standby_description": "Base Stations will power off only their lasers for a faster wake up when SteamVR starts. This is unavailable on Base Station 1.0 models and some Base Station 2.0 models, and sleep will be used in these cases.",
"base_stations_power_mgmt_sleep": "Sleep",
"base_stations_power_mgmt_sleep_description": "Base Stations will power off their lasers and motors.",
"base_stations_power_mgmt_warning": "* NOT RECOMMENDED for shared tracking spaces with multiple SteamVR users.",
/* settings */
"settings_title": "Settings",
"settings_advanced": "Advanced settings",
"settings_advanced_description": "Shows extra settings and fine-tuning controls for power users.",
"settings_calibrate_motion_vectors": "Calibrate motion vectors",
"settings_calibrate_motion_vectors_description": "Improves movement prediction for more responsive tracking. May affect physics-heavy games.",
"settings_autofix_playspace_jumps": "Auto-correct playspace jumps",
"settings_autofix_playspace_jumps_description": "Attempts to automatically detect and correct sudden jumps in playspace tracking.",
"settings_enforce_minimum_rotation_variance": "Enforce minimum rotation variance",
"settings_enforce_minimum_rotation_variance_description": "Automatically rejects samples that are rotationally similar to improve calibration quality.",
"settings_locale": "Language",
"settings_reset": "Reset all settings",
"settings_reset_description": "Resets all settings to default. This is permanent and cannot be undone.",
"settings_reset_confirm": "This change is permanent and cannot be undone. Are you sure you want to continue?",
"settings_reset_yes": "Yes",
"settings_reset_no": "No",
/* about */
"about_title": "About",
"about_description": "Space Calibrator is a free and open-source tool to allow you to use multiple vendors' devices together with SteamVR.",
"about_view_source_info": "You may view the source code on the GitHub repository.",
"about_contributors_title": "Contributors",
"about_contributors_description": "Space Calibrator is made possible with the help of the following people:",
"about_translators_title": "Translators",
"about_translators_description": "Space Calibrator has been translated by the following people:",
"about_licenses_title": "Licenses",
"about_licenses_description": "This program is licensed under the MIT license. Other dependencies are licensed under other licenses, please view the licenses below for more information.",
"about_link_github": "GitHub",
"about_link_discord": "Discord Server",
"about_link_logs_dir": "Logs folder",
/* self-troubleshooting */
/* found another driver loaded in SteamVR -> legacy ipc connection successful */
"troubleshoot_diagnosis_legacy_ipc_found_title": "Another instance of Space Calibrator is already present",
"troubleshoot_diagnosis_legacy_ipc_found_description": "A conflicting version of Space Calibrator is currently running. You will have to uninstall it to be able to use this application. Would you like to automatically remove it?",
/* found legacy overlay key registered (non steam) */
"troubleshoot_diagnosis_legacy_overlay_found_title": "Another instance of Space Calibrator is already present",
"troubleshoot_diagnosis_legacy_overlay_found_description": "Another version of Space Calibrator is currently installed. Would you like to remove it?",
"troubleshoot_action_yes": "Yes",
"troubleshoot_action_no": "No",
"troubleshoot_action_quit": "Quit",
/* tutorial */
"learn_title": "Learn",
"learn_card_standard_calibration": "Standard Calibration",
"learn_card_standard_calibration_description": "Learn how to calibrate a pair of devices.",
"learn_card_continuous_calibration": "Continuous Calibration",
"learn_card_continuous_calibration_description": "Calibrate frequently to counteract drift mid-session.",
"learn_card_base_station_management": "Base Station Management",
"learn_card_base_station_management_description": "Wirelessly control your SteamVR Base Stations.",
"learn_card_ui_tour": "UI Tour",
"learn_card_ui_tour_description": "An overview of the Space Calibrator interface.",
"learn_action_back": "Back",
/* these are kinda like a messed up wiki because its inlined into the app like this, oh well lol */
"learn_page_standard_desc": "Standard Calibration aligns two tracking systems together so that your VR devices work together in the same physical playspace.",
"learn_page_standard_select_devices_title": "Select your devices",
"learn_page_standard_select_devices_desc": "Before starting, the app needs to know which two devices you want to align.",
"learn_page_standard_ref_device": "Reference Device: This device is what you're calibrating to, usually a controller.",
"learn_page_standard_target_device": "Target Device: The unaligned device you're calibrating with, typically a tracker or similar.",
"learn_page_standard_perform_title": "Perform the Calibration",
"learn_page_standard_perform_step1": "Hold both devices together firmly in one hand. Hold them as tight as you can so that they do not move during the calibration process.",
"learn_page_standard_perform_step2": "Click on the Standard Calibration button to begin calibrating.",
"learn_page_standard_perform_step3": "Move around your room while moving your devices in a figure eight, as illustrated below.",
"learn_page_standard_speeds_title": "Calibration Speeds",
"learn_page_standard_speeds_desc": "You can optionally alter the calibration speed to affect how much movement is required. More movement tends to improve calibration quality.",
"learn_page_standard_speed_fast": "Fast: Takes less time to calibrate, but may result in poorer quality calibrations.",
"learn_page_standard_speed_slow": "Slow / Very Slow: Takes longer to calibrate, and tends to yield more accurate calibrations.",
"learn_page_standard_completion_note": "Once the progress bar fills up, your devices should instantly line up in VR. If this doesn't happen, try calibrating again.",
"learn_page_standard_drift_title": "A Note on Drift",
"learn_page_standard_drift_desc": "Over time, your devices may drift, breaking the calibration. If this happens, you will have to re-calibrate to correct it. If you have to do this frequently, you should consider using Continuous Calibration.",
"learn_page_continuous_desc1": "If you notice your devices slowly shifting, rotating, or drifting out of place while playing, Continuous Calibration is a solution to these issues.",
"learn_page_continuous_desc2": "Instead of forcing you to stop and manually calibrate every so often, Continuous Calibration automatically keeps your tracking synced in the background while you play.",
"learn_page_continuous_note_title": "Note",
"learn_page_continuous_note_desc": "A dedicated tracker mounted on your headset IS required to use this feature.",
"learn_page_continuous_desc3": "Continuous Calibration attempts to re-calibrate your devices frequently to eliminate any and all drift. It tends to make calibration something you never think about once it's set up.",
"learn_page_continuous_setup_instructions": "To set up Continuous Calibration, first select your devices as explained in the Standard Calibration page. You will want your headset selected as the reference device, and the calibration tracker as the target device. Then click the Continuous Calibration button to start calibrating. It may take a bit of time until the initial calibration is completed, so move around to let the app learn about your tracking!",
"learn_page_continuous_should_use_title": "Should you use Continuous Calibration?",
"learn_page_continuous_should_use_standard": "Use Standard Calibration if you do not have an extra tracker to mount on your headset, do not spend long sessions in VR, or don't mind re-calibrating every so often whenever drift happens.",
"learn_page_continuous_should_use_continuous": "Use Continuous Calibration if you want to practically never think about drift again. This is recommended if you spend a lot of time in VR or notice significant amounts of drift over time.",
"learn_page_continuous_physical_setup_title": "Physical setup (mounting the tracker onto your headset)",
"learn_page_continuous_physical_setup_desc1": "To use Continuous Calibration you will need a dedicated tracker attached onto your VR headset. There are various mounts depending on which specific VR headset and tracker you are using. You can typically find 3D models on the internet you can print to get started with, or you can ask around in the community to see what other people did.",
"learn_page_continuous_physical_setup_desc2": "Once mounted, the tracker should be securely attached to the headset. It should not wobble, swivel, or shake. The better the mounting the more consistent calibrations should be.",
"learn_page_basestation_desc": "Space Calibrator allows you to control SteamVR Base Stations if you have a compatible BLE Bluetooth adapter available.",
"learn_page_basestation_1_title": "Base Station 1.0 Models",
"learn_page_basestation_1_desc": "You can only turn these on or off. Setting channels wirelessly is unsupported, and you will have to press the physical button on the back of the Base Station to alternate between channels.",
"learn_page_basestation_2_title": "Base Station 2.0 Models",
"learn_page_basestation_2_desc1": "Manufactured by both Valve and HTC, these have better support for wireless management than 1.0 Base Stations. With these you're able to turn them on and off, and also set their channel. 2.0 Base Stations must each be on a unique channel, otherwise they will not work. Space Calibrator will offer to help you fix this if it happens.",
"learn_page_basestation_2_desc2": "To change the channel of a 2.0 Base Station, navigate to the Base Station Management tab, select the edit button for the Base Station you wish to control, and click on the desired channel (marked in green).",
"learn_page_basestation_auto_power_title": "Automatic Power Management",
"learn_page_basestation_auto_power_desc": "You can go to the Settings tab to enable automatic Base Station Management, which will turn on your Base Stations when Space Calibrator is started, and turn them off when you close SteamVR (with Space Calibrator running). Note that manually closing Space Calibrator before closing SteamVR will NOT turn off any Base Stations automatically.",
"learn_page_ui_tour_desc": "This interface is divided into several tabs, each containing various things.",
"learn_page_ui_tour_calibration_heading": "Calibration Tab",
"learn_page_ui_tour_cal_desc1": "The Calibration tab contains controls relating to calibrations. You control your calibrations here.",
"learn_page_ui_tour_cal_desc2": "At the top, you will see a card showing you whether you have a calibration active, and if so, which type.",
"learn_page_ui_tour_cal_desc3": "Below that, you can select what devices to calibrate with. First, you should select the tracking systems you wish to calibrate. Then, on the left pane you should select what you're going to calibrate with. And on the right pane you should select what you're going to calibrate.",
"learn_page_ui_tour_cal_desc4": "Below that are the buttons to perform a calibration. If you are unsure which calibration you should do, please read the pages talking about Standard and Continuous Calibration to learn more.",
"learn_page_ui_tour_cal_desc5": "At the bottom of the Calibration tab you will find several options to control the calibration. A number of these settings require advanced settings to be visible.",
"learn_page_ui_tour_base_stations_heading": "Base Station Management Tab",
"learn_page_ui_tour_bs_desc": "Here you can control any Base Stations in your room with a supported BLE Bluetooth adapter. You should typically not have to touch this beyond initial setup.",
"learn_page_ui_tour_settings_heading": "Settings Tab",
"learn_page_ui_tour_settings_desc1": "Here you may set some global settings for the program. These include advanced settings, Base Station Management, and UI language.",
"learn_page_ui_tour_settings_desc2": "Some features in this app require Advanced Settings to be enabled. You should only enable this if you know what you're doing.",
/* Names of the various languages one may select. Shown in settings */
"languages_en_GB": "English (UK)",
"languages_en_US": "English (US)",
"languages_fr": "French",
"languages_it": "Italian",
"languages_de": "German",
"languages_nl": "Dutch",
"languages_es_ES": "Spanish (Spain)",
"languages_es_US": "Spanish (Latin America)",
"languages_da": "Danish",
"languages_sv": "Swedish",
"languages_fi": "Finnish",
"languages_no": "Norwegian",
"languages_bg": "Bulgarian",
"languages_pl": "Polish",
"languages_cs": "Czech",
"languages_el": "Greek",
"languages_hu": "Hungarian",
"languages_pt_PT": "Portuguese (Portugal)",
"languages_pt_BR": "Portuguese (Brazil)",
"languages_ro": "Romanian",
"languages_ru": "Russian",
"languages_tr": "Turkish",
"languages_uk": "Ukrainian",
"languages_zh_HANS": "Chinese (Simplified)",
"languages_zh_HANT": "Chinese (Traditional)",
"languages_ja": "Japanese",
"languages_ko": "Korean",
"languages_th": "Thai",
"languages_vi": "Vietnamese",
"languages_system": "System Language"
}
+278
View File
@@ -0,0 +1,278 @@
{
"app_title": "{0} {1}", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"app_title_vr": "{0} {1} - cierra la overlay de VR para usar el ratón", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"tracking_system_no_systems": "No hay dispositivos rastreados presentes. Por favor enciende un dispositivo para continuar.", /* Should never be hit but you never know */
"error_unknown_catastrophic": "¡Algo ha ido catastróficamente mal! Por favor reporta esto al desarrollador en GitHub o Discusiones de Steam para poder solucionarlo.",
"ipc_unavailable": "No se pudo conectar al driver de SteamVR de Space Calibrator. Por favor verifica la integridad de los archivos en Propiedades -> Archivos instalados, o reinstala la app.",
"steamvr_unavailable": "No se pudo conectar a SteamVR.",
"vr_troubleshooting_hmd_not_found": "Por favor conecta tus gafas de VR y abre SteamVR primero.",
"vr_troubleshooting_connect_steamlink": "Por favor abre SteamLink en tus gafas de VR y conéctate a SteamVR primero.",
"vr_troubleshooting_generic": "Por favor conecta tus gafas de VR y abre SteamVR primero. ({0} {1})", /* 0 : SteamVR init error (eg 108), 1 : SteamVR init error string (eg Headset not connected) - this string is provided by Steam. */
"calibration_title": "Calibración",
"calibration_count_none": "No hay Calibración. Por favor crea una nueva calibración para empezar.", // Shown when there are no calibrations active
"calibration_new": "Nueva Calibración",
"calibration_delete": "Borrar Calibración",
"calibration_next": "Siguiente Calibración",
"calibration_previous": "Anterior Calibración",
"calibration_status_none": "No hay una Calibración activa",
"calibration_status_none_description": "Por favor selecciona una pareja de dispositivos y calibra para empezar.",
"calibration_status_edit": "Editando calibración",
"calibration_status_edit_description": "Modificando la calibración actual.",
"calibration_status_continuous": "Calibración Continua",
"calibration_status_continuous_description": "Tienes una calibración activa. Utiliza un tracker para recalibrar cada pocos segundos automáticamente.",
"calibration_status_standard": "Calibración Estándar",
"calibration_status_standard_description": "Tienes una calibración activa. Puede ser necesario recalibrar si observas desviación (drift).",
// space here refers to a tracking space / tracking system.
"reference_device": "Dispositivo de referencia",
"reference_device_description": "Este dispositivo es con lo que estás calibrando, usualmente tus gafas de VR o un mando.",
"target_device": "Dispositivo de destino",
"target_device_description": "Este dispositivo se utilizará para alinear tus trackers o mandos.",
"select_reference_device": "Selecciona dispositivo de referencia",
"select_target_device": "Selecciona dispositivo de destino",
"identify_selected_devices": "Identificar dispositivos seleccionados (parpadea LED o vibra)",
"identify_auto_detect_devices": "Auto detectar dispositivos",
"calibration_error_reference_hmd_missing": "No se pudo encontrar el dispositivo de destino del sistema de rastreamiento \"{0}\". Puede ser necesario recalibrar..", /* 0 : tracking system name, eg SteamVR Tracking*/
"calibration_warning_stage_tracking_disabled": "¡Stage tracking esta deshabilitado! Para una experiencia mejor, abre el menú de Virtual Desktop en tu Quest, ves a la pestaña Streaming y habilita \"Center to play area (Stage Tracking)\".",
"calibration_warning_stage_tracking_disabled_ignore_button": "No mostrar otra vez",
"calibration_action_start": "Calibración Estándar",
"calibration_action_clear": "Borrar Calibración",
"calibration_action_continuous": "Calibración Continua",
"calibration_action_edit": "Editar Calibración",
"save_calibration_profile": "Guardar Perfil",
"calibration_progress": "Progreso de Calibración",
"calibration_info_move_around_insufficient_samples": "Coge tus dispositivos seleccionados juntos, entonces camina por tu espacio de juego mientras los giras lentamente formando ochos.",
"calibration_speed": "Velocidad de Calibración",
"calibration_speed_description": "Cuanto se tarda en calibrar. Las calibraciones más lentas suelen ser más exactas.",
"calibration_speed_fast": "Rápido",
"calibration_speed_slow": "Lento",
"calibration_speed_very_slow": "Muy Lento",
/* Editing calibrations */
"edit_calibration_rotation": "Rotación",
"edit_calibration_position": "Posición (m)",
"edit_calibration_yaw": "Yaw",
"edit_calibration_pitch": "Pitch",
"edit_calibration_roll": "Roll",
"edit_calibration_x": "X",
"edit_calibration_y": "Y",
"edit_calibration_z": "Z",
"edit_calibration_scale": "Escala (m)",
"continuous_hide_tracker": "Esconder el tracker de referencia en las apps",
"continuous_relative_calibration": "Habilitar calibración relativa",
"continuous_relative_calibration_description": "Hace la calibración relativa al dispositivo de destino (usualmente tus gafas de VR), actualizando cada frame sin necesitar una recalibración. Esto REQUIERE Calibración Continua",
/* HMD names for Virtual Desktop */
/* Virtual desktop lies that it's a Quest 2/3 on most headsets, so we have to substitute the real HMD name ourselves. you generally should
NOT have to change these! but some headsets have different names in certain regions (eg PFD MR is YVR MR in China) */
"virtual_desktop_hmd_none": "Ninguno", /* should never appear but you never know*/
"virtual_desktop_hmd_gearvr": "Samsung Gear VR",
"virtual_desktop_hmd_oculus_go": "Oculus Go",
"virtual_desktop_hmd_oculus_quest_1": "Oculus Quest",
"virtual_desktop_hmd_oculus_quest_2": "Oculus Quest 2",
"virtual_desktop_hmd_meta_quest_pro": "Meta Quest Pro",
"virtual_desktop_hmd_pico_4": "Pico 4",
"virtual_desktop_hmd_meta_quest_3": "Meta Quest 3",
"virtual_desktop_hmd_meta_quest_3s": "Meta Quest 3S",
"virtual_desktop_hmd_pico_4_ultra": "Pico 4 Ultra",
"virtual_desktop_hmd_samsung_galaxy_xr": "Samsung Galaxy XR",
"virtual_desktop_hmd_play_for_dream_mr": "Play For Dream MR",
/* vertical tabs on the left */
"tab_page_calibration": "Calibración",
"tab_page_graphs": "Gráficos",
"tab_page_base_station_management": "Estaciones Base",
"tab_page_settings": "Ajustes",
"tab_page_debug": "Depuración",
"tab_page_learn": "Aprende",
"tab_page_about": "Sobre",
/* graphs */
"graphs_title": "Gráficos",
/* base stations */
"base_stations_title": "Gestión de Estaciones Base",
"base_stations_no_bluetooth": "No se pudo encontrar un adaptador Bluetooth válido. Por favor conecta un dispositivo Bluetooth capaz de BLE para poder utilizar Gestión de Estaciones Base.",
"base_stations_none_found": "No se han detectado estaciones base. Si tienes algunas, asegúrate de que están enchufadas.",
"base_stations_header_info": "{0}", // 0: station ID (LHB XXXXXXXX) / nickname
"base_stations_warning_collision": "¡Colisión de canales detectada! Esto causará problemas de rastreamiento. Cada una de tus estaciones base debería usar un canal distinto.",
"base_stations_state_sleep": "Durmiendo",
"base_stations_state_standby": "Standby",
"base_stations_state_active": "Activo",
"base_stations_state_unknown": "Desconocido",
"base_stations_state_faulty": "Defectuoso",
"base_stations_channel_select_label": "Selecciona un canal de estaciones base:",
"base_stations_active_channel": "Canal {0}", // 0: channel number (1-15) ; eg Channel 12
"base_stations_nickname": "Apodo",
/* actions */
"base_stations_action_wake_all": "Despertar Todos",
"base_stations_action_sleep_all": "Dormir Todos",
"base_stations_action_fix_collisions": "Arreglar Colisiones de Canales",
"base_stations_btn_wake": "Despertar",
"base_stations_btn_standby": "Standby",
"base_stations_btn_sleep": "Dormir",
"base_stations_btn_edit": "Editar",
"base_stations_btn_save": "Guardar",
"base_station_power_management": "Gestión de Energía de Estaciones Base",
"base_stations_power_mgmt_enable": "Habilitar gestión de energía *",
"base_stations_power_mgmt_enable_description_standby": "Las estaciones base encendidas entran en modo standby cuando SteamVR no está en uso, y se despiertan cuando SteamVR se abre.",
"base_stations_power_mgmt_enable_description_sleep": "Las estaciones base encendidas se duermen cuando SteamVR no está en uso, y se despiertan cuando SteamVR se abre.",
"base_stations_power_mgmt_on_startup": "Encender estaciones base con SteamVR",
"base_stations_power_mgmt_on_startup_description": "Las estaciones base apagadas se despiertan cuando SteamVR se abre.",
"base_stations_power_mgmt_on_shutdown": "Apagar estaciones base con SteamVR",
"base_stations_power_mgmt_on_shutdown_description": "Las estaciones base encendidas se apagan cuando SteamVR no está en uso.",
"base_stations_power_mgmt_standby": "Standby",
"base_stations_power_mgmt_standby_description": "Las estaciones base apagarán únicamente sus lásers para encenderse más rápido cuando SteamVR se abre. Esto no está disponible en estaciones base 1.0 y algunos modelos de estaciones base 2.0, en esos casos, se dormirá.",
"base_stations_power_mgmt_sleep": "Dormir",
"base_stations_power_mgmt_sleep_description": "Las estaciones base apagarán sus lásers y motores.",
"base_stations_power_mgmt_warning": "* NO RECOMENDADO para espacios de juego compartidos por múltiples usuarios de SteamVR.",
/* settings */
"settings_title": "Ajustes",
"settings_advanced": "Ajustes avanzados",
"settings_advanced_description": "Muestra ajustes extra y controles más finos para power users.",
"settings_calibrate_motion_vectors": "Calibrar vectores de movimiento",
"settings_calibrate_motion_vectors_description": "Mejora la predicción de movimiento para rastreamiento más fluido. Puede afectar a juegos con muchas físicas.",
"settings_autofix_playspace_jumps": "Auto corregir saltos de espacio de juego",
"settings_autofix_playspace_jumps_description": "Intenta automáticamente detectar y corregir saltos repentinos en el rastreo del espacio de juego.",
"settings_enforce_minimum_rotation_variance": "Forzar variación mínima de rotación",
"settings_enforce_minimum_rotation_variance_description": "Automáticamente descarta muestras que son similares en rotación para mejorar la calidad de calibración.",
"settings_locale": "Idioma",
"settings_reset": "Reiniciar todos los ajustes",
"settings_reset_description": "Reinicia todos los ajustes a su valor predeterminado. Esto es permanente y no se puede revertir.",
"settings_reset_confirm": "Esta acción es permanente y no se puede revertir. ¿Estás seguro de que quieres continuar?",
"settings_reset_yes": "Sí",
"settings_reset_no": "No",
/* about */
"about_title": "Sobre",
"about_description": "Space Calibrator es una herramienta gratuita y de código abierto que te permite utilizar dispositivos de diferentes fabricantes juntos con SteamVR.",
"about_view_source_info": "Puedes ver el código fuente en el repositorio de GitHub.",
"about_contributors_title": "Contribuidores",
"about_contributors_description": "Space Calibrator es posible gracias a la ayuda de las siguientes personas:",
"about_translators_title": "Tranductores",
"about_translators_description": "Space Calibrator se ha traducido por las siguientes personas:",
"about_licenses_title": "Licencias",
"about_licenses_description": "Este programa está licenciado bajo la licencia MIT. Otras dependencias están licenciadas bajo otras licencias, por favor mira las licencias abajo para más información.",
"about_link_github": "GitHub",
"about_link_discord": "Servidor de Discord",
"about_link_logs_dir": "Carpeta de logs",
/* self-troubleshooting */
/* found another driver loaded in SteamVR -> legacy ipc connection successful */
"troubleshoot_diagnosis_legacy_ipc_found_title": "Otra instancia de Space Calibrator ya está presente",
"troubleshoot_diagnosis_legacy_ipc_found_description": "Una versión incompatible de Space Calibrator está abierta actualmente. Has de desinstalarla para usar esta aplicación. ¿Te gustaría eliminarla automáticamente?",
/* found legacy overlay key registered (non steam) */
"troubleshoot_diagnosis_legacy_overlay_found_title": "Otra instancia de Space Calibrator ya está presente",
"troubleshoot_diagnosis_legacy_overlay_found_description": "Otra versión de Space Calibrator está instalada actualmente. ¿Te gustaría eliminarla?",
"troubleshoot_action_yes": "Sí",
"troubleshoot_action_no": "No",
"troubleshoot_action_quit": "Cerrar",
/* tutorial */
"learn_title": "Aprende",
"learn_card_standard_calibration": "Calibración Estándar",
"learn_card_standard_calibration_description": "Aprende a calibrar una pareja de dispositivos.",
"learn_card_continuous_calibration": "Calibración Continua",
"learn_card_continuous_calibration_description": "Calibra frecuentemente para evitar desviación (drift) durante el juego.",
"learn_card_base_station_management": "Gestión de Estaciones Base",
"learn_card_base_station_management_description": "Controla tus estaciones base de SteamVR de forma inalámbrica.",
"learn_card_ui_tour": "Tour de interfaz de usuario",
"learn_card_ui_tour_description": "Una vista rápida de la interfaz de Space Calibrator.",
"learn_action_back": "Atrás",
/* these are kinda like a messed up wiki because its inlined into the app like this, oh well lol */
"learn_page_standard_desc": "La Calibración Estándar alinea dos sistemas de rastreamiento juntos para que tus dispositivos de realidad virtual funcionen juntos en el mismo espacio de juego físico.",
"learn_page_standard_select_devices_title": "Selecciona tus dispositivos",
"learn_page_standard_select_devices_desc": "Antes de empezar, la app necesita saber los dos dispositivos que quieres alinear.",
"learn_page_standard_ref_device": "Dispositivo de referencia: Este dispositivo es lo que estás calibrando, usualmente un mando.",
"learn_page_standard_target_device": "Dispositivo de destino: El dispositivo no alineado con el que estás calibrando, usualmente un tracker o similar.",
"learn_page_standard_perform_title": "Haz la Calibración",
"learn_page_standard_perform_step1": "Coge ambos dispositivos firmemente en una mano. Cógelos lo más fuerte que puedas para que no se muevan durante el proceso de calibración.",
"learn_page_standard_perform_step2": "Haz clic en el botón de Calibración Estándar para empezar.",
"learn_page_standard_perform_step3": "Muévete por tu habitación mientas mueves los dispositivos en ochos, como se muestra abajo.",
"learn_page_standard_speeds_title": "Velocidades de Calibración",
"learn_page_standard_speeds_desc": "Puedes, opcionalmente, alterar la velocidad de calibración para afectar cuánto movimiento es necesario. Más movimiento tiende a mejorar la calidad de calibración.",
"learn_page_standard_speed_fast": "Rápido: Tarda menos tiempo en calibrarse, pero puede resultar en una calibración de menor calidad.",
"learn_page_standard_speed_slow": "Lento / Muy Lento: Tarda más tiempo en calibrarse y tiende a resultar en calibraciones más exactas.",
"learn_page_standard_completion_note": "Una vez la barra de progreso se llene, tus dispositivos se deberían alinear instantáneamente en realidad virtual. Si esto no ocurre, prueba a calibrar otra vez.",
"learn_page_standard_drift_title": "Una Nota sobre la desviación (drift)",
"learn_page_standard_drift_desc": "Con el paso del tiempo, tus dispositivos pueden desviarse, rompiendo la calibración. Si esto pasa, deberás recalibrar para corregirlo. Si has de hacer esto frecuentemente, deberías considerar usar Calibración Continua.",
"learn_page_continuous_desc1": "Si te das cuenta de que tus dispositivos se están moviendo lentamete, girando, o desviándose fuera de lugar mientras juegas, la Calibración Continua es la solución a esos problemas.",
"learn_page_continuous_desc2": "En lugar de forzarte a parar y manualmente calibrar cada cierto tiempo, la Calibración Continua automáticamente mantiene tu rastreamiento sincronizado en segundo plano mientras juegas.",
"learn_page_continuous_note_title": "Nota",
"learn_page_continuous_note_desc": "Un tracker dedicado montado en tus gafas de VR ES necesario para utilizar esta función.",
"learn_page_continuous_desc3": "La Calibración Continua intenta recalibrar tus dispositivos frecuentemente para eliminar toda la desviación (drift). Tiende a hacer la calibración algo sobre lo que no piensas nunca después de configurarla.",
"learn_page_continuous_setup_instructions": "Para configurar la Calibración Continua, primero selecciona tus dispositivos tal y como se explica en la página de la Calibración Estándar. Tus gafas de VR deberán estar seleccionadas como el dispositivo de referencia, y el tracker de calibración como el dispositivo de destino. Entonces, haz clic en el botón de Calibración Continua para empezar la calibración. Puede tardar un poco de tiempo hasta que la calibración inicial se haya completado, ¡así que muévete para permitir que la app aprenda sobre tu rastreamiento!",
"learn_page_continuous_should_use_title": "¿Debería utilizar Calibración Continua?",
"learn_page_continuous_should_use_standard": "Utiliza Calibración Estándar si no tienes un tracker extra para montar en tus gafas de VR, no pasas mucho tiempo en realidad virtual, o no te molesta recalibrar cada cierto tiempo cuando observas desviación (drift).",
"learn_page_continuous_should_use_continuous": "Utiliza Calibración Continua si deseas prácticamente nunca pensar sobre la desviación. Esto está recomendado si pasas mucho tiempo en realidad virtual o observas cantidades significantes de desviación (drift) al paso del tiempo.",
"learn_page_continuous_physical_setup_title": "Configuración física (montar el tracker en tus gafas de realidad virtual)",
"learn_page_continuous_physical_setup_desc1": "Para utilizar Calibración Continua necesitarás un tracker dedicado agarrado a tus gafas de VR. Usualmente puedes encontrar modelos 3D en el internet que puedes imprimir para empezar, o puedes preguntar alrededor en la comunidad para ver lo que otras personas han hecho.",
"learn_page_continuous_physical_setup_desc2": "Una vez montado, el tracker debería estar enganchado de forma segura a las gafas. No debería tambalearse, girar, o sacudirse. Cuanto mejor montado, la calibración será más consistente.",
"learn_page_basestation_desc": "Space Calibrator te permite controlar tus estaciones base de SteamVR si tienes un adaptador Bluetooth BLE compatible disponible.",
"learn_page_basestation_1_title": "Modelos de Estación Base 1.0",
"learn_page_basestation_1_desc": "Únicamente las puedes encender y apagar. Cambiar canales inalámbricamente no está soportado, has de utilizar el botón físico en la parte trasera de la estación base para alternar entre canales.",
"learn_page_basestation_2_title": "Modelos de Estación Base 2.0",
"learn_page_basestation_2_desc1": "Fabricados por Valve y HTC, estas tienen mejor soporte para gestión inalámbrica que las estaciones base 1.0. Con estas puedes encenderlas y apagarlas, y también establecer su canal. Las estaciones base 2.0 han de estar cada una en un canal único, no funcionarán en caso contrario. Space Calibrator ofrecerá ayuda para arreglar esto si ocurre.",
"learn_page_basestation_2_desc2": "Para cambiar el canal de una estación base 2.0, navega a la pestaña de Gestión de Estaciones Base, selecciona el botón de editar para la estación base que quieres controlar, y haz clic en el canal deseado (marcado en verde).",
"learn_page_basestation_auto_power_title": "Gestión de Energía automática",
"learn_page_basestation_auto_power_desc": "Puedes ir a la pestaña de Ajustes para activar Gestión automática de Estaciones Base, lo que encenderá tus estaciones base cuando Space Calibrator se abra, y las apagará cuando cierres SteamVR (con Space Calibrator abierto). Nota que cerrar Space Calibrator manualmente antes de cerrar SteamVR NO apagará tus estaciones base automáticamente.",
"learn_page_ui_tour_desc": "Esta interfaz está dividida en diversas pestañas, cada una conteniendo varias cosas.",
"learn_page_ui_tour_calibration_heading": "Pestaña de Calibración",
"learn_page_ui_tour_cal_desc1": "La pestaña de Calibración contiene controles relacionados con las calibraciones. Controlas tu calibraciones aquí.",
"learn_page_ui_tour_cal_desc2": "Arriba verás una tarjeta mostrando si tienes una calibración activa y su tipo.",
"learn_page_ui_tour_cal_desc3": "Debajo de eso puedes seleccionar que dispositivos deseas utilizar para calibrar. Primero deberás seleccionar los sistemas de rastreamiento que quieres calibrar. Y en el panel de la derecha deberás seleccionar que vas a calibrar.",
"learn_page_ui_tour_cal_desc4": "Debajo de eso están los botones para hacer una calibración. Si no estás seguro de lo que deberías hacer, por favor lee las páginas sobre la Calibración Estándar y la Calibración Continua para aprender más.",
"learn_page_ui_tour_cal_desc5": "Abajo de la pestaña de Calibración encontrarás diversas opciones para controlar la calibración. Algunos de estos ajustes sólo se hacen visibles si los ajustes avanzados están habilitados.",
"learn_page_ui_tour_base_stations_heading": "Pestaña de Gestión de Estaciones Base",
"learn_page_ui_tour_bs_desc": "Aquí puedes controlar cualquier estación base en tu habitación con un adaptador Bluetooth BLE soportado. Usualmente no tienes que tocarlo despueś de la configuración inicial.",
"learn_page_ui_tour_settings_heading": "Pestaña de Ajustes",
"learn_page_ui_tour_settings_desc1": "Aquí puedes establecer algunos ajustes globales para el programa. Éstos incluyen ajustes avanzados, Gestión de Estaciones Base, y el idioma de la interfaz.",
"learn_page_ui_tour_settings_desc2": "Algunas funciones de esta app requieren que los Ajustes Avanzados estén activados. Sólo deberías activar esto si sabes lo que estás haciendo.",
/* Names of the various languages one may select. Shown in settings */
"languages_en_GB": "Inglés (Reino Unido)",
"languages_en_US": "Inglés (Estados Unidos)",
"languages_fr": "Francés",
"languages_it": "Italiano",
"languages_de": "Alemán",
"languages_nl": "Neerlandés",
"languages_es_ES": "Español (España)",
"languages_es_US": "Español (Latinomérica)",
"languages_da": "Danés",
"languages_sv": "Sueco",
"languages_fi": "Finlandés",
"languages_no": "Noruego",
"languages_bg": "Búlgaro",
"languages_pl": "Polaco",
"languages_cs": "Checo",
"languages_el": "Griego",
"languages_hu": "Húngaro",
"languages_pt_PT": "Portugués (Portugal)",
"languages_pt_BR": "Portugués (Brazil)",
"languages_ro": "Rumano",
"languages_ru": "Ruso",
"languages_tr": "Turco",
"languages_uk": "Ucraniano",
"languages_zh_HANS": "Chino (Simplificado)",
"languages_zh_HANT": "Chino (Tradicional)",
"languages_ja": "Japonés",
"languages_ko": "Coreano",
"languages_th": "Tailandés",
"languages_vi": "Vietnamita",
"languages_system": "Idioma del Sistema"
}
+280
View File
@@ -0,0 +1,280 @@
{
"app_title": "{0} {1}", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"app_title_vr": "{0} {1} - sulje VR sovelluksen näkymä käyttääksesi hiirtä", /* 0 : Space Calibrator ; 1 : version (eg 2.0.0) */
"tracking_system_no_systems": "Seurattuja laitteita ei havaittu. Käynnistä laite jatkaaksesi.", /* Should never be hit but you never know */
"error_unknown_catastrophic": "Jokin meni pahasti vikaan! Kirjoita raportti kehittäjälle GitHubissa tai Steam keskustelussa, jotta tämä voidaan korjata.",
"ipc_unavailable": "Yhdistäminen Space Calibrator SteamVR ajuriin epäonnistui. Tarkista sovellustiedostojen eheys Steamissa -> Ominaisuudet -> Tiedostot, tai asenna sovellus uudelleen.",
"steamvr_unavailable": "Yhdistäminen SteamVR:ään epäonnistui.",
"vr_troubleshooting_hmd_not_found": "Yhdistä ensin VR-lasisi ja käynnistä SteamVR.",
"vr_troubleshooting_connect_steamlink": "Avaa ensin SteamLink VR-laseissasi ja yhdistä SteamVR:ään",
"vr_troubleshooting_generic": "Yhdistä ensin VR-lasisi ja käynnistä SteamVR. ({0} {1})", /* 0 : SteamVR init error (eg 108), 1 : SteamVR init error string (eg Headset not connected) - this string is provided by Steam. */
"ipc_unvailable_auto_fix": "Yritä korjata",
"ipc_unvailable_auto_fix_note_reboot": "Tietokone on käynnistettävä uudelleen, jotta muutokset toteutuvat.",
"calibration_title": "Kalibrointi",
"calibration_count_none": "Ei kalibrointia. Luo uusi kalibrointi aloittaaksesi.", // Shown when there are no calibrations active
"calibration_new": "Uusi kalibrointi",
"calibration_delete": "Poista kalibrointi",
"calibration_next": "Seuraava kalibrointi",
"calibration_previous": "Edellinen kalibrointi",
"calibration_status_none": "Ei aktiivista kalibrointia.",
"calibration_status_none_description": "Valitse laitepari ja kalibroi ne.",
"calibration_status_edit": "Kalibroinnin muokkaus.",
"calibration_status_edit_description": "Muokataan nykyistä kalibrointia.",
"calibration_status_continuous": "Jatkuva kalibrointi",
"calibration_status_continuous_description": "Jatkuva kalibrointi käyttää toiseen laitteeseen fyysisesti kiinnitettyä seurantalaitetta ja kalibroi ne keskenään automaattisesti muutaman sekunnin välein.",
"calibration_status_standard": "Tavallinen kalibrointi",
"calibration_status_standard_description": "Sinulla on aktiivinen kalibrointi. Uusi kalibrointi voi olla tarpeen, jos laitteet ovat liukuneet erilleen toisistaan.",
// space here refers to a tracking space / tracking system.
"reference_device": "Päälaite",
"reference_device_description": "Kohdelaite kalibroidaan tämän laiteen kanssa. Päälaite on yleensä VR-lasit, tai ohjain.",
"target_device": "Kohdelaite",
"target_device_description": "Tätä laitetta käytetään muiden seurantalaitteiden tai ohjaimien kohdistamiseen.",
"select_reference_device": "Valitse päälaite",
"select_target_device": "Valitse kohdelaite",
"identify_selected_devices": "Tunnista valitut laitteet (vilkuttaa LED-valoa tai värisee)",
"identify_auto_detect_devices": "Tunnista laitteet automaattisesti",
"calibration_error_reference_hmd_missing": "Edellistä kohdelaitetta ei löytynyt. Kalibroi eri kohdelaitteella tai varmista, että edellinen kohdelaite on yhdistetty.", /* 0 : tracking system name, eg SteamVR Tracking*/
"calibration_warning_stage_tracking_disabled": "Stage tracking -asetus on pois käytöstä! Paremman kokemuksen saamiseksi, avaa Virtual Desktopin Streaming-valikko VR-laseissasi ja kytke päälle \"Center to play area (Stage Tracking)\".",
"calibration_warning_stage_tracking_disabled_ignore_button": "Älä näytä tätä uudelleen",
"calibration_action_start": "Tavallinen Kalibrointi",
"calibration_action_clear": "Tyhjennä Kalibrointi",
"calibration_action_continuous": "Jatkuva Kalibrointi",
"calibration_action_edit": "Muokkaa Kalibrointia",
"save_calibration_profile": "Tallenna Profiili",
"calibration_progress": "Kalibroinnin edistyminen",
"calibration_info_move_around_insufficient_samples": "Pidä valitut laitteesi kiinni toisissaan ja kävele ympäri pelialuettasi, tehden laitteilla kahdeksikko muotoa ilmassa.",
"calibration_speed": "Kalibroinnin nopeus",
"calibration_speed_description": "Kuinka kauan kalibrointi kestää. Hitaampi kalibrointi antaa tarkemman tuloksen.",
"calibration_speed_fast": "Nopea",
"calibration_speed_slow": "Hidas",
"calibration_speed_very_slow": "Erittäin hidas",
/* Editing calibrations */
"edit_calibration_rotation": "Kierto",
"edit_calibration_position": "Sijainti (m)",
"edit_calibration_yaw": "Kääntyminen",
"edit_calibration_pitch": "Nyökkääminen",
"edit_calibration_roll": "Kallistuminen",
"edit_calibration_x": "X",
"edit_calibration_y": "Y",
"edit_calibration_z": "Z",
"edit_calibration_scale": "Suhteellinen mittakaava (m)",
"continuous_hide_tracker": "Piilota kohdelaite muilta sovelluksilta.",
"continuous_relative_calibration": "Käytä suhteellista kalibrointia",
"continuous_relative_calibration_description": "Suhteellisessa kalibroinnissa kohdelaite ja siitä riippuvat laitteet seuraavat aina päälaitetta (yleensä VR-laseja) vaatimatta uutta kalibrointia. Tämä VAATII jatkuvan kalibroinnin.",
/* HMD names for Virtual Desktop */
/* Virtual desktop lies that it's a Quest 2/3 on most headsets, so we have to substitute the real HMD name ourselves. you generally should
NOT have to change these! but some headsets have different names in certain regions (eg PFD MR is YVR MR in China) */
"virtual_desktop_hmd_none": "Ei nimeä", /* should never appear but you never know*/
"virtual_desktop_hmd_gearvr": "Samsung Gear VR",
"virtual_desktop_hmd_oculus_go": "Oculus Go",
"virtual_desktop_hmd_oculus_quest_1": "Oculus Quest",
"virtual_desktop_hmd_oculus_quest_2": "Oculus Quest 2",
"virtual_desktop_hmd_meta_quest_pro": "Meta Quest Pro",
"virtual_desktop_hmd_pico_4": "Pico 4",
"virtual_desktop_hmd_meta_quest_3": "Meta Quest 3",
"virtual_desktop_hmd_meta_quest_3s": "Meta Quest 3S",
"virtual_desktop_hmd_pico_4_ultra": "Pico 4 Ultra",
"virtual_desktop_hmd_samsung_galaxy_xr": "Samsung Galaxy XR",
"virtual_desktop_hmd_play_for_dream_mr": "Play For Dream MR",
/* vertical tabs on the left */
"tab_page_calibration": "Kalibrointi",
"tab_page_graphs": "Kuvaajat",
"tab_page_base_station_management": "Tukiasemat",
"tab_page_settings": "Asetukset",
"tab_page_debug": "Virheenjäljitys",
"tab_page_learn": "Käyttöohje",
"tab_page_about": "Tietoja",
/* graphs */
"graphs_title": "Kuvaajat",
/* base stations */
"base_stations_title": "Tukiasemien hallinta",
"base_stations_no_bluetooth": "Sopivaa Bluetooth sovitinta ei löytynyt. Yhdistä BLE-yhteensopiva Bluetooth laite hallinnoidaksesi tukiasemia.",
"base_stations_none_found": "Tukiasemia ei havaittu. Jos tukiasemia on, varmista että ne ovat kytkettynä virtaan.",
"base_stations_header_info": "{0}", // 0: station ID (LHB XXXXXXXX) / nickname
"base_stations_warning_collision": "Kanavaristiriita havaittu! Tämä aiheuttaa seuranta ongelmia. Varmista että mikään tukiasema ei käytä samaa kanavaa toisen tukiaseman kanssa.",
"base_stations_state_sleep": "Lepotilassa",
"base_stations_state_standby": "Valmiustilassa",
"base_stations_state_active": "Aktiivinen",
"base_stations_state_unknown": "Tuntematon tila",
"base_stations_state_faulty": "Viallinen",
"base_stations_channel_select_label": "Valitse tukiaseman kanava:",
"base_stations_active_channel": "Kanava {0}", // 0: channel number (1-15) ; eg Channel 12
"base_stations_nickname": "Lempinimi",
/* actions */
"base_stations_action_wake_all": "Käynnistä kaikki",
"base_stations_action_sleep_all": "Aseta kaikki lepotilaan",
"base_stations_action_fix_collisions": "Korjaa kanavaristiriidat",
"base_stations_btn_wake": "Käynnistä",
"base_stations_btn_standby": "Valmiustilaan",
"base_stations_btn_sleep": "Lepotilaan",
"base_stations_btn_edit": "Muokkaa",
"base_stations_btn_save": "Tallenna",
"base_station_power_management": "Tukiasemien virranhallinta",
"base_stations_power_mgmt_enable": "Käytä virranhallintaa *",
"base_stations_power_mgmt_enable_description_standby": "Käynnissä olevat tukiasemat menevät automaattisesti valmiustilaan kun SteamVR suljetaan, ja käynnistyvät kun SteamVR avataan.",
"base_stations_power_mgmt_enable_description_sleep": "Käynnissä olevat tukiasemat menevät automaattisesti lepotilaan kun SteamVR suljetaan, ja käynnistyvät kun SteamVR avataan.",
"base_stations_power_mgmt_on_startup": "Käynnistä tukiasemat kun SteamVR käynnistyy.",
"base_stations_power_mgmt_on_startup_description": "Lepotilassa olevat tukiasemat käynnistyvät kun SteamVR avataan.",
"base_stations_power_mgmt_on_shutdown": "Laita tukiasemat pois päältä kun SteamVR suljetaan.",
"base_stations_power_mgmt_on_shutdown_description": "Käynnissä olevat tukiasemat sammuvat kun SteamVR suljetaan.",
"base_stations_power_mgmt_standby": "Valmiustila",
"base_stations_power_mgmt_standby_description": "Tukiasemat kytkevät vain laser-valon pois päältä, mutta pyörintämoottori pysyy käynnissä. Tämä on saatavilla vain 2.0 tukiasemissa. Jos tukiasema ei tue toimintoa, sen sijaan käytetään lepotilaa.",
"base_stations_power_mgmt_sleep": "Lepotila",
"base_stations_power_mgmt_sleep_description": "Tukiasemien laser-valot sekä moottorit sammuvat.",
"base_stations_power_mgmt_warning": "* EI SUOSITELLA jos samaa pelialuetta käyttää useampi SteamVR käyttäjä.",
/* settings */
"settings_title": "Asetukset",
"settings_advanced": "Edistyneet asetukset",
"settings_advanced_description": "Näyttää enemmän asetuksia ja hienosäätöjä tehokäyttäjille.",
"settings_calibrate_motion_vectors": "Kalibroi liikevektorit",
"settings_calibrate_motion_vectors_description": "Parantaa liikkeen ennakointia ja seurannan reaktiivisuutta nopeisiin liikkeisiin. Saattaa vaikuttaa fysiikka-painoitteisiin peleihin.",
"settings_autofix_playspace_jumps": "Korjaa pelialue hypyt automaattisesti",
"settings_autofix_playspace_jumps_description": "Yrittää korjata pelialueen teleporttaus- tai hyppäämisvirheet automaattisesti.",
"settings_enforce_minimum_rotation_variance": "Pakota minimi kiertopoikkeama",
"settings_enforce_minimum_rotation_variance_description": "Hylkää kiertoasteiltaan liian samanlaiset näytteet automaattisesti kalibroinnin laadun parantamiseksi.",
"settings_locale": "Kieli",
"settings_reset": "Nollaa asetukset",
"settings_reset_description": "Palauttaa kaikki asetukset oletusarvoihin. Tätä ei voi perua.",
"settings_reset_confirm": "Tätä ei voida perua. Haluatko varmasti jatkaa?",
"settings_reset_yes": "Kyllä",
"settings_reset_no": "En",
/* about */
"about_title": "Tietoja",
"about_description": "Space Calibrator on ilmainen avoimenlähdekoodin työkalu, joka mahdollistaa eri valmistajien laitteiden samanaikaisen käytön SteamVR-ympäristössä.",
"about_view_source_info": "Voit tarkastella lähdekoodia Githubissa.",
"about_contributors_title": "Kehittäjät",
"about_contributors_description": "Space Calibratorin ovat mahdollistaneet:",
"about_translators_title": "Kääntäjät",
"about_translators_description": "Space Calibratorin ovat kääntäneet:",
"about_licenses_title": "Lisenssit",
"about_licenses_description": "Tämä ohjelma on lisensoitu MIT-lisenssin alla. Muut riippuvuudet käyttävät muita lisensointeja, tarkastele lisenssejä alta saadaksesi lisätietoja.",
"about_link_github": "GitHub",
"about_link_discord": "Discord Palvelin",
"about_link_logs_dir": "Loki kansio",
/* self-troubleshooting */
/* found another driver loaded in SteamVR -> legacy ipc connection successful */
"troubleshoot_diagnosis_legacy_ipc_found_title": "Löydettiin toinen Space Calibrator asennus",
"troubleshoot_diagnosis_legacy_ipc_found_description": "Toinen asennus Space Calibratorista on käynnissä ja se luo konfliktin tämän asennuksen kanssa. Toinen Space Calibrator versio on poistettava ennen kuin tätä versiota voidaan käyttää. Haluatko poistaa toisen version asennuksen automaattisesti?",
/* found legacy overlay key registered (non steam) */
"troubleshoot_diagnosis_legacy_overlay_found_title": "Löydettiin toinen Space Calibrator asennus",
"troubleshoot_diagnosis_legacy_overlay_found_description": "Löydettiin toinen versio Space Calibratorista. Haluatko poistaa sen?",
"troubleshoot_action_yes": "Kyllä",
"troubleshoot_action_no": "Ei",
"troubleshoot_action_quit": "Sulje",
/* tutorial */
"learn_title": "Käyttöohje",
"learn_card_standard_calibration": "Tavallinen Kalibrointi",
"learn_card_standard_calibration_description": "Opi kalibroimaan laitepari.",
"learn_card_continuous_calibration": "Jatkuva Kalibrointi",
"learn_card_continuous_calibration_description": "Kalibroi laitepari jatkuvasti erilleen liukumisen minimoimiseksi.",
"learn_card_base_station_management": "Tukiasemien hallinta",
"learn_card_base_station_management_description": "Hallinnoi SteamVR tukiasemia langattomasti.",
"learn_card_ui_tour": "Käyttöliittymän esittely",
"learn_card_ui_tour_description": "Yleiskatsaus Space Calibratorin käyttöliittymästä.",
"learn_action_back": "Takaisin",
/* these are kinda like a messed up wiki because its inlined into the app like this, oh well lol */
"learn_page_standard_desc": "Tavallinen kalibrointi kohdistaa kaksi erilaista seurantalaitteistoa toisiinsa siten, että kaikkien laitteiden virtuaalinen sijainti vastaa niiden todellista sijaintia.",
"learn_page_standard_select_devices_title": "Valitse laitteesi",
"learn_page_standard_select_devices_desc": "Ennen aloittamista, sovelluksen tulee tietää mitkä laitteet haluat kohdistaa toisiinsa.",
"learn_page_standard_ref_device": "Päälaite: Tämä laite on kalibroinnin kohde, yleensä ohjain.",
"learn_page_standard_target_device": "Kohdelaite: kohdistamaton laite, jonka haluat kohdistaa Päälaitteen kanssa, yleensä seurantalaite tai vastaava.",
"learn_page_standard_perform_title": "Suorita kalibrointi",
"learn_page_standard_perform_step1": "Pidä laiteparia tiukasti samassa kädessä. Yritä pitää laitteet kokoajan kiinni toisissaan ilman, että ne pääsevät liikkumaan toistensa suhteen yhtään kalibroinnin aikana.",
"learn_page_standard_perform_step2": "Paina Tavallinen Kalibrointi -nappia aloittaaksesi kalibrointi.",
"learn_page_standard_perform_step3": "Kävele ympäri huonettasi samalla tehden laiteparilla kahdeksikko muotoa ilmassa kuvan mukaisesti.",
"learn_page_standard_speeds_title": "Kalibrointi nopeudet",
"learn_page_standard_speeds_desc": "Voit halutessasi muuttaa kalibrointiin vaadittavaa aikaa ja liikkeen määrä. Pidempi kalibrointi ja enemmän liikettä antaa paremman tuloksen.",
"learn_page_standard_speed_fast": "Nopea: Kestää vähemmän aikaa, mutta voi antaa epätarkemman tuloksen.",
"learn_page_standard_speed_slow": "Hidas / Erittäin Hidas: Kalibrointiin vaadittava aika on pidempi, ja tulokset ovat tarkempia.",
"learn_page_standard_completion_note": "Kun edistymispalkki on täynnä, laitteidesi pitäisi välittömästi kohdistua toisiinsa VR:ässä. Jos näin ei tapahdu, yritä kalibroida uudelleen.",
"learn_page_standard_drift_title": "Erilleen liukuminen",
"learn_page_standard_drift_desc": "Ajan kuluessa laitteesi saattavat liukua erilleen. Jos laitteesi ovat liukuneet erilleen, sinun on kalibroitava uudelleen. Jos tätä tapahtuu usein, harkitse jatkuvan kalibroinnin käyttöä.",
"learn_page_continuous_desc1": "Jos havaitset laitteiden liukuvan hiljaa erilleen kääntyen tai liikkuen. Jatkuva kalibrointi on vastaus tähän ongelmaan.",
"learn_page_continuous_desc2": "Sen sijaan, että joutuisit jatkuvasti manuaalisesti kalibroimaan laitteet uudelleen, jatkuva kalibrointi pitää laitteet kohdistettuna taustalla automaattisesti samalla kun pelaat.",
"learn_page_continuous_note_title": "Huomaa",
"learn_page_continuous_note_desc": "Tämä ominaisuus vaatii erillisen VR-laseihin kiinnitetyn seurantalaitteen.",
"learn_page_continuous_desc3": "Jatkuva kalibrointi kalibroi laitteesi usein ja pitää ne kohdistettuna erilleen liukumisen estämiseksi. Tätä ominaisuutta käytettäessä, kalibrointi tehdään kerran ja sen jälkeen laitteet pysyvät kohdistettuna automaattisesti.",
"learn_page_continuous_setup_instructions": "Jatkuvan kalibroinnin aloittamiseksi, valitse laitepari, kuten tavallisen kalibroinnin sivulla ohjeistetaan. Valitse VR-lasit päälaitteeksi ja niihin kiinnitetty seurantalaite kohdelaitteeksi. Paina jatkuva kalibrointi nappia aloittaaksesi kalibrointi. Ensimmäisen kalibroinnin valmiiksi saaminen saattaa kestää hetken, liikuta laiteparia kunnes ensimmäinen kalibrointi on valmis.",
"learn_page_continuous_should_use_title": "Milloin jatkuvaa kalibrointia tulisi käyttää?",
"learn_page_continuous_should_use_standard": "Jos sinulla ei ole ylimääräistä seurantalaitetta, jonka kiinnittää VR-laseihin, et pelaa pitkiä pelisessioita, tai sinua ei haittaa kalibroida laitteita uudelleen manuaalisesti erilleen liukumisen tapahtuessa, voit käyttää tavallista kalibrointia.",
"learn_page_continuous_should_use_continuous": "Käytä jatkuvaa kalibrointia jos et halua miettiä erilleen liukumista. Tätä ominaisuutta suositellaan käytettäväksi jos pelaat pitkiä VR pelisessioita, tai havaitset usein tapahtuvaa erilleen liukumista.",
"learn_page_continuous_physical_setup_title": "Fyysinen kokonpano (Seurantalaitteen kiinnitys VR-laseihin)",
"learn_page_continuous_physical_setup_desc1": "Käyttääksesi jatkuvaa kalibrointia, tarvitset erillisen seurantalaitteen, jonka kiinnittää VR-laseihin. Seurantalaite voidaan kiinnittää useilla eri tavoilla, riippuen mitä VR-laseja ja seurantalaitetta käytät. Voit käyttää internetistä löytyviä 3D tulostettavia kiinnikettä, pienoiskameroille (esim GoPro tai vastaava) tarkoitettua kiinnikettä tai kysyä yhteisöltä mitä kiinnitys tapoja muut käyttävät.",
"learn_page_continuous_physical_setup_desc2": "Kun seurantalaite on kiinnitetty VR-laseihin hyvin, sen ei pitäisi heilua tai liikkua yhtään suhteessa VR-lasien etuosaan. Parempi kiinnitys antaa tarkemmat kalibroinnin tulokset.",
"learn_page_basestation_desc": "Space Calibratorilla voit myös hallinnoida SteamVR tukiasemiasi, jos tietokoneessasi on yhteensopiva BLE Bluetooth sovitin.",
"learn_page_basestation_1_title": "1.0 Tukiasema mallit",
"learn_page_basestation_1_desc": "Voit käynnistää ja sammuttaa tukiasemat sovelluksesta, mutta kanavien langatonta hallintaa ei tueta, paina tukiaseman takaosassa olevaa nappia jos haluat vaihtaa kanavia.",
"learn_page_basestation_2_title": "2.0 Tukiasema mallit",
"learn_page_basestation_2_desc1": "Valven ja HTC:n valmistamat 2.0 tukiasemat. Näissä on parempi langattoman hallinnan tuki kuin 1.0 tukiasemissa. Voit käynnistää ja sammuttaa tukiasemat sovelluksesta, sekä vaihtaa niiden kanavia. 2.0 tukiasemat tulee asettaa kaikki omille erillisille kanavilleen, muuten ne eivät toimi oiken. Space Calibrator tunnistaa yleensä kanavaristiriidat ja tarjoaa niiden automaattista korjaamista.",
"learn_page_basestation_2_desc2": "Vaihtaaksesi 2.0 tukiasemien kanavia, mene Tukiasemien hallinta -sivulle, valitse tukiasema jota haluat hallinnoida, ja sitten valitse haluttu kanava (nykyinen kanava merkitty vihreällä, muiden tukiasemien käyttämät kanavat tummanharmaalla).",
"learn_page_basestation_auto_power_title": "Automaattinen virranhallinta",
"learn_page_basestation_auto_power_desc": "Asetukset sivulta, voit kytkeä päälle automaattisen tukiasemien virranhallinnan, joka käynnistää tukiasemat kun Space Calibrator käynnistyy, ja sammuttaa ne, kun SteamVR sulkeutuu (Space Calibratorin ollessa käynnissä). Huomaa, että Space Calibratorin manuaalinen sulkeminen ennen SteamVR:än sulkemista ei sammuta tukiasemia automaattisesti.",
"learn_page_ui_tour_desc": "Käyttöliittymä on jaettu erillisiin sivuihin, jotka kukin sisältävät eri asioihin liittyviä toimintoja.",
"learn_page_ui_tour_calibration_heading": "Kalibrointi Sivu",
"learn_page_ui_tour_cal_desc1": "Kalibrointi sivu sisältää kalibrointiin liittyvät toiminnot, hallinnoi kalibrointia täällä.",
"learn_page_ui_tour_cal_desc2": "Ylhäältä näet onko kalibrointi tällähetkellä aktiivinen ja mikä kalibrointi tyyppi on kyseessä.",
"learn_page_ui_tour_cal_desc3": "Sen alta, voit valita mitä laitteita haluat käyttää kalibrointiin. Valitse ensin mitkä seurantalaitteistot haluat kalibroida. Valitse vaemmalta päälaite (tämän laitten paikka pysyy samana), ja sitten oikealta, kohdelaite, jonka haluat kohdistaa päälaitteeseen.",
"learn_page_ui_tour_cal_desc4": "Näiden alta löydät napit kalibroinnin aloittamiseksi. Jos olet epävarma mitä kalibrointia käyttää, lue sivut joilla kerrotaan lisätietoja tavallisen kalibroinnin ja jatkuvan kalibroinnin eroista.",
"learn_page_ui_tour_cal_desc5": "Kalibrointi sivun alaosasta löydät asetuksia joilla hallinnoida kalibrointia. Jotkin näistä asetuksista eivät näy, ellei edistyneet asetukset ole päällä.",
"learn_page_ui_tour_base_stations_heading": "Tukiasemien hallinta Sivu",
"learn_page_ui_tour_bs_desc": "Täällä voit hallinnoida huonessasi olevia tukiasemia tietokoneessasi olevalla tuetulla BLE Bluetooth sovittimella. Tätä sivua ei yleensä tarvitse käyttää kun sen asetukset on kerran tehty.",
"learn_page_ui_tour_settings_heading": "Asetukset Sivu",
"learn_page_ui_tour_settings_desc1": "Täällä voit muuttaa sovelluksen asetuksia, mukaan lukien edistyneet asetukset, tukiasemien hallinta, sekä käyttöliittymän kielen valinta.",
"learn_page_ui_tour_settings_desc2": "Jotkin sovelluksen toiminnot vaativat edistyneiden asetusten päällä olon. Käytä edistyneitä asetuksia vain jos tiedät mitä teet.",
/* Names of the various languages one may select. Shown in settings */
"languages_en_GB": "Englanti (Yhdistynyt Kuningaskunta)",
"languages_en_US": "Englanti (Yhdysvallat)",
"languages_fr": "Ranska",
"languages_it": "Italia",
"languages_de": "Saksa",
"languages_nl": "Hollanti",
"languages_es_ES": "Espanja (Manner-Espanja)",
"languages_es_US": "Espanja (Etelä-Amerikka)",
"languages_da": "Tanska",
"languages_sv": "Ruotsi",
"languages_fi": "Suomi",
"languages_no": "Norja",
"languages_bg": "Bulgaria",
"languages_pl": "Puola",
"languages_cs": "Tsekki",
"languages_el": "Kreikka",
"languages_hu": "Unkari",
"languages_pt_PT": "Portugali (Portugali)",
"languages_pt_BR": "Portugali (Brasilia)",
"languages_ro": "Romania",
"languages_ru": "Venäjä",
"languages_tr": "Turkki",
"languages_uk": "Ukraina",
"languages_zh_HANS": "Kiina (Yksinkertaistettu)",
"languages_zh_HANT": "Kiina (Perinteinen)",
"languages_ja": "Japani",
"languages_ko": "Korea",
"languages_th": "Thai",
"languages_vi": "Vietnam",
"languages_system": "Järjestelmän Kieli"
}
Loaded 100 of 2414 files, more files were not shown because too many files have changed in this diff. Show more