From 290fe3c4763c7640609839fde0e31a619a79b3e2 Mon Sep 17 00:00:00 2001 From: Butterscotch! Date: Thu, 15 Jan 2026 23:18:54 -0500 Subject: [PATCH 001/117] Update vcpkg --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index 2fa7118..66c0373 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 2fa7118fb2ce0c27ab73e08ab1991f4cb67af880 +Subproject commit 66c0373dc7fca549e5803087b9487edfe3aca0a1 From c69900c1dffcc7e18a5c5e9273a5c1548f4e9104 Mon Sep 17 00:00:00 2001 From: Erimel Date: Sat, 21 Jun 2025 18:43:16 -0400 Subject: [PATCH 002/117] WIP Add support for fingers via protobuf --- .../resources/icons/slimevr_tracker_icon.png | Bin 0 -> 15880 bytes .../input/slimevr_controller_bindings.json | 58 +++++++++ .../resources/localization/localization.json | 13 ++ src/TrackerDevice.cpp | 121 ++++++++++++++++-- src/TrackerDevice.hpp | 32 ++++- src/VRDriver.cpp | 7 +- 6 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 driver/slimevr/resources/icons/slimevr_tracker_icon.png create mode 100644 driver/slimevr/resources/input/slimevr_controller_bindings.json diff --git a/driver/slimevr/resources/icons/slimevr_tracker_icon.png b/driver/slimevr/resources/icons/slimevr_tracker_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e50fbc71f6fbb5b7009b7664417f9b2bcf70dc GIT binary patch literal 15880 zcmeIZWmjBH@GrWDfdK|5g9aHS1a}KMKpS z8UR26R#H+*a#B)oM`s6fD_b)F_?+eyBP`pk1PgjBNR%%Y{4LQ#BlXc*Laj%+L<ryEC_b^SAI^RDIE_pLY4L;+6*zpECet z%8K;4cGE7wGv?}CM{j!wG-6+}0WY*Gz#bn4iUS9hd>BMug?aqV5(| zAs*tZTfHZkf&yjTY`6XJ_hZCPkrFgRpp&3073zyqCwCxFrir3IJkKD1cc9EM8qVdWaQ5&D5)VD<`j@`1aj`hDOFFre@|AmR8m_ws!Uoj!w=lt{>e#eg5L^;pye=gY@%Zx(wQ$U;8_*I%Ev^z+q6x`ynw}n_uHUyoC5mXaUg@ zzNOeNmc2LdUYW0r`cr7*1&ZcAzomAY&>!;_u?a8e|N4bG3M;+cXE}%p6ixr}hRS`l zeci%Vb?A5f%2c7lE4$CnI{?nmO1r;bzEr+nEK4Lxe>?<}Q2oj+0XW|!EWP<~*;S`+ zO~NA1(X=)`YK4-eXMK(t(ADcypKIwP7@w{z4aDp8elPrUIVV)#esy;|(sx-(56BnD z$h>>75T2GZ?p^&e3_)4x;2p +oYVV#5nY(HB8kaUiGqe2Ts|eLd6Ra~o5R+F5z0 zU*kh@sc&jRFHp-*O=TIM{CuvWLQO9^PFVs-lBafEIt`y*Z19B5cQgR(>qpP99aQe} z1jWY{fu!Sm{70GIOc;r2Bdqh}WD91mbeFpsIH!;5$Y`*P)#spbS164~t?>pZEKX9d z`k+jBk~2Q;3Zkgneq_yN477_eq>cg_X14>n&cnF1?zRa&qE&GiWd6ws$QfRfZuUa0 z$`$IXU3LgqYrRl-)~T_J*40V`nIj*yXGFfO8#Edlv*aQMR|PK3zSaC@Jc`qfTE*@l zV_DJl8tar>1Vt;_r$?$3R-o&+2SfoglP~HdW9Y+Ax!X{*q__6MiR9`xDt=IbR4V00 zEy7@06uWSQ{4{oeNtjwPfh{8a%1STXKfb>zxb=+mk4gYHp_-C z?!@J#w@CUET(&>uRf#!iIw;>NwH>p&{M=x;=NN_sVcIbPT_xt|dsv2v?Ai5Dg$1I~ zf0%}CgN2GJzdQX(Q=o=rGuRIo1W~5$sGMOImPU$3FJ1ij&^RUQ_jjNRb7{-c$bZ(< z^I27eV>Ndu@#&u|1+%Lv^P4}SF{aMzT~~J;M7ci8I@1Z@XHHw;8AVmnS;TTlXGH43O0p`Jn!1L>uTo1@;LH1Yd*CdIKMZBF~T(!x+?X?<8n zFR=8&@3@3lh?+Fk?-rq-QU!HZ-l_Yi5?wh{``)Vcf5`8QqV+;MTpMB5Ska83LLng& zmG%`65uK1+A-oq;TH$cpoW^fL+;#<^_%!nOaX2=)e^EwznA>OdjZ<$kE=74{pVVK` zO74Z;6@HeW^QHUfso?uq&!+ZvMX>%Tl39sRA?g%L(feGh93e-90~73DQiU-FewGmR zmEvVn%8H-ayh6oQdiALlC11cOtMj5Jkx_}^b}zI*u5~$S-*C) z#9t<{CJvoKlTw{Ngi>@)MBc`_2}67ttB>(IQx{Utw`^>s4}Q=RZEi)=y} z4m3|AF9!aiBuW*&sS!z>hj_e(Y9VMV{on(hnleOHK@JO;u_dd)P_3T@{%CUX)rugN zf%nho6)x=2$~NPE`sEc}w4o%{x);GQh2Cj)`Wfp%<^6ISm<``n&Junbq*m#yA-;I_ zt!Re?s`dM;Drx~)gAT~XLr~uk`ku1@Iqfy4WazhRgl20fNA0_+^Io%)*o}gPQ|l)) znk_BCKWK#OAUoRrdL%hY_mw2{_h6j{ioY6lC32)}QPVJ_frqFneft#(7~7x{Nx7KG z;CQg$v(irP=X|o9i?n-d!_F8iM9pkyqR@ghST=2oW@|TODM5CwUyK})eoquXF#Z<$ z+aTkOlDqFBRPaX}5o&%yoKh#*QY8~3(egc1@aNfIKb4}h5)|B;y40*5Yo8Kit}Q7o zV|HRK%<-EY9omaYyJD2DwR@ZrnEQ=#AoQ?w+AD+GBel*LLK3WpK4oRJH0BM0yCl_h zsNgL4!;cPOmTY94Xez^%fCL)FVdS8n&qlL4sP8r}Wrh0lKbWKMSSi-S&&zKp6-sIf z$0S4_lsaF8Sz|rW->akPnMm6GpjHcIT%moohvBWdj)kVzf`??LW?A8m+r`jZ5a%qe zSV318Q|Sz3m?e^^AA>pOGkx6Y_!kUq`n91`ar9_{rnfgWxMoeXq}16WY};=JCrrjij1b8NruNf)^+Mrl&j+c8Hw_E+c)WLE~nUm56aH3gtt$*s{l zB}J%s4BIr96DFz;tD+HIyp(_SyG`OD{P_6_fCe|!-pRzWvd(MbE<57xo zg~y^BV#4MqtBO|(3S%{i=o)#U$VK_ys6CrkN_ttuc$oYa*mmO|Q%@ zs|Em>pOD&LCqgqU(pK+buOUL}I)Llu%76&B_}_kyENF&Jk>x$O6`BCU6~o&y2U$hX zw^c}W8#xvX$+YGXIxU(FDArU3Y3UZAaK81`(pe;ec8UatCx})8ignq^TC*|GPBnrk zE%n@bXs4b}ukfk4>-sSkspOML22MO@D;vo3PI+c#p|+K+Ssw8&a=6yRokZ|#)sg|C~p&1sZ)ZQY;x(gX2|3F!gUePk<^x87TVWeo|HQuSOrm)T&v(swx_7A`S* zhYf@&7wn#&BWb_m1YzAqWt(lbW?i{4=7%`2t@O;>S!uh#3{1j4UlRS%I<(PoLkT;; z4!gP(p%_@yRAM=P)b&P{zH22KO>2#ITMSuq51=J;u^Bdi1h0;Z zO$PyTBMrLZ&36WwR|9+}o&N(s(HeS2xxQb1l(@WP_ z{6(4@i1{2adDwjmW_gn^mG$r?&#GP$yjS-$PnlpP#~D(zarjW-VzXTATaKa4?6y(-cL^&*p7^{len1ogwdZ1nXIQ0Zyd zc?l1i=iM?9Orn-2ScgW@wcwafq{O~mnNNmQA4Q7Lwr2bl!J8FQq6hdArPP`m>dgPj zJK9i(p=M7&L#zwAbzH!IO5VkA)_ovTZhvbNw6Qu#ya~g6eY#07kuqgD66(0a7KWNT zI=MOvO6|<%`>RY(K@HFG6ws+<`ST@&mgWlu0j0c+7?Nk`ZJw+wlmwn5#B07f@3<)+ z7GO3Z7Gi$OO@4~6w<8tPsrVaX!c*RaffY;OYqqT8JZq#ZzxHbrcE9KKjD#751S8?R z1;S~+tKw-_iv=MuWv^0yQlOGh70QM%R8n7=3=cXQ?wr6dRpKaew7iJzn6%Lc(xqY$ z`jnqB9AC!T$i*N=mWB554IOdi>uD0$8K~OC z|M3oOzJ??AD&il9du8>nU>LfLZQ$0bW&W3VAAUnS_$BR&>cc&t`O{8c z8xFw_nmV(kl8__kLGkC`X}leEePb2()Isn|uKO!OW!HSRS@?I2cpqMSthTTuG!1gh|hX`+_V)+FQ`U3J8G8lUW)ljy3veqqt<^U zC|66w=@m+4FI3lUlsWGB6NTNBRZ>1(-VXH7I);tZ;8<^Y>kK69BL{`P=H@rJX{ybb z6N+-ac;cvM$?2vm#V%OORS^KAX^bvkPpRPh~8+5xqa!UTM zXP$8|h}=XNo}kR)`JjM&X%!!9J}w-r!95En8sY4`DPdAsu*K1Dq~74q*8b|dR9P3> z1fCXpDWPEODyn7?OxSXO-Y?BC&X|cPr!J?b3eRyzPns;58+ui=beQkDkQgnTDI@3V zwm}trt^!dR#|D?R<`xamn7Phb_Sc6`F>WFDKIy1O49GxJjF9Sp?=!FVlrNbw*JSed zR_Z54?DR6MdFqihaWu}pnXLf&6e~E(g{*w=yFC&H>jqf71p}bl6>AzlBF*%{>D6`2 z*ZE9&^AGYYfjNtCw`YqVX*Q_xl?x=y;N|iRb$)u`4puMfd8wTz`nF57$?zAr$F-wb zEY*2yc~q31bo=6;P2iDOF^^?D(abw&wsVKunX*7BG{-SfV(hHBZQ3p+;Dy?}0QRIY z#V>~vUr0g2jF9KL!bMHgn0q+YI97>N(XFu>UxtFEI8+d&ew8T_m8$}-6YBWkiad5b z%mQCEUX=<2X-tQMW}bg3iu@@)DOziS-}+VN8fNTom7rS3XT8%eSpF!IfysACXS5(; zOB$&%)1qMj(mrX=iZ&)ST%-!jxQ(f@pb-d^c}1uqK7M1c=E#^w3eiGSI_4zQ_(NeV zFwW?JD6;dL@geM!EFJ6xSSqecx1H$s^Ew|WvWjR-zh<9z)W#Gn@sPRq-XeZ1$o|Ir zaZhaNaE|7>r;;~<3)Zj;ud{0kV<*F|ZncargvYj~%yigG&+qplUp(&w>5@!PHX>|U z$j!8g>D{uwAFmR@DS4%(2A9L2TN>!Z75ByZ8XTnQ9|fPB`8Bg_e$jq zUBx16n5_R{Ta6t0@y;0`?ihcCmI-KYQThFT1}gP`1GQq?_WzGDkx@j~Ontp`380TN zF3;+L>k@IKQ}#IxH#L-w_33SOW%(kf=tC2WmUgh)iE`md9tOf{MjVh~YG^`)?k%EX zuCmMoPjVlN+{{Pq=xxwq$_PB^oVf73sv9|2a2X(fSemvt{XV8jIyt%}MCJ?X^q|&{ zXqGtw=V}XCV%+L(WY|wPr2PFSxzX+ILfPLxqsiap4u@w6UB}mgI{WGFhH4_S2|dPV zI)-e{=^nWo&^FwOYu*u|UZDGemD3eS?L+CZ9qAsG>sl!xWvAZYBG-&gpSwRj;xSd3 zG#9Lq&I~1|nB521jCye!YxPffr-2i`*Is3E>`B>gKxgv5Mhj)gkojBxVlgjDM-Rkm zdbg%1jD6L&s9Y4G*Z*;8?Bp%|zNPGWzVeJYUHt*6wSdGbs)mwbFsS}|sHzt#?e}9u z_V*Az=f8z!9HQ(~$7Z^6kZVQkqnQ2M>R)&v$;|G|gd2iu=_Z;&q0^HJEVhDl2K(YZk12yxpQi{8TT?T-YKV{m6#f>09UcX9RcR^LT^;gM+IamnNH@k%bMcI z?~B!TQced>HEt?%sKqrM;S5_*6wpVPP)Vj?AfV*szxMmh@p&I7yx;;Uzoe`9!YP7- z*61bVoM6Q&FIv|Yj6`Yw%R_X}3Ez4F>3UzDJ$Z&zpwS?{krUOtFvjypV92lX`x^~k zb-jyle1fhu2PEL+^l?VKTIfX73m;RL7;BdfK|=qDz1zzCWq&>S7)(+G`!gLLvpz$$ zd+)*xoRDASNg;HaW=%4AmsW|2>!UKcogd?`UP{!8W?&}J1eOhy;J}kEG55N(CA?6Z z@Z_Y4IU|TK{PnsWk|jfeL5**+YfiXlZ09(~=!_gBtuZ3GaplfUN!h4zo7l(q`cDbf89iS4Kfb0*_M``7x-BXI$5-iH^-d*n zfmBZ?;hO7vm}MISVgOrwADkc|dCg}|r5$37BaR=cU# z{X(e;iR%tiX-s~AUWrJVD)Mjq)8t{(l(dMVz0q4^-QL&ksY}BRL=m!}&BNuqCvxQO zUJt*Ce%=-UH6n%dMEQ-<+)*TVN~RA>pm!D>&IZeLV((dvR_#RNN4TOU4a%u3Y7iT!N}i+PS?lBQI^Pw()J z>(4OKfUn$7`H%#4`-w%{oQeJ?M7jWCK3~rFqoy+g!+HN~JAho|$)H0)k`+sZYL4!t z(f1-=GLOQ^8sbVnl47cIY2x)I<5(ro=t!ZZ|7enKX^12_v6VW^+%ja*d}O+){C;up z%G<<$66`h{Dae@lJG+ma(0_iGU{RCBZOBf^pHI0VG?EAQ4*X@pJ7_*XA9yYA7NECJ z_8%yYpJ2>%B+5T3W2!z=1-#2ueuwx{$#b=KE6MIG{-3gp$WdYzZFFyEQ9^z~$<#^4 zPXPx~D?KqvO))w@zLQ1^f`x9Alz#NZRGGkk2};CyBAsj2vD>Dy9r^xKGlUv9{n=pA z%9iYJ(dZQUI9N^^2)pex%TeqlaKl|OBV#MCqy&~Ik6BsF9ykq`44)eRTQVM6 zqkEb4NiKY_lzRCR)@zL8jOO(WghOL~d z99@mv8*%h!YsaVjsfjM4W)BmlY*5~Dw4_Mz3oXQaS|7&+vjTj+O#+@{n?k}Af18&B${b*vU*zsH9WyrKQD`Ief(Lx(}yr4fBPVU8~!XdUbX%$R! zWX@z9H5YFF<4e4QG*@Ho*kz&RG_yuRM0yku+YhfD$_Iiw-`NxyD$?&uVw?+{3;rdA zOb@?{w&7bUyiJK=*{L`4SI+g9FE)Hfzh8`eAYCE2v!Df+RFQ0?Jj$Sv1cOAkraW9) zvLG*$lz(K&{(c`#t`O%L;3GZ5?RbV$EVDe}HD;bIlWsnN$51c%krqviLqvw!n2pE` zvHc~*6!I|r^+%nj;Ko0(3oEnIUcsAgj<1S8G+|6vUFn&Gn=G6nTLA-;+VGj9`b?!F(uQT%!g-O819OLh)qHc`J@*c-?CoB~E z;_wIAa^=2uiXWON5gp-%_KTjWtm5~Q*eI|bs z@!c!hoa{yJ?Fejncn_#iW7^zfP1W*4vhgfq`%a?duq%4xzF!CEien3AevcEJUpGw_ zBb>1!oSFtpUQW_yt~yN6LPEqWJhUIdA~e}ry5uSMswuM z*BuVXY?HEd-n`rfhfcx3@b^S^q0Se~1=N2?B%uCVJYHK;mLw_kG5#GJJS+B5+n?MH zS(LDmBK7XzyQ6WHo9=R1`yN`Ml^CSP4|irUFy~yma$b!B(|>IuLQAeg=NHMN(96|R zgDtxnzjs#@lCR8o2l%gs=CIoed1C$~+7xd*)syOU$Dmet?`x7fG3J614rMiRGu3Pd?YYIlFVa-BEM)A%%HFd&dew5)O1FgL!?&J$MJCuZC2yeerg4OC%JE zHb;Arf7iDi#Q8KlACBDVhp)wcIAScCtTabll0h>0CC%EQ#s3KfEl(VYS)OCwGkkwJ zC`=>Kw~wCvROl8Fm&v-=Gm%8Uu68D`JK#gjq0O4?08j< zGq}eY>8&P6ziy^eUuzA%V{rzjpP>fajMF~@;4D9)zx~pt23(hG1T3d#CTXs8&!~iU zZ7?<&ETcC|@IWI1jnofQ+a5Z;<(<7$*|mv?l4ynjG|seDE+71dm`f%$sckwVZH`H+ zw=`!&ZznN3aj|3a_trU@smGy3WR#~eQ%-(>%6_Xkz3Aee_&v7W<_}jx z#O>!%rl(eJ<6Rtr`Wx6Hd>u3X*Vg9$O9WyMrX(;uZe3_LG>&U4`|BBH8Q>~evS`*6 z=f#N_UfY~wVV{kmLD@FtFA`i6KxYTufdy_a3Q@^;fTK@;g8@wi_aDH`#aI^9Yg3Xc z_ACR$tC%&e%D(YYZuvQ)8wIMb&x$29E)E&5@|Aqk8l9PMgp;+22yAepdBmfz^#?a0 zvN!bxjwSj@JHgPA4f&tbe<0)kd?j;RV~gY7lla1@QLxVnv65A7$PrUlP_Gq9;#p$I zIB%n5R7raycK40O~aZ~E_{-#h92xoUd*CF#$5v?ZJMG@T$4e( zqB-Lt>>I55Ho)5>7T#>EW<7!Dh$#aw6VH`T#X9k#8DN+D$@tm!J5fu(j)&r1tN0~RyRk3c>ZU@*|FxQpteU_jmLo7r>%eRC+ zOp`#yF9IaPo4*PqqXD$kjJ)l8pWkyYPj_E9a z=t{d>appQC2d($iA+5Zw~m{5w-j>yUWkSfjfpszHEx{IgbOOj%rvM zB%Ks`b0Ry>p>~_x-V>J){Lfn1(|SKIZ)O0LN|w6lp(`DO=ZKX(+&i+B+WpcOz$0$i z@Nf2YDKC2(hXEGga^CU6!CqW`P04c0uVSrS0@I73C(nKNcfA<@5O zvxW6~Ro&+S!q$&V%ZR955LlL?c)Yk`O=8g;2(5()^{D^JEyaO5{*AFuQ9Vva6zI!>t{>r^%e{D1B97OytSo*dLE>RHO)WKN_27;fgoQ5FgJDc0THQ?yix)@kiF@IdVVuk3u=?Rfq#JBa5XL(x*m-qQOqxXaZ7qUTrMR@UTp!9~5pZ4TkOA_UUXF6>Uf8&5?^-!oKNAHdEq;{-+|s%a5x`g^HTA-H|V`JV9FqzDu!5Xf{locr=HSbAI)sb@?to^JYK$F^p9 zL^`M+cu!(e<)?P&b=*duMD2|ckU@}HM2sMs%7c+TfNm^tRRI;O83^hPWluv?#X0l@x7n>Pn?3qCFt763_S-Il#shJe_2g7WyIX{kL*7xz3%^9x-J6~AQ<`j*h04vsfC5-EU+1}HNQ9)^D_lPz8ZVYre}kCNuo;LW9s{!2SfF&QgCu%EZY&Cf3MWw=73Z%A#_xt5g!r$m! zi1AC=<;r6TDpNV``vxbS%De9G$~+vD5Z zp=Nx&9?pEp(CyZ_y`COO5ens-u4u&%Ox_~ExFH|d!^(`8TyUA zpKb1~HH7Z|yXf3teShlIdYF4|2IlIGX+b+~)?s`TB}H;xl<@3ZB;);5nrh{EiL>7~ zNm+foD?MDE!J+iUXY+|D<}PX9cc0xQk(Kun^}rCjA~ava8J{=0MLOuizTvpoyt>}T zstam^Gf)#+jAA<3O26U^96c#U?Iaacn?%)@Q5eEA{s(s=p<(xlZ}JfC`$7cyDYkX8 zw{}cgmn8{j5FmUy>Dfh2p5F}r$y5NFgbps!uBgFuWR>ybo`s$UHx1_S>g&ut>RK4G zYR{!ALcJo1l~8(2->kL4*;_3b`6fC>t}8e>85nG&9_cu^(*H;(YS33;^tizCH#Vz7 zN1OqZ;&r`G!`YhOaLo{k#CZq&JtRn#lV2-^gD4UCk>T7TQ7-15F_6SL-=sLh~SpE;+%^SuI1$ggaCY-PlX?q(D{pheER@2LR;ON`d(G>0~`JsAyX+%=oOA! zDPDwjYPj_%pQ4OD{+XV8;f`b|Hjg=b#r2{BO9GC1`E-Y+yH$OQRyc}D`BdM$SqPD} z;!56X?uh&SWM{9?B7M1AmdtvtMx#>kcMWDy`6eOX5mnFCq%)H&5Qe*7Kzd-$6|EJd z>J_{FVM2#W{k0sAX@vcWb^B`1b$>s40b?CQ?T$>|Nr2IMO6ShA`u5fmU49h}*H5+R z3o2T*_pcaa(EO-qj9h5AHlynbap5k_3TmZ$2UDMh&&+5b>*BL-&GXg=rXPh{?9OdF zK;O&v)LAl~ZH(n#x5+AI6|>p{;!?&i2Kobq=v0aq9kv%P;*YON8>nzwO(iiyn;$DD z@M}zW`zw}~-A&P(nP-JJaDPr5pfBaNhX%Ao&Mf zjgCq%+uJ*sZSkCAo}s>?H6oWyTce8AAm~?xl}(hSF2_=CIV8t;;d|Zp1QmO=+iakv zuKES2Bk64=CtJYk+AO>mlza|D#WoJnFV=gHZWALB7nihR{GPn!AdI?f3{1Ph6v{stPkUxBFw{LZ+-Py>sTiQZ?^XFQut6ac^ z>tiZF(Xd`N`f^m$iv5K4n~^_+7x>B2?GG+n`(h*V!bne7L`#WeEI-_;1M%0fV!Ulu z?Y`Ngziem3Ko>0J@fY-pG{|&O84=q;XBAH4Z{~D42_dJ=jWMH$Q$8xxFbB!$hMC$S z^AaQ`Y2mpWw||qIe!M)Q9b&EQ7KpGin4zNI#fZ)KHPw?E9RBiSzGn8$Xn)@2$`2K*qjehvTK`QW1YjnK4OuXe($BM z2B`{h0wrsA`G=$4zC*gWyl>01*kwu2Fmf`Lp2bne@)5|XO1Qt$P`Nz_;iZdfNfo|p z`-dXO7gNR)-R2NecM|P(VKyIybS}Hr!80}}6w;%W{@2gi}W+8Q3l;R;3b=sc-&hc@K0q!{|s5fHr zJDSluzSd#!YwYBMrnZ7ld>;L-G_o`9vY|FG6@Kj3g6jX0pWP59%Z%{>= zehh$7N)5UpplKaD9SpSO$!y`csgJ6d{j69qW#9awY1vfFYy9b%1|PVOhHXsu#Wnp} z5(5>LcJu2jY&B&jOAtBEGc$)x;Ss$~9>E&L$cTE(9dZx~JB?X8jhJ+PhRuV-Pr^^1 zOggOHqK(vKwO-&?0Za=(rpuN4al>uD)<4Bm4^1R?%sO=*3*qa!*Ms(5=-M_mpF|{o z_WYT#kw;FWJsAT;z$sncFxa^H%X{H-2$@2_4)R=1C zP_aUrZB&i5YZOe6Z4Ak_n3|-1ts9Yrr6F)x)}2=#D+aRjIg$(dkUY!$Tub9bK~~2G z9~KcYV4zkcU5hF&O>6aD9}z9UZT`Fh-Q5f=yvY5z)dGNd{2W=G6!g#wUu>hMb+X1M z4v3hG#n87CmPPkSCkyq6!3p?6W5WM;riOnG9?6{c`X9fP^A%3#@1aG}aEaYlU%dm* z?oF=)WPV<8Whp&|5*~{PW2^`OCXKUqI{2us>K+Z^18|;BWf)9{-IBXe$hVNNgfvI+ ziyXeZL2J|D2IKxab^F{=Cd;s19ZB6~XHZm`px zC7=E|b5?nDAo8*SPl@Bfat41y$PzeTHO84!fGANDPedP>10PpSaP%)3!A0o7#Sj5< z1m$+B44>8ru-n;PBHi5x!CxjW8DN6oFLl_J46yn`@E3baCa|8OyOIf92>u_N67>aY z2>wDv$(%L>f4<*-+O|Lp9I|h1=GXyx-O$9X)eWF{s3vb22na} zcu8%-L;?i=S7+Iv<&p$AWJkj6q7Bqjm&m=)1xViHNF?(;+5!Xzr4$tQ0O)=ppThOt z1UN}6)=RSjR?kMW^c4AKfShKzSIvV!j<(9HJCAzEIX3)DZlN022R`Jl^nAavr={(4 zvE;CSX`s9VR3h_NVA=U)QRdNgNOX;qde#dF4qU&(wMh08DWc^Ck3GrY(E#cv8`EhE6BQ-M6+xep{d z%%DZR!V3pZlv->WSxJTNH7JS~MNW&il7mQD7+kK9eGlrI;s1k}GYs!t1z z0nFU)_UdKVMBt*Iqa}YL9!U{I6{;vR&v}vm+8456X*NLqtqJ5s_K6XgQTu36{`FWN zM5*FFK0^C}8_B(x7f?2718jb&)n}R;0pw2?f5{JRLX={C{%RPfWbz_aBJ|%4?07!0 zIIEfod3(1YN^SlLd1T(a$ei@b*CUUWAlP1zOuqHb1VkyXH8r7vkAW27bm*Wl*l-|# z%n_L{sk5850D3(i~(WCDoK-#W|^Rkqd+R@D(Onl#=!*cH2nhr9NH?wbQL*~ zBKiw?f*Q|_fa~TO*f#owX$iY zhKGB^hst;&T;{a+6C*bh9qg~o zP3E-hzx?;EyEN3W$W8UWA!FI?=<-zj&hc9c{p$%TBLKOa+#Od3``!(#yc|P~O>5F6 z1?~*r6UM@&Q)jV($JGf1xyif+F5qsen1Wtg-X9&@Y4C-tJ1%}xs1G@~! zD>Pu){=kM9%sY1D+I4I=j+l)+6ULpKiT4P=ew56FCV}YL^^5`O&1VM@V4%n=I|sf5 zHx<1sU`6$Prs`?|keHIt5uo}~6uiM+QL)F-c!o+OP>z-jG(~Kky2|^(iBvmA!$D~o zjq23Na8mkR$MGak+PA3kkG6h>aXGh65XX079t$sNG0X=RblXWg2d;iQ-bl%}nLl6R zLQWn3o+mF9e=S{Jy^RSL1od^LOkhf;=|228{Em&EQW3#YL65=a6z~NXfc#^?Ci|S5^#rbtI^;U?? z9F71#x~nT}s02_7iR*`>(>ciCmW%q63@)T7Nm;}5#-m>hAQ;dOeGGf51Wn(*nW5W$ z?gV_DJ|$$!nxbeXvp&aZk;nn^CEYVui<2DWNlX)0aj;Ev7)6%1KQJ*}@O9ex zCj(MEENdoACj20b!@ur>XPm_N>z9CdXrlJ4{ng!K(h_%5N#pEgWyYUM>3mPQZURK- zYc-p5$f?k+J|t;H(4_VowtG55wjbc9xN-L4Z1E@iyw@R`f7}<3z2AS)*4=qollXj} zgHS6mF!_Y4I|htf*Lv}FmX<_K(k*s5cA@4yx_H@i5%THnmM^>(#BvLC%Yr%n_zdAk zWataGEOJ{R7Up|v>9OAc?_Nr_@ix~5#Ryw#^D;yD7t`g2^TDn6)EpEb+4pBx zyZhJm(|h4T58WfDNxGNRt^ZcghQ2ng&&w_R%>PT^H7`(6F_O-1q=ph*UgY#k+r?%G z6Q^f1$cS#FmTj*b{UE7Q_(OH+siU5_!*cT+w?|lYro=C-7g#nbFO>sd0LT8aC3(9P zDLM21Byp7YO}9;WQ%0~@OUq{HE^0OH1tlNDJ|8&hbvSo@?`s+5UKW`U>={>wp=HPO zM=_KOeV3O2@Y{@w)G()af00KP6lv(b!w)de`Mq>J!MXm%WF(Wd!Lx4ghMI+ynvL6s zd;V$yzZ;!Wy1MxI#M0)}hBtY7cY`?rTIvh}fE6zfM+C~#r9Aldp%5 YR^oT=?ju>j{|c6qe)qaU($Me!0S`F1f&c&j literal 0 HcmV?d00001 diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json new file mode 100644 index 0000000..a8e6971 --- /dev/null +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -0,0 +1,58 @@ +{ + "jsonid": "input_profile", + "controller_type": "slimevr_virtual_controller", + "device_class": "TrackedDeviceClass_Controller", + "resource_root": "slimevr", + "driver_name": "slimevr", + "input_bindingui_mode": "controller_handed", + "should_show_binding_errors": true, + "input_bindingui_left": { + "image": "{slimevr}/icons/slimevr_tracker_icon.png" + }, + "input_bindingui_right": { + "image": "{slimevr}/icons/slimevr_tracker_icon.png" + }, + "input_source": { + "/pose/raw" : { + "type" : "pose", + "binding_image_point" : [50,50] + }, + "/output/haptic": { + "type": "vibration", + "binding_image_point": [50,50] + }, + "/input/tap": { + "type": "button", + "click": true, + "touch" : false, + "binding_image_point": [50,50], + "order" : 1 + }, + "/input/double_tap": { + "type": "button", + "click": true, + "touch" : false, + "binding_image_point": [50,50], + "order" : 2 + }, + "/input/triple_tap": { + "type": "button", + "click": true, + "touch" : false, + "binding_image_point": [50,50], + "order" : 3 + }, + "/input/skeleton/left" : { + "type" : "skeleton", + "skeleton": "/skeleton/hand/left", + "side" : "left", + "binding_image_point" : [50,50] + }, + "/input/skeleton/right" : { + "type" : "skeleton", + "skeleton": "/skeleton/hand/right", + "side" : "right", + "binding_image_point" : [50,50] + } + } +} diff --git a/driver/slimevr/resources/localization/localization.json b/driver/slimevr/resources/localization/localization.json index d85396f..8e1ecab 100644 --- a/driver/slimevr/resources/localization/localization.json +++ b/driver/slimevr/resources/localization/localization.json @@ -1,6 +1,19 @@ [ { "language_tag": "en_US", + "/input/a": "A Button", + "/input/b": "B Button", + "/input/system": "System Button", + "/input/trackpad": "Trackpad", + "/input/joystick": "Joystick", + "/input/skeleton": "Skeleton", + "/input/trigger": "Trigger", + "/output/haptic": "Haptic", + "slimevr": "SlimeVR", + "slimevr_virtual_controller": "SlimeVR Virtual Controller", + "/input/tap": "Single tap", + "/input/double_tap": "Double tap", + "/input/triple_tap": "Triple tap", "slimevr_tracker" : "SlimeVR Tracker", "slimevr_tracker_left_foot" : "SlimeVR Tracker on Left Foot", "slimevr_tracker_right_foot" : "SlimeVR Tracker on Right Foot", diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 5efa691..3defcfa 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -1,9 +1,13 @@ #include "TrackerDevice.hpp" -SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role): +SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role, bool fingertracking_enabled): serial_(serial), tracker_role_(tracker_role), device_id_(device_id), + is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), + is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), + fingertracking_enabled_real_(fingertracking_enabled && (is_left_hand_ || is_right_hand_)), + is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER), last_pose_(MakeDefaultPose()), last_pose_atomic_(MakeDefaultPose()) { } @@ -79,6 +83,36 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) pose.qWorldFromDriverRotation.z = 0; } + if (is_controller_) { + // Set inputs + // TODO + //GetDriver()->GetInput()->UpdateBooleanComponent(this->tap_component_, false, 0); + } + + if (fingertracking_enabled_real_) { + // Set finger rotations + vr::VRBoneTransform_t finger_skeleton_[31]{}; + for (int i = 0; i < position.finger_bone_rotations_size(); i++) + { + // Get data from protobuf + auto fingerData = position.finger_bone_rotations(i); + int fingerBoneName = fingerData.name(); + + // Map from our 15 bones to OpenVR's 31 bones + int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; + finger_skeleton_[boneIndex].orientation = { + fingerData.w(), + fingerData.x(), + fingerData.y(), + fingerData.z() + }; + } + + // Update the finger skeleton for this hand. With and without controller are the same. + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); + } + pose.deviceIsConnected = true; pose.poseIsValid = true; pose.result = vr::ETrackingResult::TrackingResult_Running_OK; @@ -155,19 +189,84 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, "SlimeVR"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); + // Set up a model "number" (not needed but good to have) + if (is_controller_) { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); + } else { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); + } - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); + // Hand selection + if (is_left_hand_) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + } else if (is_right_hand_) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + } else { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); + } - // Some device properties will be derived at runtime by SteamVR - // using the profile, such as the device class and controller type - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_tracker_profile.json"); + // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) + if (is_controller_) { + vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); + } else { + vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); + } - // Doesn't apply until restart of SteamVR - auto role = GetViveRole(tracker_role_); - if (role != "") { - vr::VRSettings()->SetString(vr::k_pch_Trackers_Section, ("/devices/slimevr/" + serial_).c_str(), role.c_str()); + // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) + if (is_controller_) { + vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); + } else { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); + } + + // Set the icons + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{slimevr}/icons/tracker_status_off.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); + + // Set inputs + if (is_controller_) { + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/tap", &this->tap_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap", &this->double_tap_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap", &this->triple_tap_component_); + + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + } + + // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) + if (!is_controller_) { + auto role_hint = GetViveRoleHint(tracker_role_); + if (role_hint != "") { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, role_hint.c_str()); + } + + auto role = GetViveRole(tracker_role_); + if (role != "") { + vr::VRSettings()->SetString(vr::k_pch_Trackers_Section, ("/devices/slimevr/" + serial_).c_str(), role.c_str()); + } + } + + // Setup skeletal input for fingertracking + if (fingertracking_enabled_real_) { + vr::VRDriverInput()->CreateSkeletonComponent( + props, + is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", + is_right_hand_ ? "/skeleton/hand/right" : "/skeleton/hand/left", + "/pose/raw", + vr::EVRSkeletalTrackingLevel::VRSkeletalTracking_Full, + NULL, // Fist + 31, + &skeletal_component_handle_); + + // Update the skeleton so steamvr knows we have an active skeletal input device + vr::VRBoneTransform_t finger_skeleton_[31]{}; + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); } return vr::EVRInitError::VRInitError_None; diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 7653655..dc57008 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -19,7 +19,7 @@ namespace SlimeVRDriver { class TrackerDevice : public IVRDevice { public: - TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); + TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role, bool fingertracking_enabled); ~TrackerDevice() = default; // Inherited via IVRDevice @@ -49,6 +49,7 @@ namespace SlimeVRDriver { int device_id_; TrackerRole tracker_role_; + bool fingertracking_enabled_real_; vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); @@ -57,7 +58,32 @@ namespace SlimeVRDriver { float vibrate_anim_state_ = 0.f; vr::VRInputComponentHandle_t haptic_component_ = 0; - vr::VRInputComponentHandle_t system_click_component_ = 0; - vr::VRInputComponentHandle_t system_touch_component_ = 0; + vr::VRInputComponentHandle_t tap_component_ = 0; + vr::VRInputComponentHandle_t double_tap_component_ = 0; + vr::VRInputComponentHandle_t triple_tap_component_ = 0; + + bool is_controller_; + bool is_left_hand_; + bool is_right_hand_; + + vr::VRInputComponentHandle_t skeletal_component_handle_; + + const int protobuf_fingers_to_openvr[15] = { + 2, // THUMB_METACARPAL → eBone_Thumb1 + 3, // THUMB_PROXIMAL → eBone_Thumb2 + 4, // THUMB_DISTAL → eBone_Thumb3 + 6, // INDEX_PROXIMAL → eBone_IndexFinger1 + 7, // INDEX_INTERMEDIATE → eBone_IndexFinger2 + 8, // INDEX_DISTAL → eBone_IndexFinger3 + 11, // MIDDLE_PROXIMAL → eBone_MiddleFinger1 + 12, // MIDDLE_INTERMEDIATE → eBone_MiddleFinger2 + 13, // MIDDLE_DISTAL → eBone_MiddleFinger3 + 16, // RING_PROXIMAL → eBone_RingFinger1 + 17, // RING_INTERMEDIATE → eBone_RingFinger2 + 18, // RING_DISTAL → eBone_RingFinger3 + 21, // LITTLE_PROXIMAL → eBone_PinkyFinger1 + 22, // LITTLE_INTERMEDIATE → eBone_PinkyFinger2 + 23 // LITTLE_DISTAL → eBone_PinkyFinger3 + }; }; }; \ No newline at end of file diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 9d8dcd2..e2415ba 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -212,12 +212,7 @@ void SlimeVRDriver::VRDriver::OnBridgeMessage(const messages::ProtobufMessage& m std::lock_guard lock(devices_mutex_); if (message.has_tracker_added()) { messages::TrackerAdded ta = message.tracker_added(); - switch(GetDeviceType(static_cast(ta.tracker_role()))) { - case DeviceType::TRACKER: - case DeviceType::CONTROLLER: - AddDevice(std::make_shared(ta.tracker_serial(), ta.tracker_id(), static_cast(ta.tracker_role()))); - break; - } + AddDevice(std::make_shared(ta.tracker_serial(), ta.tracker_id(), static_cast(ta.tracker_role()), ta.fingertracking_enabled())); } else if (message.has_position()) { messages::Position pos = message.position(); auto device = devices_by_id_.find(pos.tracker_id()); From fcfe3c361e538d6a16f08db95146333feaeedc42 Mon Sep 17 00:00:00 2001 From: Erimel Date: Thu, 3 Jul 2025 21:27:57 -0400 Subject: [PATCH 003/117] Add version and input support via protobuf --- src/TrackerDevice.cpp | 31 ++++++++++++++++++++++++------- src/TrackerDevice.hpp | 4 ++-- src/VRDriver.cpp | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 3defcfa..1e22085 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -1,12 +1,12 @@ #include "TrackerDevice.hpp" -SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role, bool fingertracking_enabled): +SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role): serial_(serial), tracker_role_(tracker_role), device_id_(device_id), is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), - fingertracking_enabled_real_(fingertracking_enabled && (is_left_hand_ || is_right_hand_)), + fingertracking_enabled_(is_left_hand_ || is_right_hand_), is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER), last_pose_(MakeDefaultPose()), last_pose_atomic_(MakeDefaultPose()) @@ -84,12 +84,28 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) } if (is_controller_) { + bool tap = false; + bool double_tap = false; + bool triple_tap = false; + + // Get inputs from protobuf + for (int i = 0; i < position.input_size(); ++i) { + if (position.input(i).type() == messages::Input_InputType_TAP) { + tap = true; + } else if (position.input(i).type() == messages::Input_InputType_DOUBLE_TAP) { + double_tap = true; + } else if (position.input(i).type() == messages::Input_InputType_TRIPLE_TAP) { + triple_tap = true; + } + } + // Set inputs - // TODO - //GetDriver()->GetInput()->UpdateBooleanComponent(this->tap_component_, false, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->tap_component_, tap, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); } - if (fingertracking_enabled_real_) { + if (fingertracking_enabled_) { // Set finger rotations vr::VRBoneTransform_t finger_skeleton_[31]{}; for (int i = 0; i < position.finger_bone_rotations_size(); i++) @@ -108,7 +124,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) }; } - // Update the finger skeleton for this hand. With and without controller are the same. + // Update the finger skeleton for this hand. With and without controller have the same pose. vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); } @@ -208,6 +224,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) if (is_controller_) { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. } else { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); } @@ -252,7 +269,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { } // Setup skeletal input for fingertracking - if (fingertracking_enabled_real_) { + if (fingertracking_enabled_) { vr::VRDriverInput()->CreateSkeletonComponent( props, is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index dc57008..bbcd347 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -19,7 +19,7 @@ namespace SlimeVRDriver { class TrackerDevice : public IVRDevice { public: - TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role, bool fingertracking_enabled); + TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); ~TrackerDevice() = default; // Inherited via IVRDevice @@ -49,7 +49,7 @@ namespace SlimeVRDriver { int device_id_; TrackerRole tracker_role_; - bool fingertracking_enabled_real_; + bool fingertracking_enabled_; vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index e2415ba..493586b 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -212,7 +212,7 @@ void SlimeVRDriver::VRDriver::OnBridgeMessage(const messages::ProtobufMessage& m std::lock_guard lock(devices_mutex_); if (message.has_tracker_added()) { messages::TrackerAdded ta = message.tracker_added(); - AddDevice(std::make_shared(ta.tracker_serial(), ta.tracker_id(), static_cast(ta.tracker_role()), ta.fingertracking_enabled())); + AddDevice(std::make_shared(ta.tracker_serial(), ta.tracker_id(), static_cast(ta.tracker_role()))); } else if (message.has_position()) { messages::Position pos = message.position(); auto device = devices_by_id_.find(pos.tracker_id()); From 1cd2fe84ca7d5885014050cebae201899b645dad Mon Sep 17 00:00:00 2001 From: Erimel Date: Fri, 11 Jul 2025 21:13:24 -0400 Subject: [PATCH 004/117] WIP working on inputs --- .../input/slimevr_controller_bindings.json | 31 +++++----- .../input/vrcompositor_bindings_slimevr.json | 62 +++++++++++++++++++ .../resources/localization/localization.json | 1 - src/TrackerDevice.cpp | 16 ++--- src/TrackerDevice.hpp | 1 - 5 files changed, 82 insertions(+), 29 deletions(-) create mode 100644 driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index a8e6971..1c7c276 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -15,44 +15,43 @@ "input_source": { "/pose/raw" : { "type" : "pose", - "binding_image_point" : [50,50] + "binding_image_point" : [80,80] }, "/output/haptic": { "type": "vibration", - "binding_image_point": [50,50] - }, - "/input/tap": { - "type": "button", - "click": true, - "touch" : false, - "binding_image_point": [50,50], - "order" : 1 + "binding_image_point": [80,80] }, "/input/double_tap": { "type": "button", "click": true, "touch" : false, - "binding_image_point": [50,50], - "order" : 2 + "binding_image_point": [80,80], + "order" : 1 }, "/input/triple_tap": { "type": "button", "click": true, "touch" : false, - "binding_image_point": [50,50], - "order" : 3 + "binding_image_point": [80,80], + "order" : 2 }, "/input/skeleton/left" : { "type" : "skeleton", "skeleton": "/skeleton/hand/left", "side" : "left", - "binding_image_point" : [50,50] + "binding_image_point" : [80,80] }, "/input/skeleton/right" : { "type" : "skeleton", "skeleton": "/skeleton/hand/right", "side" : "right", - "binding_image_point" : [50,50] + "binding_image_point" : [80,80] + } + }, + "default_bindings": [ + { + "app_key": "openvr.component.vrcompositor", + "binding_url": "vrcompositor_bindings_slimevr.json" } - } + ] } diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json new file mode 100644 index 0000000..364b1d7 --- /dev/null +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -0,0 +1,62 @@ +{ + "action_manifest_version": 0, + "alias_info": {}, + "app_key": "openvr.component.vrcompositor", + "bindings": { + "/actions/lasermouse": { + "sources": [ + { + "inputs": { + "click": { + "output": "/actions/lasermouse/in/LeftClick" + } + }, + "mode": "button", + "parameters": {}, + "path": "/user/hand/left/input/double_tap" + }, + { + "inputs": { + "click": { + "output": "/actions/lasermouse/in/LeftClick" + } + }, + "mode": "button", + "parameters": {}, + "path": "/user/hand/right/input/double_tap" + } + ] + }, + "/actions/system": { + "sources": [ + { + "inputs": { + "click": { + "output": "/actions/system/in/ToggleDashboard" + } + }, + "mode": "button", + "parameters": {}, + "path": "/user/hand/left/input/triple_tap" + }, + { + "inputs": { + "click": { + "output": "/actions/system/in/ToggleDashboard" + } + }, + "mode": "button", + "parameters": {}, + "path": "/user/hand/right/input/triple_tap" + } + ] + } + }, + "category": "steamvr_input", + "controller_type": "slimevr_virtual_controller", + "description": "Triple tap to toggle the dashboard. Double tap to click with the laser mouse.", + "interaction_profile": "", + "name": "Default SteamVR Dashboard bindings for SlimeVR", + "options": {}, + "simulated_actions": [] +} \ No newline at end of file diff --git a/driver/slimevr/resources/localization/localization.json b/driver/slimevr/resources/localization/localization.json index 8e1ecab..75560ce 100644 --- a/driver/slimevr/resources/localization/localization.json +++ b/driver/slimevr/resources/localization/localization.json @@ -11,7 +11,6 @@ "/output/haptic": "Haptic", "slimevr": "SlimeVR", "slimevr_virtual_controller": "SlimeVR Virtual Controller", - "/input/tap": "Single tap", "/input/double_tap": "Double tap", "/input/triple_tap": "Triple tap", "slimevr_tracker" : "SlimeVR Tracker", diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 1e22085..59c61f3 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -84,15 +84,11 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) } if (is_controller_) { - bool tap = false; + // Get inputs from protobuf bool double_tap = false; bool triple_tap = false; - - // Get inputs from protobuf for (int i = 0; i < position.input_size(); ++i) { - if (position.input(i).type() == messages::Input_InputType_TAP) { - tap = true; - } else if (position.input(i).type() == messages::Input_InputType_DOUBLE_TAP) { + if (position.input(i).type() == messages::Input_InputType_DOUBLE_TAP) { double_tap = true; } else if (position.input(i).type() == messages::Input_InputType_TRIPLE_TAP) { triple_tap = true; @@ -100,7 +96,6 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) } // Set inputs - GetDriver()->GetInput()->UpdateBooleanComponent(this->tap_component_, tap, 0); GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); } @@ -248,11 +243,10 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set inputs if (is_controller_) { - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/tap", &this->tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap", &this->double_tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap", &this->triple_tap_component_); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index bbcd347..5425f44 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -58,7 +58,6 @@ namespace SlimeVRDriver { float vibrate_anim_state_ = 0.f; vr::VRInputComponentHandle_t haptic_component_ = 0; - vr::VRInputComponentHandle_t tap_component_ = 0; vr::VRInputComponentHandle_t double_tap_component_ = 0; vr::VRInputComponentHandle_t triple_tap_component_ = 0; From 898078b361cc2a60c2921eee3c4ad4c42b905b4e Mon Sep 17 00:00:00 2001 From: Erimel Date: Thu, 6 Nov 2025 18:43:28 -0500 Subject: [PATCH 005/117] get finger bone index properly --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 59c61f3..b2bff05 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -107,7 +107,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) { // Get data from protobuf auto fingerData = position.finger_bone_rotations(i); - int fingerBoneName = fingerData.name(); + int fingerBoneName = static_cast(fingerData.name()); // Map from our 15 bones to OpenVR's 31 bones int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; From f0de33ff7284efc1f1294e724f53eddc1eca3370 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 18 Nov 2025 23:17:42 -0600 Subject: [PATCH 006/117] Add initial structures for allowing full controller input forwarding from SlimeVR server. --- .../input/slimevr_controller_bindings.json | 108 +++- src/TrackerDevice.cpp | 585 ++++++++++-------- 2 files changed, 426 insertions(+), 267 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 1c7c276..d39701f 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -13,39 +13,111 @@ "image": "{slimevr}/icons/slimevr_tracker_icon.png" }, "input_source": { - "/pose/raw" : { - "type" : "pose", - "binding_image_point" : [80,80] + "/pose/raw": { + "type": "pose", + "binding_image_point": [ 80, 80 ] }, "/output/haptic": { "type": "vibration", - "binding_image_point": [80,80] + "binding_image_point": [ 80, 80 ] }, "/input/double_tap": { "type": "button", "click": true, - "touch" : false, - "binding_image_point": [80,80], - "order" : 1 + "touch": false, + "binding_image_point": [ 80, 80 ], + "order": 1 }, "/input/triple_tap": { "type": "button", "click": true, - "touch" : false, - "binding_image_point": [80,80], - "order" : 2 + "touch": false, + "binding_image_point": [ 80, 80 ], + "order": 2 }, - "/input/skeleton/left" : { - "type" : "skeleton", + "/input/skeleton/left": { + "type": "skeleton", "skeleton": "/skeleton/hand/left", - "side" : "left", - "binding_image_point" : [80,80] + "side": "left", + "binding_image_point": [ 80, 80 ] }, - "/input/skeleton/right" : { - "type" : "skeleton", + "/input/skeleton/right": { + "type": "skeleton", "skeleton": "/skeleton/hand/right", - "side" : "right", - "binding_image_point" : [80,80] + "side": "right", + "binding_image_point": [ 80, 80 ] + }, + "/input/x/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/y/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/a/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/b/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/left/trigger": { + "type": "trigger", + "binding_image_point": [ 80, 80 ] + }, + "/input/right/trigger": { + "type": "trigger", + "binding_image_point": [ 80, 80 ] + }, + "/input/left/stick/x": { + "type": "scalar", + "binding_image_point": [ 80, 80 ] + }, + "/input/left/stick/y": { + "type": "scalar", + "binding_image_point": [ 80, 80 ] + }, + "/input/left/stick/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/right/stick/x": { + "type": "scalar", + "binding_image_point": [ 80, 80 ] + }, + "/input/right/stick/y": { + "type": "scalar", + "binding_image_point": [ 80, 80 ] + }, + "/input/right/stick/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/menu/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/recenter/click": { + "type": "button", + "click": true, + "binding_image_point": [ 80, 80 ] + }, + "/input/left/grip": { + "type": "trigger", + "binding_image_point": [ 80, 80 ] + }, + "/input/right/grip": { + "type": "trigger", + "binding_image_point": [ 80, 80 ] } }, "default_bindings": [ diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index b2bff05..ca7b709 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -1,63 +1,64 @@ #include "TrackerDevice.hpp" -SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role): - serial_(serial), - tracker_role_(tracker_role), - device_id_(device_id), - is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), - is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), - fingertracking_enabled_(is_left_hand_ || is_right_hand_), - is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER), - last_pose_(MakeDefaultPose()), - last_pose_atomic_(MakeDefaultPose()) -{ } +SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role) : + serial_(serial), + tracker_role_(tracker_role), + device_id_(device_id), + is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), + is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), + fingertracking_enabled_(is_left_hand_ || is_right_hand_), + is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER), + last_pose_(MakeDefaultPose()), + last_pose_atomic_(MakeDefaultPose()) +{ +} std::string SlimeVRDriver::TrackerDevice::GetSerial() { - return serial_; + return serial_; } void SlimeVRDriver::TrackerDevice::Update() { - if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; - - // Check if this device was asked to be identified - auto& events = GetDriver()->GetOpenVREvents(); - for (const auto& event : events) { - // Note here, event.trackedDeviceIndex does not necessarily equal device_index_, not sure why, but the component handle will match so we can just use that instead - //if (event.trackedDeviceIndex == device_index_) { - if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { - if (event.data.hapticVibration.componentHandle == haptic_component_) { - did_vibrate_ = true; - } - } - //} - } - - // Check if we need to keep vibrating - if (did_vibrate_) { - vibrate_anim_state_ += GetDriver()->GetLastFrameTime().count() / 1000.f; - if (vibrate_anim_state_ > 1.0f) { - did_vibrate_ = false; - vibrate_anim_state_ = 0.0f; - } - } + if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; + + // Check if this device was asked to be identified + auto events = GetDriver()->GetOpenVREvents(); + for (auto event : events) { + // Note here, event.trackedDeviceIndex does not necessarily equal device_index_, not sure why, but the component handle will match so we can just use that instead + //if (event.trackedDeviceIndex == device_index_) { + if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { + if (event.data.hapticVibration.componentHandle == haptic_component_) { + did_vibrate_ = true; + } + } + //} + } + + // Check if we need to keep vibrating + if (did_vibrate_) { + vibrate_anim_state_ += GetDriver()->GetLastFrameTime().count() / 1000.f; + if (vibrate_anim_state_ > 1.0f) { + did_vibrate_ = false; + vibrate_anim_state_ = 0.0f; + } + } } -void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) { - if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; +void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) { + if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; - // Setup pose for this frame - auto pose = last_pose_; - //send the new position and rotation from the pipe to the tracker object - if (position.has_x()) { - pose.vecPosition[0] = position.x(); - pose.vecPosition[1] = position.y(); - pose.vecPosition[2] = position.z(); - } + // Setup pose for this frame + auto pose = last_pose_; + //send the new position and rotation from the pipe to the tracker object + if (position.has_x()) { + pose.vecPosition[0] = position.x(); + pose.vecPosition[1] = position.y(); + pose.vecPosition[2] = position.z(); + } - pose.qRotation.w = position.qw(); - pose.qRotation.x = position.qx(); - pose.qRotation.y = position.qy(); - pose.qRotation.z = position.qz(); + pose.qRotation.w = position.qw(); + pose.qRotation.x = position.qx(); + pose.qRotation.y = position.qy(); + pose.qRotation.z = position.qz(); if (position.has_vx()) { pose.vecVelocity[0] = position.vx(); @@ -72,242 +73,328 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position &position) if (current_universe.has_value()) { auto trans = current_universe.value(); - // TODO: set this once, somewhere? - pose.vecWorldFromDriverTranslation[0] = -trans.translation.v[0]; - pose.vecWorldFromDriverTranslation[1] = -trans.translation.v[1]; - pose.vecWorldFromDriverTranslation[2] = -trans.translation.v[2]; - - pose.qWorldFromDriverRotation.w = cos(trans.yaw / 2); - pose.qWorldFromDriverRotation.x = 0; - pose.qWorldFromDriverRotation.y = sin(trans.yaw / 2); - pose.qWorldFromDriverRotation.z = 0; - } - - if (is_controller_) { - // Get inputs from protobuf - bool double_tap = false; - bool triple_tap = false; - for (int i = 0; i < position.input_size(); ++i) { - if (position.input(i).type() == messages::Input_InputType_DOUBLE_TAP) { - double_tap = true; - } else if (position.input(i).type() == messages::Input_InputType_TRIPLE_TAP) { - triple_tap = true; - } - } - - // Set inputs - GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); - } - - if (fingertracking_enabled_) { - // Set finger rotations - vr::VRBoneTransform_t finger_skeleton_[31]{}; - for (int i = 0; i < position.finger_bone_rotations_size(); i++) - { - // Get data from protobuf - auto fingerData = position.finger_bone_rotations(i); - int fingerBoneName = static_cast(fingerData.name()); - - // Map from our 15 bones to OpenVR's 31 bones - int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; - finger_skeleton_[boneIndex].orientation = { - fingerData.w(), - fingerData.x(), - fingerData.y(), - fingerData.z() - }; - } - - // Update the finger skeleton for this hand. With and without controller have the same pose. - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); - } - - pose.deviceIsConnected = true; - pose.poseIsValid = true; - pose.result = vr::ETrackingResult::TrackingResult_Running_OK; - - // Notify SteamVR that pose was updated - last_pose_atomic_ = (last_pose_ = pose); - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); + // TODO: set this once, somewhere? + pose.vecWorldFromDriverTranslation[0] = -trans.translation.v[0]; + pose.vecWorldFromDriverTranslation[1] = -trans.translation.v[1]; + pose.vecWorldFromDriverTranslation[2] = -trans.translation.v[2]; + + pose.qWorldFromDriverRotation.w = cos(trans.yaw / 2); + pose.qWorldFromDriverRotation.x = 0; + pose.qWorldFromDriverRotation.y = sin(trans.yaw / 2); + pose.qWorldFromDriverRotation.z = 0; + } + + if (is_controller_) { + // Get inputs from protobuf + bool double_tap = false; + bool triple_tap = false; + bool x_pressed = false; + bool y_pressed = false; + bool a_pressed = false; + bool b_pressed = false; + bool stick_click = false; + float stick_x = 0.0f; + float stick_y = 0.0f; + float trigger = 0.0f; + float grip = 0.0f; + + for (int i = 0; i < position.input_size(); ++i) { + const auto& input = position.input(i); + switch (input.type()) { + case messages::Input_InputType_DOUBLE_TAP: + double_tap = true; + break; + case messages::Input_InputType_TRIPLE_TAP: + triple_tap = true; + break; + case messages::Input_InputType_TRIGGER: + trigger = input.value(); + break; + case messages::Input_InputType_GRIP: + grip = input.value(); + break; + case messages::Input_InputType_BUTTON_X: + x_pressed = true; + break; + case messages::Input_InputType_BUTTON_Y: + y_pressed = true; + break; + case messages::Input_InputType_BUTTON_A: + a_pressed = true; + break; + case messages::Input_InputType_BUTTON_B: + b_pressed = true; + break; + case messages::Input_InputType_STICK_X: + stick_x = input.value(); + break; + case messages::Input_InputType_STICK_Y: + stick_y = input.value(); + break; + case messages::Input_InputType_STICK_CLICK: + stick_click = true; + break; + } + } + + // Set inputs + GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); + + if (is_left_hand_) { + GetDriver()->GetInput()->UpdateScalarComponent(left_trigger_component_, trigger, 0); + GetDriver()->GetInput()->UpdateScalarComponent(left_grip_value_component_, grip, 0); + GetDriver()->GetInput()->UpdateScalarComponent(left_stick_x_component_, stick_x, 0); + GetDriver()->GetInput()->UpdateScalarComponent(left_stick_y_component_, stick_y, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_x_component_, x_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_y_component_, y_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(left_stick_click_component_, stick_click, 0); + } + else if (is_right_hand_) { + GetDriver()->GetInput()->UpdateScalarComponent(right_trigger_component_, trigger, 0); + GetDriver()->GetInput()->UpdateScalarComponent(right_grip_value_component_, grip, 0); + GetDriver()->GetInput()->UpdateScalarComponent(right_stick_x_component_, stick_x, 0); + GetDriver()->GetInput()->UpdateScalarComponent(right_stick_y_component_, stick_y, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, a_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, b_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); + } + } + + if (fingertracking_enabled_) { + // Set finger rotations + vr::VRBoneTransform_t finger_skeleton_[31]{}; + for (int i = 0; i < position.finger_bone_rotations_size(); i++) + { + // Get data from protobuf + auto fingerData = position.finger_bone_rotations(i); + int fingerBoneName = static_cast(fingerData.name()); + + // Map from our 15 bones to OpenVR's 31 bones + int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; + finger_skeleton_[boneIndex].orientation = { + fingerData.w(), + fingerData.x(), + fingerData.y(), + fingerData.z() + }; + } + + // Update the finger skeleton for this hand. With and without controller have the same pose. + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); + } + + pose.deviceIsConnected = true; + pose.poseIsValid = true; + pose.result = vr::ETrackingResult::TrackingResult_Running_OK; + + // Notify SteamVR that pose was updated + last_pose_atomic_ = (last_pose_ = pose); + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } -void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery &battery) { - if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) - return; +void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { + if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; - // Get the properties handle - auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); + // Get the properties handle + auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); - vr::ETrackedPropertyError err; + vr::ETrackedPropertyError err; - // Set that the tracker reports battery level in case it has not already been set to true - // It's a given that the tracker supports reporting battery life because otherwise a BatteryMessage would not be received - if (vr::VRProperties()->GetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err) != true) { - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); - } + // Set that the tracker reports battery level in case it has not already been set to true + // It's a given that the tracker supports reporting battery life because otherwise a BatteryMessage would not be received + if (vr::VRProperties()->GetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err) != true) { + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); + } - if (battery.is_charging()) { - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, true); - } else { - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, false); - } - - // Set the battery Level; 0 = 0%, 1 = 100% - vr::VRProperties()->SetFloatProperty(props, vr::Prop_DeviceBatteryPercentage_Float, battery.battery_level()); -} + if (battery.is_charging()) { + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, true); + } + else { + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, false); + } -void SlimeVRDriver::TrackerDevice::StatusMessage(messages::TrackerStatus &status) { - if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; - - vr::DriverPose_t pose = last_pose_; - switch (status.status()) { - case messages::TrackerStatus_Status_OK: - pose.deviceIsConnected = true; - pose.poseIsValid = true; - break; - case messages::TrackerStatus_Status_DISCONNECTED: - pose.deviceIsConnected = false; - pose.poseIsValid = false; - break; - case messages::TrackerStatus_Status_ERROR: - case messages::TrackerStatus_Status_BUSY: - default: - pose.deviceIsConnected = true; - pose.poseIsValid = false; - break; - } + // Set the battery Level; 0 = 0%, 1 = 100% + vr::VRProperties()->SetFloatProperty(props, vr::Prop_DeviceBatteryPercentage_Float, battery.battery_level()); +} - // TODO: send position/rotation of 0 instead of last pose? - - last_pose_atomic_ = (last_pose_ = pose); - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); +void SlimeVRDriver::TrackerDevice::StatusMessage(messages::TrackerStatus& status) { + if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; + + vr::DriverPose_t pose = last_pose_; + switch (status.status()) { + case messages::TrackerStatus_Status_OK: + pose.deviceIsConnected = true; + pose.poseIsValid = true; + break; + case messages::TrackerStatus_Status_DISCONNECTED: + pose.deviceIsConnected = false; + pose.poseIsValid = false; + break; + case messages::TrackerStatus_Status_ERROR: + case messages::TrackerStatus_Status_BUSY: + default: + pose.deviceIsConnected = true; + pose.poseIsValid = false; + break; + } + + // TODO: send position/rotation of 0 instead of last pose? + + last_pose_atomic_ = (last_pose_ = pose); + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } DeviceType SlimeVRDriver::TrackerDevice::GetDeviceType() { - return DeviceType::TRACKER; + return DeviceType::TRACKER; } vr::TrackedDeviceIndex_t SlimeVRDriver::TrackerDevice::GetDeviceIndex() { - return device_index_; + return device_index_; } vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { - device_index_ = unObjectId; - - logger_->Log("Activating tracker {}", serial_); - - auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); - - // Set up a model "number" (not needed but good to have) - if (is_controller_) { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); - } else { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); - } - - // Hand selection - if (is_left_hand_) { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); - } else if (is_right_hand_) { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); - } else { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); - } - - // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) - if (is_controller_) { - vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. - } else { - vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); - } - - // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) - if (is_controller_) { - vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); - } else { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); - } - - // Set the icons - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{slimevr}/icons/tracker_status_ready.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{slimevr}/icons/tracker_status_off.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{slimevr}/icons/tracker_status_ready.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); - - // Set inputs - if (is_controller_) { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); - - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - } - - // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) - if (!is_controller_) { - auto role_hint = GetViveRoleHint(tracker_role_); - if (role_hint != "") { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, role_hint.c_str()); - } - - auto role = GetViveRole(tracker_role_); - if (role != "") { - vr::VRSettings()->SetString(vr::k_pch_Trackers_Section, ("/devices/slimevr/" + serial_).c_str(), role.c_str()); - } - } - - // Setup skeletal input for fingertracking - if (fingertracking_enabled_) { - vr::VRDriverInput()->CreateSkeletonComponent( - props, - is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", - is_right_hand_ ? "/skeleton/hand/right" : "/skeleton/hand/left", - "/pose/raw", - vr::EVRSkeletalTrackingLevel::VRSkeletalTracking_Full, - NULL, // Fist - 31, - &skeletal_component_handle_); - - // Update the skeleton so steamvr knows we have an active skeletal input device - vr::VRBoneTransform_t finger_skeleton_[31]{}; - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); - } - - return vr::EVRInitError::VRInitError_None; + device_index_ = unObjectId; + + logger_->Log("Activating tracker %s", serial_.c_str()); + + // Get the properties handle + auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); + + // Set some universe ID (Must be 2 or higher) + GetDriver()->GetProperties()->SetUint64Property(props, vr::Prop_CurrentUniverseId_Uint64, 4); + + // Set up a model "number" (not needed but good to have) + if (is_controller_) { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); + } + else { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); + } + + // Hand selection + if (is_left_hand_) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + } + else if (is_right_hand_) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + } + else { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); + } + + // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) + if (is_controller_) { + vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. + } + else { + vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); + } + + // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) + if (is_controller_) { + vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); + } + else { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); + } + + // Set the icons + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{slimevr}/icons/tracker_status_off.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); + + // Set inputs + if (is_controller_) { + std::string hand_prefix = is_left_hand_ ? "/input/left/" : "/input/right/"; + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/x/click", &button_x_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &button_y_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &button_a_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &button_b_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &menu_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &system_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "stick/click", &stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "grip/click", &grip_click_component_); + + // Scalar components + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "trigger/value", &trigger_component_handle_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/value", &grip_value_component_handle_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/x", &stick_x_component_handle_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/y", &stick_y_component_handle_); + } + + // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) + if (!is_controller_) { + auto role_hint = GetViveRoleHint(tracker_role_); + if (role_hint != "") { + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, role_hint.c_str()); + } + + auto role = GetViveRole(tracker_role_); + if (role != "") { + vr::VRSettings()->SetString(vr::k_pch_Trackers_Section, ("/devices/slimevr/" + serial_).c_str(), role.c_str()); + } + } + + // Setup skeletal input for fingertracking + if (fingertracking_enabled_) { + vr::VRDriverInput()->CreateSkeletonComponent( + props, + is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", + is_right_hand_ ? "/skeleton/hand/right" : "/skeleton/hand/left", + "/pose/raw", + vr::EVRSkeletalTrackingLevel::VRSkeletalTracking_Full, + NULL, // Fist + 31, + &skeletal_component_handle_); + + // Update the skeleton so steamvr knows we have an active skeletal input device + vr::VRBoneTransform_t finger_skeleton_[31]{}; + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); + } + + return vr::EVRInitError::VRInitError_None; } void SlimeVRDriver::TrackerDevice::Deactivate() { - device_index_ = vr::k_unTrackedDeviceIndexInvalid; + device_index_ = vr::k_unTrackedDeviceIndexInvalid; } void SlimeVRDriver::TrackerDevice::EnterStandby() { } void* SlimeVRDriver::TrackerDevice::GetComponent(const char* pchComponentNameAndVersion) { - return nullptr; + return nullptr; } void SlimeVRDriver::TrackerDevice::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) { - if (unResponseBufferSize >= 1) { - pchResponseBuffer[0] = 0; - } + if (unResponseBufferSize >= 1) { + pchResponseBuffer[0] = 0; + } } vr::DriverPose_t SlimeVRDriver::TrackerDevice::GetPose() { - return last_pose_atomic_; + return last_pose_atomic_; } int SlimeVRDriver::TrackerDevice::GetDeviceId() { - return device_id_; + return device_id_; } void SlimeVRDriver::TrackerDevice::SetDeviceId(int device_id) { - device_id_ = device_id; + device_id_ = device_id; } From 7f9af89a9f3e7d87ab66d3fed7c95324942c332e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 12 Jan 2026 21:31:00 -0600 Subject: [PATCH 007/117] Update protobuffs --- src/bridge/ProtobufMessages.proto | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/bridge/ProtobufMessages.proto b/src/bridge/ProtobufMessages.proto index 30d07d0..a7a7059 100644 --- a/src/bridge/ProtobufMessages.proto +++ b/src/bridge/ProtobufMessages.proto @@ -58,6 +58,48 @@ message Version { int32 protocol_version = 1; } +message FingerBoneRotation { + enum FingerBoneName { + THUMB_METACARPAL = 0; + THUMB_PROXIMAL = 1; + THUMB_DISTAL = 2; + INDEX_PROXIMAL = 3; + INDEX_INTERMEDIATE = 4; + INDEX_DISTAL = 5; + MIDDLE_PROXIMAL = 6; + MIDDLE_INTERMEDIATE = 7; + MIDDLE_DISTAL = 8; + RING_PROXIMAL = 9; + RING_INTERMEDIATE = 10; + RING_DISTAL = 11; + LITTLE_PROXIMAL = 12; + LITTLE_INTERMEDIATE = 13; + LITTLE_DISTAL = 14; + } + FingerBoneName name = 1; + float x = 2; + float y = 3; + float z = 4; + float w = 5; +} + +message Input { + enum InputType { + DOUBLE_TAP = 0; + TRIPLE_TAP = 1; + TRIGGER = 2; + GRIP = 3; + BUTTON_X = 4; + BUTTON_Y = 5; + BUTTON_A = 6; + BUTTON_B = 7; + STICK_X = 8; + STICK_Y = 9; + STICK_CLICK = 10; + } + InputType type = 1; +} + message Position { int32 tracker_id = 1; optional float x = 2; From ebc7d70ce4452bd57a90657f71ad770b3f628bc9 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 16 Jan 2026 21:26:03 -0600 Subject: [PATCH 008/117] Adjust protobuf data for proper controller input support. --- src/bridge/ProtobufMessages.proto | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/bridge/ProtobufMessages.proto b/src/bridge/ProtobufMessages.proto index a7a7059..179446f 100644 --- a/src/bridge/ProtobufMessages.proto +++ b/src/bridge/ProtobufMessages.proto @@ -87,15 +87,14 @@ message Input { enum InputType { DOUBLE_TAP = 0; TRIPLE_TAP = 1; - TRIGGER = 2; - GRIP = 3; - BUTTON_X = 4; - BUTTON_Y = 5; - BUTTON_A = 6; - BUTTON_B = 7; - STICK_X = 8; - STICK_Y = 9; - STICK_CLICK = 10; + BUTTON_1_HELD = 2; + BUTTON_1_UNHELD = 3; + BUTTON_2_HELD = 4; + BUTTON_2_UNHELD = 5; + MENU_RECENTER_HELD = 6; + MENU_RECENTER_UNHELD = 7; + STICK_CLICK_HELD = 9; + STICK_CLICK_UNHELD = 10; } InputType type = 1; } @@ -121,6 +120,18 @@ message Position { optional float vz = 12; } +message ControllerInput { + int32 tracker_id = 1; + float thumbstick_x = 2; + float thumbstick_y = 3; + float trigger = 4; + float grip = 5; + bool button_1 = 6; + bool button_2 = 7; + bool menu_recenter = 8; + bool stick_click = 9; +} + message UserAction { string name = 1; map action_arguments = 2; @@ -168,5 +179,6 @@ message ProtobufMessage { TrackerStatus tracker_status = 4; Battery battery = 5; Version version = 6; + ControllerInput controller_input = 7; } } \ No newline at end of file From c5f9327ead7e033f93e18796bb1f1d373d857d83 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 16 Jan 2026 22:26:50 -0600 Subject: [PATCH 009/117] Add controller input message --- src/TrackerDevice.cpp | 135 +++++++++++++++++++----------------------- src/TrackerDevice.hpp | 1 + src/VRDriver.cpp | 10 +++- 3 files changed, 71 insertions(+), 75 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index ca7b709..91eab4e 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -84,80 +84,17 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) pose.qWorldFromDriverRotation.z = 0; } - if (is_controller_) { - // Get inputs from protobuf - bool double_tap = false; - bool triple_tap = false; - bool x_pressed = false; - bool y_pressed = false; - bool a_pressed = false; - bool b_pressed = false; - bool stick_click = false; - float stick_x = 0.0f; - float stick_y = 0.0f; - float trigger = 0.0f; - float grip = 0.0f; - - for (int i = 0; i < position.input_size(); ++i) { - const auto& input = position.input(i); - switch (input.type()) { - case messages::Input_InputType_DOUBLE_TAP: - double_tap = true; - break; - case messages::Input_InputType_TRIPLE_TAP: - triple_tap = true; - break; - case messages::Input_InputType_TRIGGER: - trigger = input.value(); - break; - case messages::Input_InputType_GRIP: - grip = input.value(); - break; - case messages::Input_InputType_BUTTON_X: - x_pressed = true; - break; - case messages::Input_InputType_BUTTON_Y: - y_pressed = true; - break; - case messages::Input_InputType_BUTTON_A: - a_pressed = true; - break; - case messages::Input_InputType_BUTTON_B: - b_pressed = true; - break; - case messages::Input_InputType_STICK_X: - stick_x = input.value(); - break; - case messages::Input_InputType_STICK_Y: - stick_y = input.value(); - break; - case messages::Input_InputType_STICK_CLICK: - stick_click = true; - break; - } - } - - // Set inputs - GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); - - if (is_left_hand_) { - GetDriver()->GetInput()->UpdateScalarComponent(left_trigger_component_, trigger, 0); - GetDriver()->GetInput()->UpdateScalarComponent(left_grip_value_component_, grip, 0); - GetDriver()->GetInput()->UpdateScalarComponent(left_stick_x_component_, stick_x, 0); - GetDriver()->GetInput()->UpdateScalarComponent(left_stick_y_component_, stick_y, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_x_component_, x_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_y_component_, y_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(left_stick_click_component_, stick_click, 0); - } - else if (is_right_hand_) { - GetDriver()->GetInput()->UpdateScalarComponent(right_trigger_component_, trigger, 0); - GetDriver()->GetInput()->UpdateScalarComponent(right_grip_value_component_, grip, 0); - GetDriver()->GetInput()->UpdateScalarComponent(right_stick_x_component_, stick_x, 0); - GetDriver()->GetInput()->UpdateScalarComponent(right_stick_y_component_, stick_y, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, a_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, b_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); + bool double_tap = false; + bool triple_tap = false; + for (int i = 0; i < position.input_size(); ++i) { + const auto& input = position.input(i); + switch (input.type()) { + case messages::Input_InputType_DOUBLE_TAP: + double_tap = true; + break; + case messages::Input_InputType_TRIPLE_TAP: + triple_tap = true; + break; } } @@ -189,11 +126,61 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) pose.poseIsValid = true; pose.result = vr::ETrackingResult::TrackingResult_Running_OK; + // Set inputs + GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); + // Notify SteamVR that pose was updated last_pose_atomic_ = (last_pose_ = pose); GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } +void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { + if (is_controller_) { + // Get inputs from protobuf + bool x_pressed = false; + bool y_pressed = false; + bool a_pressed = false; + bool b_pressed = false; + bool stick_click = false; + bool menu = false; + bool recenter = false; + float thumbstick_x = 0.0f; + float thumbstick_y = 0.0f; + float trigger = 0.0f; + float grip = 0.0f; + + thumbstick_x = controllerInput.thumbstick_x; + thumbstick_y = controllerInput.thumbstick_y; + trigger = controllerInput.trigger; + grip = controllerInput.grip; + stick_click = controllerInput.stick_click; + if (is_left_hand_) { + x_pressed = controllerInput.button_1; + y_pressed = controllerInput.button_2; + menu = controllerInput.menu_recenter; + GetDriver()->GetInput()->UpdateScalarComponent(left_trigger_component_, trigger, 0); + GetDriver()->GetInput()->UpdateScalarComponent(left_grip_value_component_, grip, 0); + GetDriver()->GetInput()->UpdateScalarComponent(left_stick_x_component_, thumbstick_x, 0); + GetDriver()->GetInput()->UpdateScalarComponent(left_stick_y_component_, thumbstick_y, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_x_component_, x_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_y_component_, y_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(left_stick_click_component_, stick_click, 0); + } + else if (is_right_hand_) { + a_pressed = controllerInput.button_1; + b_pressed = controllerInput.button_2; + recenter = controllerInput.menu_recenter; + GetDriver()->GetInput()->UpdateScalarComponent(right_trigger_component_, trigger, 0); + GetDriver()->GetInput()->UpdateScalarComponent(right_grip_value_component_, grip, 0); + GetDriver()->GetInput()->UpdateScalarComponent(right_stick_x_component_, thumbstick_x, 0); + GetDriver()->GetInput()->UpdateScalarComponent(right_stick_y_component_, thumbstick_y, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, a_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, b_pressed, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); + } + } +} void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 5425f44..2488745 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -30,6 +30,7 @@ namespace SlimeVRDriver { virtual int GetDeviceId() override; virtual void SetDeviceId(int device_id) override; virtual void PositionMessage(messages::Position &position) override; + virtual void ControllerInputMessage(messages::ControllerInput& position) override; virtual void StatusMessage(messages::TrackerStatus &status) override; virtual void BatteryMessage(messages::Battery &battery) override; diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 493586b..ecb8e98 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -219,7 +219,15 @@ void SlimeVRDriver::VRDriver::OnBridgeMessage(const messages::ProtobufMessage& m if (device != devices_by_id_.end()) { device->second->PositionMessage(pos); } - } else if (message.has_tracker_status()) { + } + else if (message.has_controller_input()) { + messages::ControllerInput controllerInput = message.controller_input(); + auto device = devices_by_id_.find(controllerInput.tracker_id()); + if (device != devices_by_id_.end()) { + device->second->ControllerInputMessage(controllerInput); + } + } + else if (message.has_tracker_status()) { messages::TrackerStatus status = message.tracker_status(); auto device = devices_by_id_.find(status.tracker_id()); if (device != devices_by_id_.end()) { From fabb90f3f87cf00a5e89963f25b00b40dca1ce46 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 16 Jan 2026 22:50:02 -0600 Subject: [PATCH 010/117] Temporarily comment out finger code. --- src/TrackerDevice.cpp | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 91eab4e..dd13bff 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -98,29 +98,29 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) } } - if (fingertracking_enabled_) { - // Set finger rotations - vr::VRBoneTransform_t finger_skeleton_[31]{}; - for (int i = 0; i < position.finger_bone_rotations_size(); i++) - { - // Get data from protobuf - auto fingerData = position.finger_bone_rotations(i); - int fingerBoneName = static_cast(fingerData.name()); - - // Map from our 15 bones to OpenVR's 31 bones - int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; - finger_skeleton_[boneIndex].orientation = { - fingerData.w(), - fingerData.x(), - fingerData.y(), - fingerData.z() - }; - } - - // Update the finger skeleton for this hand. With and without controller have the same pose. - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); - } + //if (fingertracking_enabled_) { + // // Set finger rotations + // vr::VRBoneTransform_t finger_skeleton_[31]{}; + // for (int i = 0; i < position.finger_bone_rotations_size(); i++) + // { + // // Get data from protobuf + // auto fingerData = position.finger_bone_rotations(i); + // int fingerBoneName = static_cast(fingerData.name()); + + // // Map from our 15 bones to OpenVR's 31 bones + // int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; + // finger_skeleton_[boneIndex].orientation = { + // fingerData.w(), + // fingerData.x(), + // fingerData.y(), + // fingerData.z() + // }; + // } + + // // Update the finger skeleton for this hand. With and without controller have the same pose. + // vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); + // vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); + //} pose.deviceIsConnected = true; pose.poseIsValid = true; From 723449579fd4dd1766e3cfe1f169ddb3675ba75d Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 00:57:49 -0600 Subject: [PATCH 011/117] Add perenthesis. --- src/TrackerDevice.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index dd13bff..24f8d3d 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -150,14 +150,14 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn float grip = 0.0f; - thumbstick_x = controllerInput.thumbstick_x; - thumbstick_y = controllerInput.thumbstick_y; - trigger = controllerInput.trigger; - grip = controllerInput.grip; - stick_click = controllerInput.stick_click; + thumbstick_x = controllerInput.thumbstick_x(); + thumbstick_y = controllerInput.thumbstick_y(); + trigger = controllerInput.trigger(); + grip = controllerInput.grip(); + stick_click = controllerInput.stick_click(); if (is_left_hand_) { - x_pressed = controllerInput.button_1; - y_pressed = controllerInput.button_2; + x_pressed = controllerInput.button_1(); + y_pressed = controllerInput.button_2(); menu = controllerInput.menu_recenter; GetDriver()->GetInput()->UpdateScalarComponent(left_trigger_component_, trigger, 0); GetDriver()->GetInput()->UpdateScalarComponent(left_grip_value_component_, grip, 0); @@ -168,9 +168,9 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(left_stick_click_component_, stick_click, 0); } else if (is_right_hand_) { - a_pressed = controllerInput.button_1; - b_pressed = controllerInput.button_2; - recenter = controllerInput.menu_recenter; + a_pressed = controllerInput.button_1(); + b_pressed = controllerInput.button_2(); + recenter = controllerInput.menu_recenter(); GetDriver()->GetInput()->UpdateScalarComponent(right_trigger_component_, trigger, 0); GetDriver()->GetInput()->UpdateScalarComponent(right_grip_value_component_, grip, 0); GetDriver()->GetInput()->UpdateScalarComponent(right_stick_x_component_, thumbstick_x, 0); From 35e828f795416ad501dd8481cfa87d5c2fe7b312 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 00:59:46 -0600 Subject: [PATCH 012/117] Comment out inputs. --- src/TrackerDevice.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 24f8d3d..a9e9768 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -86,17 +86,17 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) bool double_tap = false; bool triple_tap = false; - for (int i = 0; i < position.input_size(); ++i) { - const auto& input = position.input(i); - switch (input.type()) { - case messages::Input_InputType_DOUBLE_TAP: - double_tap = true; - break; - case messages::Input_InputType_TRIPLE_TAP: - triple_tap = true; - break; - } - } + //for (int i = 0; i < position.input_size(); ++i) { + // const auto& input = position.input(i); + // switch (input.type()) { + // case messages::Input_InputType_DOUBLE_TAP: + // double_tap = true; + // break; + // case messages::Input_InputType_TRIPLE_TAP: + // triple_tap = true; + // break; + // } + //} //if (fingertracking_enabled_) { // // Set finger rotations From 417b6bf1c7e62429c54f0aee7e237136f2620ad0 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 01:19:21 -0600 Subject: [PATCH 013/117] Update header file. --- src/TrackerDevice.cpp | 2 +- src/TrackerDevice.hpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index a9e9768..5b854c6 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -158,7 +158,7 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn if (is_left_hand_) { x_pressed = controllerInput.button_1(); y_pressed = controllerInput.button_2(); - menu = controllerInput.menu_recenter; + menu = controllerInput.menu_recenter(); GetDriver()->GetInput()->UpdateScalarComponent(left_trigger_component_, trigger, 0); GetDriver()->GetInput()->UpdateScalarComponent(left_grip_value_component_, grip, 0); GetDriver()->GetInput()->UpdateScalarComponent(left_stick_x_component_, thumbstick_x, 0); diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 2488745..1b1257f 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -62,6 +62,21 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t double_tap_component_ = 0; vr::VRInputComponentHandle_t triple_tap_component_ = 0; + vr::VRInputComponentHandle_t left_trigger_component_ = 0; + vr::VRInputComponentHandle_t left_grip_value_component_ = 0; + vr::VRInputComponentHandle_t left_stick_x_component_ = 0; + vr::VRInputComponentHandle_t left_stick_y_component_ = 0; + vr::VRInputComponentHandle_t button_x_component_ = 0; + vr::VRInputComponentHandle_t button_y_component_ = 0; + vr::VRInputComponentHandle_t left_stick_click_component_ = 0; + + vr::VRInputComponentHandle_t right_trigger_component_ = 0; + vr::VRInputComponentHandle_t right_grip_value_component_ = 0; + vr::VRInputComponentHandle_t right_stick_x_component_ = 0; + vr::VRInputComponentHandle_t right_stick_y_component_ = 0; + vr::VRInputComponentHandle_t button_a_component_ = 0; + vr::VRInputComponentHandle_t button_b_component_ = 0; + vr::VRInputComponentHandle_t right_stick_click_component_ = 0; bool is_controller_; bool is_left_hand_; bool is_right_hand_; From 168681eda1a933d08349539c02bf854a449ed287 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 01:25:34 -0600 Subject: [PATCH 014/117] Add menu/recenter mappings. --- src/TrackerDevice.cpp | 2 ++ src/TrackerDevice.hpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 5b854c6..b488edb 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -166,6 +166,7 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(button_x_component_, x_pressed, 0); GetDriver()->GetInput()->UpdateBooleanComponent(button_y_component_, y_pressed, 0); GetDriver()->GetInput()->UpdateBooleanComponent(left_stick_click_component_, stick_click, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, menu, 0); } else if (is_right_hand_) { a_pressed = controllerInput.button_1(); @@ -178,6 +179,7 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, a_pressed, 0); GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, b_pressed, 0); GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(recenter_component_, recenter, 0); } } } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 1b1257f..4e7ce63 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -69,6 +69,7 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t button_x_component_ = 0; vr::VRInputComponentHandle_t button_y_component_ = 0; vr::VRInputComponentHandle_t left_stick_click_component_ = 0; + vr::VRInputComponentHandle_t menu_component_ = 0; vr::VRInputComponentHandle_t right_trigger_component_ = 0; vr::VRInputComponentHandle_t right_grip_value_component_ = 0; @@ -77,6 +78,8 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t button_a_component_ = 0; vr::VRInputComponentHandle_t button_b_component_ = 0; vr::VRInputComponentHandle_t right_stick_click_component_ = 0; + vr::VRInputComponentHandle_t recenter_component_ = 0; + bool is_controller_; bool is_left_hand_; bool is_right_hand_; From 4ad037bc495b80cad494cd6aea1f902f7b3c2ec0 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 02:02:23 -0600 Subject: [PATCH 015/117] Update interface to include controller inputs --- src/IVRDevice.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/IVRDevice.hpp b/src/IVRDevice.hpp index b2681ab..e88ebc4 100644 --- a/src/IVRDevice.hpp +++ b/src/IVRDevice.hpp @@ -68,6 +68,11 @@ namespace SlimeVRDriver { * Updates device position from a received message. */ virtual void PositionMessage(messages::Position& position) = 0; + + /** + * Updates device position from a received message. + */ + virtual void ControllerInputMessage(messages::Position& position) = 0; /** * Updates device status from a received message. From 54e238f8e438879c8b5e48d523a4a9854e573281 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 02:18:48 -0600 Subject: [PATCH 016/117] Adjust component creation fields --- src/TrackerDevice.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index b488edb..b929e66 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -314,15 +314,15 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &button_a_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &menu_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &system_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "stick/click", &stick_click_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "grip/click", &grip_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &recenter_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "stick/click", is_left_hand_ ? &left_stick_click_component_ : &right_stick_click_component_); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "trigger/value", &trigger_component_handle_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/value", &grip_value_component_handle_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/x", &stick_x_component_handle_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/y", &stick_y_component_handle_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/click", is_left_hand_ ? &left_grip_value_component_ : right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "trigger/value", is_left_hand_ ? &left_trigger_component_ : left_trigger_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/value", is_left_hand_ ? &left_grip_value_component_ : right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/x", is_left_hand_ ? &left_stick_x_component_ : right_stick_x_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/y", is_left_hand_ ? &left_stick_y_component_ : right_stick_y_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From fceea0e26186e883a4a8c13831ffcec216447ee4 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 02:34:54 -0600 Subject: [PATCH 017/117] Fix syntax. --- src/TrackerDevice.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index b929e66..8f70a73 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -309,20 +309,20 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/x/click", &button_x_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &button_y_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &button_a_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &button_b_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &menu_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &recenter_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "stick/click", is_left_hand_ ? &left_stick_click_component_ : &right_stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/x/click", &this->button_x_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &this->button_y_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &this->button_a_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &this->menu_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &this->recenter_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "stick/click", is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/click", is_left_hand_ ? &left_grip_value_component_ : right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "trigger/value", is_left_hand_ ? &left_trigger_component_ : left_trigger_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/value", is_left_hand_ ? &left_grip_value_component_ : right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/x", is_left_hand_ ? &left_stick_x_component_ : right_stick_x_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/y", is_left_hand_ ? &left_stick_y_component_ : right_stick_y_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/click", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From 2b3761a0752037f26a759ae6d8d80f6ddb84ea73 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 02:37:27 -0600 Subject: [PATCH 018/117] Fix strings. --- src/TrackerDevice.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 8f70a73..7651fdc 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -315,14 +315,14 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &this->menu_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &this->recenter_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix + "stick/click", is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix.c_str() + "stick/click", is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/click", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix + "stick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "grip/click", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "stick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "stick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From 7e87cbeca64905d3071afc324b8cdc9f32c3b12e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 02:42:43 -0600 Subject: [PATCH 019/117] Fix strings differently. --- src/TrackerDevice.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 7651fdc..ec8ef67 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -315,14 +315,14 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &this->menu_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &this->recenter_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, hand_prefix.c_str() + "stick/click", is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, (hand_prefix + "stick/click").c_str(), is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "grip/click", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "stick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, hand_prefix.c_str() + "stick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "grip/click").c_str(), is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "trigger/value").c_str(), is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "grip/value").c_str(), is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "stick/x").c_str(), is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "stick/y").c_str(), is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From fcc39e225ff70881c80b90458e71a696e9c5935a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 03:06:02 -0600 Subject: [PATCH 020/117] Fix argument type for ControllerInputMessage in interface. --- src/IVRDevice.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IVRDevice.hpp b/src/IVRDevice.hpp index e88ebc4..6fafa0a 100644 --- a/src/IVRDevice.hpp +++ b/src/IVRDevice.hpp @@ -72,7 +72,7 @@ namespace SlimeVRDriver { /** * Updates device position from a received message. */ - virtual void ControllerInputMessage(messages::Position& position) = 0; + virtual void ControllerInputMessage(messages::ControllerInput& position) = 0; /** * Updates device status from a received message. From 8d7279adb9977b84eebb9036c5eeb8a08ec8e9b5 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 15:08:39 -0600 Subject: [PATCH 021/117] Change input strings to match with standard Quest Touch controllers. --- src/TrackerDevice.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index ec8ef67..6f0386e 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -309,20 +309,22 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/x/click", &this->button_x_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &this->button_y_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &this->button_a_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/menu/click", &this->menu_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", &this->recenter_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, (hand_prefix + "stick/click").c_str(), is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_); + if (is_left_hand_) { + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/x/click", &this->button_x_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &this->button_y_component_); + } + if (is_right_hand_) { + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &this->button_a_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); + } + GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", is_left_hand_ ? &this->menu_component_ : &this->recenter_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, ("/input/joystick/click").c_str(), (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "grip/click").c_str(), is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "trigger/value").c_str(), is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "grip/value").c_str(), is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "stick/x").c_str(), is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_); - GetDriver()->GetInput()->CreateScalarComponent(props, (hand_prefix + "stick/y").c_str(), is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/trigger/value").c_str(), is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/grip/value").c_str(), is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/joystick/x").c_str(), is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_,vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/joystick/y").c_str(), is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From a31c5465618b55c1037e84d9796873bcb263c025 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 15:28:46 -0600 Subject: [PATCH 022/117] Remove c_str() conversion. --- src/TrackerDevice.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 6f0386e..88f49e4 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -86,17 +86,6 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) bool double_tap = false; bool triple_tap = false; - //for (int i = 0; i < position.input_size(); ++i) { - // const auto& input = position.input(i); - // switch (input.type()) { - // case messages::Input_InputType_DOUBLE_TAP: - // double_tap = true; - // break; - // case messages::Input_InputType_TRIPLE_TAP: - // triple_tap = true; - // break; - // } - //} //if (fingertracking_enabled_) { // // Set finger rotations @@ -318,13 +307,13 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); } GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", is_left_hand_ ? &this->menu_component_ : &this->recenter_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, ("/input/joystick/click").c_str(), (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/trigger/value").c_str(), is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/grip/value").c_str(), is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/joystick/x").c_str(), is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_,vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, ("/input/joystick/y").c_str(), is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_,vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From 6a9f86b4169f91e747eec5e6ea072f85c52e1886 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 18:58:38 -0600 Subject: [PATCH 023/117] Adjustments for proper controller detection. --- src/TrackerDevice.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 88f49e4..386d79c 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -252,14 +252,18 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); } - // Hand selection - if (is_left_hand_) { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); - } - else if (is_right_hand_) { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); - } - else { + //// Hand selection + //if (is_left_hand_) { + // GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + //} + //else if (is_right_hand_) { + // GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + //} + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, + is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" + : "{indexcontroller}valve_controller_knu_1_0_left"); + + if(!is_left_hand_ && !is_right_hand_) { GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); } @@ -310,7 +314,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->left_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->right_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_,vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); From 9db4b8f695bcb3c87b88add94b90f7a66a40fe45 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 19:33:43 -0600 Subject: [PATCH 024/117] Adjust inputs, and device type reporting. --- src/TrackerDevice.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 386d79c..4bb4098 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -226,9 +226,13 @@ void SlimeVRDriver::TrackerDevice::StatusMessage(messages::TrackerStatus& status } DeviceType SlimeVRDriver::TrackerDevice::GetDeviceType() { + if (is_controller_) { + return DeviceType::CONTROLLER; + } return DeviceType::TRACKER; } + vr::TrackedDeviceIndex_t SlimeVRDriver::TrackerDevice::GetDeviceIndex() { return device_index_; } @@ -259,11 +263,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { //else if (is_right_hand_) { // GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); //} - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, - is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" - : "{indexcontroller}valve_controller_knu_1_0_left"); - - if(!is_left_hand_ && !is_right_hand_) { + if (!is_left_hand_ && !is_right_hand_) { GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); } @@ -297,7 +297,9 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set inputs if (is_controller_) { std::string hand_prefix = is_left_hand_ ? "/input/left/" : "/input/right/"; - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{steamvr}/input/vr_controller_generic_hmd.json" + ); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); @@ -316,7 +318,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Scalar components GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->right_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_,vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); } From 524770d93583fb057ae2a2505b6cbd919c18209b Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 19:39:36 -0600 Subject: [PATCH 025/117] Use Quest Touch bindings --- src/TrackerDevice.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 4bb4098..cf1f2fd 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -297,9 +297,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set inputs if (is_controller_) { std::string hand_prefix = is_left_hand_ ? "/input/left/" : "/input/right/"; - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{steamvr}/input/vr_controller_generic_hmd.json" - ); - + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{openvr}/input/oculus_quest_touch.json"); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); From c0420c8d8b706f8ff2504be2feceb282f1c4e110 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 20:13:03 -0600 Subject: [PATCH 026/117] Fix input strings --- src/TrackerDevice.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index cf1f2fd..cc4988a 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -257,12 +257,12 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { } //// Hand selection - //if (is_left_hand_) { - // GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); - //} - //else if (is_right_hand_) { - // GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); - //} + if (is_left_hand_) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + } + else if (is_right_hand_) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + } if (!is_left_hand_ && !is_right_hand_) { GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); } @@ -297,20 +297,20 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set inputs if (is_controller_) { std::string hand_prefix = is_left_hand_ ? "/input/left/" : "/input/right/"; - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{openvr}/input/oculus_quest_touch.json"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{steamvr}/input/vr_controller_vive_1_5.json"); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); if (is_left_hand_) { - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/x/click", &this->button_x_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/click", &this->button_x_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &this->button_y_component_); } if (is_right_hand_) { - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/a/click", &this->button_a_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/b/click", &this->button_b_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/click", &this->button_a_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); } - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/system/click", is_left_hand_ ? &this->menu_component_ : &this->recenter_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", is_left_hand_ ? &this->menu_component_ : &this->recenter_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); // Scalar components From 1c03ed79ab951e0e9623046cf2e5da26f97cf61e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 21:51:29 -0600 Subject: [PATCH 027/117] Update controller bindings. --- .../input/slimevr_controller_bindings.json | 94 +++++++++---------- src/TrackerDevice.cpp | 27 ++++-- src/TrackerDevice.hpp | 22 +++++ 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index d39701f..20cee7e 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -47,77 +47,75 @@ "side": "right", "binding_image_point": [ 80, 80 ] }, + "/input/x/click": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "click": true }, - "/input/y/click": { + "/input/x/touch": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "touch": true }, - "/input/a/click": { + "/input/y/click": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "click": true }, - "/input/b/click": { + "/input/y/touch": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] - }, - "/input/left/trigger": { - "type": "trigger", - "binding_image_point": [ 80, 80 ] - }, - "/input/right/trigger": { - "type": "trigger", - "binding_image_point": [ 80, 80 ] + "touch": true }, - "/input/left/stick/x": { - "type": "scalar", - "binding_image_point": [ 80, 80 ] - }, - "/input/left/stick/y": { - "type": "scalar", - "binding_image_point": [ 80, 80 ] + + "/input/a/click": { + "type": "button", + "click": true }, - "/input/left/stick/click": { + "/input/a/touch": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "touch": true }, - "/input/right/stick/x": { - "type": "scalar", - "binding_image_point": [ 80, 80 ] + "/input/b/click": { + "type": "button", + "click": true }, - "/input/right/stick/y": { - "type": "scalar", - "binding_image_point": [ 80, 80 ] + "/input/b/touch": { + "type": "button", + "touch": true }, - "/input/right/stick/click": { + + "/input/system/click": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "click": true }, "/input/menu/click": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "click": true }, "/input/recenter/click": { "type": "button", - "click": true, - "binding_image_point": [ 80, 80 ] + "click": true }, - "/input/left/grip": { - "type": "trigger", - "binding_image_point": [ 80, 80 ] + + "/input/trigger/value": { "type": "trigger" }, + "/input/trigger/touch": { + "type": "button", + "touch": true }, - "/input/right/grip": { - "type": "trigger", - "binding_image_point": [ 80, 80 ] + + "/input/grip/value": { "type": "trigger" }, + "/input/grip/touch": { + "type": "button", + "touch": true + }, + + "/input/joystick/x": { "type": "scalar" }, + "/input/joystick/y": { "type": "scalar" }, + "/input/joystick/click": { + "type": "button", + "click": true + }, + "/input/joystick/touch": { + "type": "button", + "touch": true } }, "default_bindings": [ diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index cc4988a..ca6844c 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -138,7 +138,6 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn float trigger = 0.0f; float grip = 0.0f; - thumbstick_x = controllerInput.thumbstick_x(); thumbstick_y = controllerInput.thumbstick_y(); trigger = controllerInput.trigger(); @@ -263,7 +262,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { else if (is_right_hand_) { GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); } - if (!is_left_hand_ && !is_right_hand_) { + else { GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); } @@ -277,8 +276,17 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { } // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) + std::string model_path; if (is_controller_) { - vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); + // Try Oculus Touch first, fallback to generic + model_path = "{oculus}oculus_touch_left"; + if (!SteamVRModelExists(model_path)) { + model_path = "{generic}controller"; + } + GetDriver()->GetProperties()->SetStringProperty( + props, vr::Prop_RenderModelName_String, + is_right_hand_ ? model_path.replace("left", "right") : model_path.c_str() + ); } else { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); @@ -296,28 +304,35 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set inputs if (is_controller_) { - std::string hand_prefix = is_left_hand_ ? "/input/left/" : "/input/right/"; - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{steamvr}/input/vr_controller_vive_1_5.json"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); if (is_left_hand_) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/click", &this->button_x_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "input/y/click", &this->button_y_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/touch", &this->button_x_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/y/click", &this->button_y_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/y/touch", &this->button_y_component_touch_); } if (is_right_hand_) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/click", &this->button_a_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/touch", &this->button_a_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); } GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", is_left_hand_ ? &this->menu_component_ : &this->recenter_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", (is_left_hand_ ? &this->left_stick_click_component_touch_ : &this->right_stick_click_component_touch_)); // Scalar components GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->right_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", is_left_hand_ ? &this->left_trigger_component_touch_ : &this->right_trigger_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", is_left_hand_ ? &this->left_grip_value_component_touch_ : &this->right_grip_value_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &haptic_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 4e7ce63..d652de1 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -62,6 +62,8 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t double_tap_component_ = 0; vr::VRInputComponentHandle_t triple_tap_component_ = 0; + vr::VRInputComponentHandle_t ignored = 0; + vr::VRInputComponentHandle_t left_trigger_component_ = 0; vr::VRInputComponentHandle_t left_grip_value_component_ = 0; vr::VRInputComponentHandle_t left_stick_x_component_ = 0; @@ -71,6 +73,15 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t left_stick_click_component_ = 0; vr::VRInputComponentHandle_t menu_component_ = 0; + vr::VRInputComponentHandle_t left_trigger_component_touch_ = 0; + vr::VRInputComponentHandle_t left_grip_value_component_touch_ = 0; + vr::VRInputComponentHandle_t left_stick_x_component_touch_ = 0; + vr::VRInputComponentHandle_t left_stick_y_component_touch_ = 0; + vr::VRInputComponentHandle_t button_x_component_touch_ = 0; + vr::VRInputComponentHandle_t button_y_component_touch_ = 0; + vr::VRInputComponentHandle_t left_stick_click_component_touch_ = 0; + vr::VRInputComponentHandle_t menu_component_touch_ = 0; + vr::VRInputComponentHandle_t right_trigger_component_ = 0; vr::VRInputComponentHandle_t right_grip_value_component_ = 0; vr::VRInputComponentHandle_t right_stick_x_component_ = 0; @@ -80,6 +91,17 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t right_stick_click_component_ = 0; vr::VRInputComponentHandle_t recenter_component_ = 0; + vr::VRInputComponentHandle_t right_trigger_component_touch_ = 0; + vr::VRInputComponentHandle_t right_grip_value_component_touch_ = 0; + vr::VRInputComponentHandle_t right_stick_x_component_touch_ = 0; + vr::VRInputComponentHandle_t right_stick_y_component_touch_ = 0; + vr::VRInputComponentHandle_t button_a_component_touch_ = 0; + vr::VRInputComponentHandle_t button_b_component_touch_ = 0; + vr::VRInputComponentHandle_t right_stick_click_component_touch_ = 0; + vr::VRInputComponentHandle_t recenter_component_touch_ = 0; + + vr::VRInputComponentHandle_t haptic_component_ = 0; + bool is_controller_; bool is_left_hand_; bool is_right_hand_; From 0d07eb47908229b19f4b6599f3ddeae9f86e8baf Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 21:58:51 -0600 Subject: [PATCH 028/117] Dont bother displaying oculus touch --- src/TrackerDevice.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index ca6844c..1a0a9a1 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -278,14 +278,9 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) std::string model_path; if (is_controller_) { - // Try Oculus Touch first, fallback to generic - model_path = "{oculus}oculus_touch_left"; - if (!SteamVRModelExists(model_path)) { - model_path = "{generic}controller"; - } + model_path = "{generic}controller"; GetDriver()->GetProperties()->SetStringProperty( - props, vr::Prop_RenderModelName_String, - is_right_hand_ ? model_path.replace("left", "right") : model_path.c_str() + props, vr::Prop_RenderModelName_String, model_path.c_str() ); } else { From 1c119f2fa3fb871df8b6b85ef07bcb5e807c8224 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 22:03:17 -0600 Subject: [PATCH 029/117] Fix double haptic_component_ --- src/TrackerDevice.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index d652de1..740ec81 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -100,7 +100,6 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t right_stick_click_component_touch_ = 0; vr::VRInputComponentHandle_t recenter_component_touch_ = 0; - vr::VRInputComponentHandle_t haptic_component_ = 0; bool is_controller_; bool is_left_hand_; From 8ac1e0aabc3d2a2537f6c21a8d5e749959b53a75 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 23:15:16 -0600 Subject: [PATCH 030/117] Temporarily assume anything mapped to hands wnts to be a controller. --- src/TrackerDevice.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 1a0a9a1..42f27ba 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -7,7 +7,7 @@ SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, T is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), fingertracking_enabled_(is_left_hand_ || is_right_hand_), - is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER), + is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), last_pose_(MakeDefaultPose()), last_pose_atomic_(MakeDefaultPose()) { @@ -278,7 +278,12 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) std::string model_path; if (is_controller_) { - model_path = "{generic}controller"; + if (is_left_hand_) { + model_path = "{oculus}oculus_quest_pro_controller_left"; + } + else if (is_right_hand_) { + model_path = "{oculus}oculus_quest_pro_controller_right"; + } GetDriver()->GetProperties()->SetStringProperty( props, vr::Prop_RenderModelName_String, model_path.c_str() ); From 28fe202416883e7989e136150bd45e9c883e0ccb Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 23:28:49 -0600 Subject: [PATCH 031/117] Lets actually remember to map the right hand too. --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 42f27ba..c477f1e 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -7,7 +7,7 @@ SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, T is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), fingertracking_enabled_(is_left_hand_ || is_right_hand_), - is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), + is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND || tracker_role_ == TrackerRole::RIGHT_HAND), last_pose_(MakeDefaultPose()), last_pose_atomic_(MakeDefaultPose()) { From a8d11fa6e7e7b1b28c76c1c17d39137ed124c887 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 17 Jan 2026 23:38:48 -0600 Subject: [PATCH 032/117] Try a different render model. --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index c477f1e..bce9aa4 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -279,10 +279,10 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { std::string model_path; if (is_controller_) { if (is_left_hand_) { - model_path = "{oculus}oculus_quest_pro_controller_left"; + model_path = "{htc}/vr_controller_vive_1_5"; // Use Vive 1.5 model for left } else if (is_right_hand_) { - model_path = "{oculus}oculus_quest_pro_controller_right"; + model_path = "{htc}/vr_controller_vive_1_5"; // Right hand uses same model } GetDriver()->GetProperties()->SetStringProperty( props, vr::Prop_RenderModelName_String, model_path.c_str() From 40046403230d135924d91ea67adbabc9850f9c6a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 00:07:56 -0600 Subject: [PATCH 033/117] Revert to the original model. --- src/TrackerDevice.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index bce9aa4..c46637d 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -278,15 +278,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) std::string model_path; if (is_controller_) { - if (is_left_hand_) { - model_path = "{htc}/vr_controller_vive_1_5"; // Use Vive 1.5 model for left - } - else if (is_right_hand_) { - model_path = "{htc}/vr_controller_vive_1_5"; // Right hand uses same model - } - GetDriver()->GetProperties()->SetStringProperty( - props, vr::Prop_RenderModelName_String, model_path.c_str() - ); + vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); } else { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); From fdf66a27a8f4cfab88861d103690461487d64abc Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 01:26:43 -0600 Subject: [PATCH 034/117] Update OpenVR, switch to UpdatePoseComponent when tracker is a controller. --- .../resources/input/slimevr_controller_bindings.json | 1 + src/TrackerDevice.cpp | 11 ++++++++--- src/TrackerDevice.hpp | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 20cee7e..83a8774 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -6,6 +6,7 @@ "driver_name": "slimevr", "input_bindingui_mode": "controller_handed", "should_show_binding_errors": true, + "compatibility_mode_controller_type": "oculus_touch", "input_bindingui_left": { "image": "{slimevr}/icons/slimevr_tracker_icon.png" }, diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index c46637d..80d0e8b 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -121,7 +121,13 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) // Notify SteamVR that pose was updated last_pose_atomic_ = (last_pose_ = pose); - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); + + if (is_controller_) { + GetDriver()->GetInput()->UpdatePoseComponent(pose_component_handle_, &pose, sizeof(vr::DriverPose_t)); + } + else { + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); + } } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { if (is_controller_) { @@ -293,14 +299,13 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); - // Set inputs if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - + vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &pose_component_handle_); if (is_left_hand_) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/click", &this->button_x_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/touch", &this->button_x_component_touch_); diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 740ec81..14c4bae 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -63,6 +63,7 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t triple_tap_component_ = 0; vr::VRInputComponentHandle_t ignored = 0; + vr::VRInputComponentHandle_t pose_component_handle_ = 0; vr::VRInputComponentHandle_t left_trigger_component_ = 0; vr::VRInputComponentHandle_t left_grip_value_component_ = 0; From 3716600d4e285c3580da735444faa870b6662a8b Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 01:56:23 -0600 Subject: [PATCH 035/117] Use HMD matrix. --- src/TrackerDevice.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 80d0e8b..b47cf9f 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -123,7 +123,8 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) last_pose_atomic_ = (last_pose_ = pose); if (is_controller_) { - GetDriver()->GetInput()->UpdatePoseComponent(pose_component_handle_, &pose, sizeof(vr::DriverPose_t)); + vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); + GetDriver()->GetInput()->UpdatePoseComponent(pose_component_handle_, &poseMatrix, 0.0); } else { GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); @@ -394,3 +395,43 @@ int SlimeVRDriver::TrackerDevice::GetDeviceId() { void SlimeVRDriver::TrackerDevice::SetDeviceId(int device_id) { device_id_ = device_id; } +vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose) { + vr::HmdMatrix34_t m; + + const auto& q = pose.qRotation; + const auto& p = pose.vecPosition; + + float x2 = q.x + q.x; + float y2 = q.y + q.y; + float z2 = q.z + q.z; + + float xx2 = q.x * x2; + float yy2 = q.y * y2; + float zz2 = q.z * z2; + + float xy2 = q.x * y2; + float xz2 = q.x * z2; + float yz2 = q.y * z2; + + float wx2 = q.w * x2; + float wy2 = q.w * y2; + float wz2 = q.w * z2; + + m.m[0][0] = 1.0f - (yy2 + zz2); + m.m[0][1] = xy2 - wz2; + m.m[0][2] = xz2 + wy2; + m.m[0][3] = p[0]; + + m.m[1][0] = xy2 + wz2; + m.m[1][1] = 1.0f - (xx2 + zz2); + m.m[1][2] = yz2 - wx2; + m.m[1][3] = p[1]; + + m.m[2][0] = xz2 - wy2; + m.m[2][1] = yz2 + wx2; + m.m[2][2] = 1.0f - (xx2 + yy2); + m.m[2][3] = p[2]; + + return m; +} + From 955a51b2557b1a53407260946bc4f9ec9ff20771 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 02:10:53 -0600 Subject: [PATCH 036/117] Update header --- src/TrackerDevice.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 14c4bae..95c9111 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -41,7 +41,7 @@ namespace SlimeVRDriver { virtual void* GetComponent(const char* pchComponentNameAndVersion) override; virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; virtual vr::DriverPose_t GetPose() override; - + vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose); private: std::shared_ptr logger_ = std::make_shared(); From 2e7f14aa6fda4ff072c35d29bf244916889732e6 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 02:54:12 -0600 Subject: [PATCH 037/117] Fix class implementation for ToHMDMatrix --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index b47cf9f..fad6510 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -395,7 +395,7 @@ int SlimeVRDriver::TrackerDevice::GetDeviceId() { void SlimeVRDriver::TrackerDevice::SetDeviceId(int device_id) { device_id_ = device_id; } -vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose) { +vr::HmdMatrix34_t SlimeVRDriver::TrackerDevice::ToHmdMatrix(const vr::DriverPose_t& pose) { vr::HmdMatrix34_t m; const auto& q = pose.qRotation; From 7fb2a12e20fb96021d66fb6e9714359d70943249 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 04:47:46 -0600 Subject: [PATCH 038/117] Use TrackedDevicePoseUpdated for both controllers and tracker. --- src/TrackerDevice.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index fad6510..1736e86 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -126,9 +126,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); GetDriver()->GetInput()->UpdatePoseComponent(pose_component_handle_, &poseMatrix, 0.0); } - else { - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); - } + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { if (is_controller_) { From 8c4c0fd672323fb778a2379ff0f734272cdcb030 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 05:08:36 -0600 Subject: [PATCH 039/117] Attempt to get pointer working. --- .../input/slimevr_controller_bindings.json | 25 ++++++++----------- src/TrackerDevice.cpp | 7 +++++- src/TrackerDevice.hpp | 3 +++ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 83a8774..dd29d39 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -18,35 +18,34 @@ "type": "pose", "binding_image_point": [ 80, 80 ] }, + "/pose/aim": { + "type": "pose", + "binding_image_point": [ 80, 80 ] + }, + "/output/haptic": { "type": "vibration", "binding_image_point": [ 80, 80 ] }, + "/input/double_tap": { "type": "button", - "click": true, - "touch": false, - "binding_image_point": [ 80, 80 ], - "order": 1 + "click": true }, "/input/triple_tap": { "type": "button", - "click": true, - "touch": false, - "binding_image_point": [ 80, 80 ], - "order": 2 + "click": true }, + "/input/skeleton/left": { "type": "skeleton", "skeleton": "/skeleton/hand/left", - "side": "left", - "binding_image_point": [ 80, 80 ] + "side": "left" }, "/input/skeleton/right": { "type": "skeleton", "skeleton": "/skeleton/hand/right", - "side": "right", - "binding_image_point": [ 80, 80 ] + "side": "right" }, "/input/x/click": { @@ -65,7 +64,6 @@ "type": "button", "touch": true }, - "/input/a/click": { "type": "button", "click": true @@ -101,7 +99,6 @@ "type": "button", "touch": true }, - "/input/grip/value": { "type": "trigger" }, "/input/grip/touch": { "type": "button", diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 1736e86..63cd45a 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -124,7 +124,8 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) if (is_controller_) { vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); - GetDriver()->GetInput()->UpdatePoseComponent(pose_component_handle_, &poseMatrix, 0.0); + GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); + GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &poseMatrix, 0.0); } GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } @@ -302,6 +303,10 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &raw_pose_component_handle_); + + vr::VRDriverInput()->CreatePoseComponent(props, "/pose/aim", &aim_pose_component_handle_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &pose_component_handle_); diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 95c9111..1fd20c7 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -65,6 +65,9 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t ignored = 0; vr::VRInputComponentHandle_t pose_component_handle_ = 0; + vr::VRInputComponentHandle_t raw_pose_component_handle_ = 0; + vr::VRInputComponentHandle_t aim_pose_component_handle_ = 0; + vr::VRInputComponentHandle_t left_trigger_component_ = 0; vr::VRInputComponentHandle_t left_grip_value_component_ = 0; vr::VRInputComponentHandle_t left_stick_x_component_ = 0; From f01300173ac6c76f897b7028ef9daffeea89b48f Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 05:57:10 -0600 Subject: [PATCH 040/117] Further adjustments to help inputs work correctly. --- .../input/vrcompositor_bindings_slimevr.json | 69 +++++-------------- src/TrackerDevice.cpp | 4 +- 2 files changed, 19 insertions(+), 54 deletions(-) diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json index 364b1d7..162b232 100644 --- a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -1,62 +1,27 @@ { - "action_manifest_version": 0, - "alias_info": {}, + "action_manifest_version": 1, + "controller_type": "slimevr_virtual_controller", + "interaction_profile": "/interaction_profiles/valve/index_controller", + "description": "SlimeVR virtual controller", "app_key": "openvr.component.vrcompositor", "bindings": { - "/actions/lasermouse": { + "/actions/default": { "sources": [ - { - "inputs": { - "click": { - "output": "/actions/lasermouse/in/LeftClick" - } - }, - "mode": "button", - "parameters": {}, - "path": "/user/hand/left/input/double_tap" - }, - { - "inputs": { - "click": { - "output": "/actions/lasermouse/in/LeftClick" - } - }, - "mode": "button", - "parameters": {}, - "path": "/user/hand/right/input/double_tap" - } + { "path": "/user/hand/left/pose/aim" }, + { "path": "/user/hand/right/pose/aim" } + ] + }, + "/actions/laser_click": { + "sources": [ + { "path": "/user/hand/left/input/trigger/value" }, + { "path": "/user/hand/right/input/trigger/value" } ] }, "/actions/system": { "sources": [ - { - "inputs": { - "click": { - "output": "/actions/system/in/ToggleDashboard" - } - }, - "mode": "button", - "parameters": {}, - "path": "/user/hand/left/input/triple_tap" - }, - { - "inputs": { - "click": { - "output": "/actions/system/in/ToggleDashboard" - } - }, - "mode": "button", - "parameters": {}, - "path": "/user/hand/right/input/triple_tap" - } + { "path": "/user/hand/left/input/system/click" }, + { "path": "/user/hand/right/input/system/click" } ] } - }, - "category": "steamvr_input", - "controller_type": "slimevr_virtual_controller", - "description": "Triple tap to toggle the dashboard. Double tap to click with the laser mouse.", - "interaction_profile": "", - "name": "Default SteamVR Dashboard bindings for SlimeVR", - "options": {}, - "simulated_actions": [] -} \ No newline at end of file + } +} diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 63cd45a..cec3642 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -127,6 +127,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &poseMatrix, 0.0); } + GetDriver()->GetInput()->ReportActiveDevice(device_index_); GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { @@ -175,6 +176,7 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); GetDriver()->GetInput()->UpdateBooleanComponent(recenter_component_, recenter, 0); } + GetDriver()->GetInput()->ReportActiveDevice(device_index_); } } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { @@ -304,12 +306,10 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &raw_pose_component_handle_); - vr::VRDriverInput()->CreatePoseComponent(props, "/pose/aim", &aim_pose_component_handle_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &pose_component_handle_); if (is_left_hand_) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/click", &this->button_x_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/touch", &this->button_x_component_touch_); From 2c73da2c735e9cc149252153f7d7d7007df9382a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 06:11:19 -0600 Subject: [PATCH 041/117] Add additional tweaks to try and get input to register. --- src/TrackerDevice.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index cec3642..c43c297 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -124,10 +124,11 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) if (is_controller_) { vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); + vr::HmdMatrix34_t aimPose = poseMatrix; + aimPose.m[0][3] += 0.02f; // 2cm forward GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); - GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &poseMatrix, 0.0); + GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &aimPose, 0.0); } - GetDriver()->GetInput()->ReportActiveDevice(device_index_); GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { @@ -277,6 +278,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) if (is_controller_) { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "index_controller"); vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. } else { From a7b17262a3761dcf8c3b9c3ca4911ce486eced59 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 06:16:40 -0600 Subject: [PATCH 042/117] Remove invalid function. --- src/TrackerDevice.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index c43c297..478059c 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -177,7 +177,6 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); GetDriver()->GetInput()->UpdateBooleanComponent(recenter_component_, recenter, 0); } - GetDriver()->GetInput()->ReportActiveDevice(device_index_); } } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { From 4a24e64cd7a3b2513c6a241f3b33c00e7b88e483 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 17:44:43 -0600 Subject: [PATCH 043/117] Cleanup inputs. More attempts to get inputs to register. --- src/TrackerDevice.cpp | 94 +++++++++++++------------------------------ src/TrackerDevice.hpp | 43 ++++++-------------- 2 files changed, 40 insertions(+), 97 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 478059c..a93361e 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -125,7 +125,10 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) if (is_controller_) { vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); vr::HmdMatrix34_t aimPose = poseMatrix; - aimPose.m[0][3] += 0.02f; // 2cm forward + // Move 2cm forward in the controller's local space + aimPose.m[0][3] += -0.02f * poseMatrix.m[2][0]; + aimPose.m[1][3] += -0.02f * poseMatrix.m[2][1]; + aimPose.m[2][3] += -0.02f * poseMatrix.m[2][2]; GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &aimPose, 0.0); } @@ -134,49 +137,14 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { if (is_controller_) { // Get inputs from protobuf - bool x_pressed = false; - bool y_pressed = false; - bool a_pressed = false; - bool b_pressed = false; - bool stick_click = false; - bool menu = false; - bool recenter = false; - float thumbstick_x = 0.0f; - float thumbstick_y = 0.0f; - float trigger = 0.0f; - float grip = 0.0f; - - thumbstick_x = controllerInput.thumbstick_x(); - thumbstick_y = controllerInput.thumbstick_y(); - trigger = controllerInput.trigger(); - grip = controllerInput.grip(); - stick_click = controllerInput.stick_click(); - if (is_left_hand_) { - x_pressed = controllerInput.button_1(); - y_pressed = controllerInput.button_2(); - menu = controllerInput.menu_recenter(); - GetDriver()->GetInput()->UpdateScalarComponent(left_trigger_component_, trigger, 0); - GetDriver()->GetInput()->UpdateScalarComponent(left_grip_value_component_, grip, 0); - GetDriver()->GetInput()->UpdateScalarComponent(left_stick_x_component_, thumbstick_x, 0); - GetDriver()->GetInput()->UpdateScalarComponent(left_stick_y_component_, thumbstick_y, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_x_component_, x_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_y_component_, y_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(left_stick_click_component_, stick_click, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, menu, 0); - } - else if (is_right_hand_) { - a_pressed = controllerInput.button_1(); - b_pressed = controllerInput.button_2(); - recenter = controllerInput.menu_recenter(); - GetDriver()->GetInput()->UpdateScalarComponent(right_trigger_component_, trigger, 0); - GetDriver()->GetInput()->UpdateScalarComponent(right_grip_value_component_, grip, 0); - GetDriver()->GetInput()->UpdateScalarComponent(right_stick_x_component_, thumbstick_x, 0); - GetDriver()->GetInput()->UpdateScalarComponent(right_stick_y_component_, thumbstick_y, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, a_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, b_pressed, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(right_stick_click_component_, stick_click, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(recenter_component_, recenter, 0); - } + GetDriver()->GetInput()->UpdateScalarComponent(trigger_component_, controllerInput.trigger(), 0); + GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); + GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); + GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(a_component_, controllerInput.button_1(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(b_component_, controllerInput.button_2(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0); } } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { @@ -277,8 +245,9 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) if (is_controller_) { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "index_controller"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "knuckles"); vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. + std::string manifestPath = "C:\\Program Files\\SlimeVR\\steamvr_input\\actions.json"; } else { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); @@ -311,30 +280,23 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - if (is_left_hand_) { - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/click", &this->button_x_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/x/touch", &this->button_x_component_touch_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/y/click", &this->button_y_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/y/touch", &this->button_y_component_touch_); - } - if (is_right_hand_) { - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/click", &this->button_a_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/touch", &this->button_a_component_touch_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); - } - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", is_left_hand_ ? &this->menu_component_ : &this->recenter_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", (is_left_hand_ ? &this->left_stick_click_component_ : &this->right_stick_click_component_)); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", (is_left_hand_ ? &this->left_stick_click_component_touch_ : &this->right_stick_click_component_touch_)); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/click", &this->button_a_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/touch", &this->button_a_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->menu_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->stick_click_component_)); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->stick_click_component_touch_)); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", is_left_hand_ ? &this->left_trigger_component_ : &this->right_trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", is_left_hand_ ? &this->left_trigger_component_touch_ : &this->right_trigger_component_touch_); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", is_left_hand_ ? &this->left_grip_value_component_ : &this->right_grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", is_left_hand_ ? &this->left_grip_value_component_touch_ : &this->right_grip_value_component_touch_); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", is_left_hand_ ? &this->left_stick_x_component_ : &this->right_stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", is_left_hand_ ? &this->left_stick_y_component_ : &this->right_stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_component_touch_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_value_component_touch_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &haptic_component_); + vr::Vrindput } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 1fd20c7..ee52bad 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -68,43 +68,24 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t raw_pose_component_handle_ = 0; vr::VRInputComponentHandle_t aim_pose_component_handle_ = 0; - vr::VRInputComponentHandle_t left_trigger_component_ = 0; - vr::VRInputComponentHandle_t left_grip_value_component_ = 0; - vr::VRInputComponentHandle_t left_stick_x_component_ = 0; - vr::VRInputComponentHandle_t left_stick_y_component_ = 0; - vr::VRInputComponentHandle_t button_x_component_ = 0; - vr::VRInputComponentHandle_t button_y_component_ = 0; - vr::VRInputComponentHandle_t left_stick_click_component_ = 0; + vr::VRInputComponentHandle_t trigger_component_ = 0; + vr::VRInputComponentHandle_t grip_value_component_ = 0; + vr::VRInputComponentHandle_t stick_x_component_ = 0; + vr::VRInputComponentHandle_t stick_y_component_ = 0; + vr::VRInputComponentHandle_t button_a_component_ = 0; + vr::VRInputComponentHandle_t button_b_component_ = 0; + vr::VRInputComponentHandle_t stick_click_component_ = 0; vr::VRInputComponentHandle_t menu_component_ = 0; - vr::VRInputComponentHandle_t left_trigger_component_touch_ = 0; - vr::VRInputComponentHandle_t left_grip_value_component_touch_ = 0; - vr::VRInputComponentHandle_t left_stick_x_component_touch_ = 0; - vr::VRInputComponentHandle_t left_stick_y_component_touch_ = 0; + vr::VRInputComponentHandle_t trigger_component_touch_ = 0; + vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; vr::VRInputComponentHandle_t button_x_component_touch_ = 0; vr::VRInputComponentHandle_t button_y_component_touch_ = 0; - vr::VRInputComponentHandle_t left_stick_click_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; vr::VRInputComponentHandle_t menu_component_touch_ = 0; - vr::VRInputComponentHandle_t right_trigger_component_ = 0; - vr::VRInputComponentHandle_t right_grip_value_component_ = 0; - vr::VRInputComponentHandle_t right_stick_x_component_ = 0; - vr::VRInputComponentHandle_t right_stick_y_component_ = 0; - vr::VRInputComponentHandle_t button_a_component_ = 0; - vr::VRInputComponentHandle_t button_b_component_ = 0; - vr::VRInputComponentHandle_t right_stick_click_component_ = 0; - vr::VRInputComponentHandle_t recenter_component_ = 0; - - vr::VRInputComponentHandle_t right_trigger_component_touch_ = 0; - vr::VRInputComponentHandle_t right_grip_value_component_touch_ = 0; - vr::VRInputComponentHandle_t right_stick_x_component_touch_ = 0; - vr::VRInputComponentHandle_t right_stick_y_component_touch_ = 0; - vr::VRInputComponentHandle_t button_a_component_touch_ = 0; - vr::VRInputComponentHandle_t button_b_component_touch_ = 0; - vr::VRInputComponentHandle_t right_stick_click_component_touch_ = 0; - vr::VRInputComponentHandle_t recenter_component_touch_ = 0; - - bool is_controller_; bool is_left_hand_; bool is_right_hand_; From fddc0a604d8c066f7b368a3f885a3125f924e157 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 17:49:38 -0600 Subject: [PATCH 044/117] Remove invalid syntax --- src/TrackerDevice.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index a93361e..f53a01a 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -296,7 +296,6 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &haptic_component_); - vr::Vrindput } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) From 88981f78eac14d102d2010dca5e6634949bcd8c0 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 17:58:57 -0600 Subject: [PATCH 045/117] Fix missing references --- src/TrackerDevice.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index ee52bad..87dfdb7 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -81,8 +81,8 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; - vr::VRInputComponentHandle_t button_x_component_touch_ = 0; - vr::VRInputComponentHandle_t button_y_component_touch_ = 0; + vr::VRInputComponentHandle_t button_a_component_touch_ = 0; + vr::VRInputComponentHandle_t button_b_component_touch_ = 0; vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; vr::VRInputComponentHandle_t menu_component_touch_ = 0; From 2c8746869da75b66f839f436e1ec2fb155f054a7 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 18:03:56 -0600 Subject: [PATCH 046/117] Fix incorrect references. --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index f53a01a..e790fb1 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -141,8 +141,8 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(a_component_, controllerInput.button_1(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(b_component_, controllerInput.button_2(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, controllerInput.button_1(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, controllerInput.button_2(), 0); GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0); } From 28a50fda4e6021dd5ccffb338425e537d7305c0d Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 18:25:53 -0600 Subject: [PATCH 047/117] Fix invalid syntax --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index e790fb1..97a039d 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -285,8 +285,8 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->menu_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->stick_click_component_)); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->stick_click_component_touch_)); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->stick_click_component_touch_); // Scalar components GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); From 34f434b916dd23c33018c6f5a8b7e075344763f4 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 19:45:39 -0600 Subject: [PATCH 048/117] Adjust bindings to more closesly match valve index config. --- .../input/slimevr_controller_bindings.json | 36 ++++--------------- src/TrackerDevice.cpp | 9 +++-- 2 files changed, 10 insertions(+), 35 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index dd29d39..693fab7 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -6,7 +6,7 @@ "driver_name": "slimevr", "input_bindingui_mode": "controller_handed", "should_show_binding_errors": true, - "compatibility_mode_controller_type": "oculus_touch", + "compatibility_mode_controller_type": "knuckles", "input_bindingui_left": { "image": "{slimevr}/icons/slimevr_tracker_icon.png" }, @@ -47,23 +47,6 @@ "skeleton": "/skeleton/hand/right", "side": "right" }, - - "/input/x/click": { - "type": "button", - "click": true - }, - "/input/x/touch": { - "type": "button", - "touch": true - }, - "/input/y/click": { - "type": "button", - "click": true - }, - "/input/y/touch": { - "type": "button", - "touch": true - }, "/input/a/click": { "type": "button", "click": true @@ -80,20 +63,14 @@ "type": "button", "touch": true }, - "/input/system/click": { "type": "button", "click": true }, - "/input/menu/click": { + "/input/system/touch": { "type": "button", "click": true }, - "/input/recenter/click": { - "type": "button", - "click": true - }, - "/input/trigger/value": { "type": "trigger" }, "/input/trigger/touch": { "type": "button", @@ -104,14 +81,13 @@ "type": "button", "touch": true }, - - "/input/joystick/x": { "type": "scalar" }, - "/input/joystick/y": { "type": "scalar" }, - "/input/joystick/click": { + "/input/thumbstick/x": { "type": "scalar" }, + "/input/thumbstick/y": { "type": "scalar" }, + "/input/thumbstick/click": { "type": "button", "click": true }, - "/input/joystick/touch": { + "/input/thumbstick/touch": { "type": "button", "touch": true } diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 97a039d..1c532f1 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -247,7 +247,6 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "knuckles"); vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. - std::string manifestPath = "C:\\Program Files\\SlimeVR\\steamvr_input\\actions.json"; } else { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); @@ -285,16 +284,16 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->menu_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->stick_click_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->stick_click_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &this->stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/touch", &this->stick_click_component_touch_); // Scalar components GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_value_component_touch_); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/thumbstick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/thumbstick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &haptic_component_); } From 5ed6a3af935afcaab117df22e428740d297c9ec1 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 20:08:16 -0600 Subject: [PATCH 049/117] Adjust vr compositor bindings. --- .../input/vrcompositor_bindings_slimevr.json | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json index 162b232..371ac84 100644 --- a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -5,23 +5,83 @@ "description": "SlimeVR virtual controller", "app_key": "openvr.component.vrcompositor", "bindings": { - "/actions/default": { + "/actions/lasermouse": { "sources": [ { "path": "/user/hand/left/pose/aim" }, { "path": "/user/hand/right/pose/aim" } ] }, - "/actions/laser_click": { + "/actions/lasermouse/in/LeftClick": { "sources": [ { "path": "/user/hand/left/input/trigger/value" }, { "path": "/user/hand/right/input/trigger/value" } ] }, - "/actions/system": { + "/actions/lasermouse/out/Haptic": { + "sources": [ + { "path": "/user/hand/left/output/haptic" }, + { "path": "/user/hand/right/output/haptic" } + ] + }, + "/actions/controller/in/DoubleTap": { + "sources": [ + { "path": "/user/hand/left/input/double_tap/click" }, + { "path": "/user/hand/right/input/double_tap/click" } + ] + }, + "/actions/controller/in/TripleTap": { + "sources": [ + { "path": "/user/hand/left/input/triple_tap/click" }, + { "path": "/user/hand/right/input/triple_tap/click" } + ] + }, + "/actions/controller/in/A": { + "sources": [ + { "path": "/user/hand/left/input/a/click" }, + { "path": "/user/hand/right/input/a/click" } + ] + }, + "/actions/controller/in/B": { + "sources": [ + { "path": "/user/hand/left/input/b/click" }, + { "path": "/user/hand/right/input/b/click" } + ] + }, + "/actions/controller/in/Menu": { "sources": [ { "path": "/user/hand/left/input/system/click" }, { "path": "/user/hand/right/input/system/click" } ] + }, + "/actions/controller/in/ThumbstickX": { + "sources": [ + { "path": "/user/hand/left/input/joystick/x" }, + { "path": "/user/hand/right/input/joystick/x" } + ] + }, + "/actions/controller/in/ThumbstickY": { + "sources": [ + { "path": "/user/hand/left/input/joystick/y" }, + { "path": "/user/hand/right/input/joystick/y" } + ] + }, + "/actions/controller/in/ThumbstickClick": { + "sources": [ + { "path": "/user/hand/left/input/joystick/click" }, + { "path": "/user/hand/right/input/joystick/click" } + ] + }, + "/actions/controller/in/Grip": { + "sources": [ + { "path": "/user/hand/left/input/grip/value" }, + { "path": "/user/hand/right/input/grip/value" } + ] + }, + "/actions/controller/in/Trigger": { + "sources": [ + { "path": "/user/hand/left/input/trigger/value" }, + { "path": "/user/hand/right/input/trigger/value" } + ] } } } From cc7b68408a707db6e14500305925bd188c2a1e48 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 20:38:49 -0600 Subject: [PATCH 050/117] Add output to sources. --- .../input/vrcompositor_bindings_slimevr.json | 246 ++++++++++++------ 1 file changed, 161 insertions(+), 85 deletions(-) diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json index 371ac84..bb99ef7 100644 --- a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -1,87 +1,163 @@ { - "action_manifest_version": 1, - "controller_type": "slimevr_virtual_controller", - "interaction_profile": "/interaction_profiles/valve/index_controller", - "description": "SlimeVR virtual controller", - "app_key": "openvr.component.vrcompositor", - "bindings": { - "/actions/lasermouse": { - "sources": [ - { "path": "/user/hand/left/pose/aim" }, - { "path": "/user/hand/right/pose/aim" } - ] - }, - "/actions/lasermouse/in/LeftClick": { - "sources": [ - { "path": "/user/hand/left/input/trigger/value" }, - { "path": "/user/hand/right/input/trigger/value" } - ] - }, - "/actions/lasermouse/out/Haptic": { - "sources": [ - { "path": "/user/hand/left/output/haptic" }, - { "path": "/user/hand/right/output/haptic" } - ] - }, - "/actions/controller/in/DoubleTap": { - "sources": [ - { "path": "/user/hand/left/input/double_tap/click" }, - { "path": "/user/hand/right/input/double_tap/click" } - ] - }, - "/actions/controller/in/TripleTap": { - "sources": [ - { "path": "/user/hand/left/input/triple_tap/click" }, - { "path": "/user/hand/right/input/triple_tap/click" } - ] - }, - "/actions/controller/in/A": { - "sources": [ - { "path": "/user/hand/left/input/a/click" }, - { "path": "/user/hand/right/input/a/click" } - ] - }, - "/actions/controller/in/B": { - "sources": [ - { "path": "/user/hand/left/input/b/click" }, - { "path": "/user/hand/right/input/b/click" } - ] - }, - "/actions/controller/in/Menu": { - "sources": [ - { "path": "/user/hand/left/input/system/click" }, - { "path": "/user/hand/right/input/system/click" } - ] - }, - "/actions/controller/in/ThumbstickX": { - "sources": [ - { "path": "/user/hand/left/input/joystick/x" }, - { "path": "/user/hand/right/input/joystick/x" } - ] - }, - "/actions/controller/in/ThumbstickY": { - "sources": [ - { "path": "/user/hand/left/input/joystick/y" }, - { "path": "/user/hand/right/input/joystick/y" } - ] - }, - "/actions/controller/in/ThumbstickClick": { - "sources": [ - { "path": "/user/hand/left/input/joystick/click" }, - { "path": "/user/hand/right/input/joystick/click" } - ] - }, - "/actions/controller/in/Grip": { - "sources": [ - { "path": "/user/hand/left/input/grip/value" }, - { "path": "/user/hand/right/input/grip/value" } - ] - }, - "/actions/controller/in/Trigger": { - "sources": [ - { "path": "/user/hand/left/input/trigger/value" }, - { "path": "/user/hand/right/input/trigger/value" } - ] - } - } + "action_manifest_version": 1, + "controller_type": "slimevr_virtual_controller", + "interaction_profile": "/interaction_profiles/valve/index_controller", + "description": "SlimeVR virtual controller", + "app_key": "openvr.component.vrcompositor", + "bindings": { + "/actions/lasermouse": { + "poses": [ + { + "output": "/actions/lasermouse/in/Pointer", + "path": "/user/hand/left/pose/aim" + }, + { + "output": "/actions/lasermouse/in/Pointer", + "path": "/user/hand/right/pose/aim" + } + ], + "haptics": [ + { + "output": "/actions/lasermouse/out/Haptic", + "path": "/user/hand/left/output/haptic" + }, + { + "output": "/actions/lasermouse/out/Haptic", + "path": "/user/hand/right/output/haptic" + } + ] + }, + "/actions/lasermouse/in/LeftClick": { + "sources": [ + { + "output": "/actions/lasermouse/in/LeftClick", + "path": "/user/hand/left/input/trigger/value" + }, + { + "output": "/actions/lasermouse/in/LeftClick", + "path": "/user/hand/right/input/trigger/value" + } + ] + }, + "/actions/controller/in/DoubleTap": { + "sources": [ + { + "output": "/actions/controller/in/DoubleTap", + "path": "/user/hand/left/input/double_tap/click" + }, + { + "output": "/actions/controller/in/DoubleTap", + "path": "/user/hand/right/input/double_tap/click" + } + ] + }, + "/actions/controller/in/TripleTap": { + "sources": [ + { + "output": "/actions/controller/in/TripleTap", + "path": "/user/hand/left/input/triple_tap/click" + }, + { + "output": "/actions/controller/in/TripleTap", + "path": "/user/hand/right/input/triple_tap/click" + } + ] + }, + "/actions/controller/in/A": { + "sources": [ + { + "output": "/actions/controller/in/A", + "path": "/user/hand/left/input/a/click" + }, + { + "output": "/actions/controller/in/A", + "path": "/user/hand/right/input/a/click" + } + ] + }, + "/actions/controller/in/B": { + "sources": [ + { + "output": "/actions/controller/in/B", + "path": "/user/hand/left/input/b/click" + }, + { + "output": "/actions/controller/in/B", + "path": "/user/hand/right/input/b/click" + } + ] + }, + "/actions/controller/in/Menu": { + "sources": [ + { + "output": "/actions/controller/in/Menu", + "path": "/user/hand/left/input/system/click" + }, + { + "output": "/actions/controller/in/Menu", + "path": "/user/hand/right/input/system/click" + } + ] + }, + "/actions/controller/in/ThumbstickX": { + "sources": [ + { + "output": "/actions/controller/in/ThumbstickX", + "path": "/user/hand/left/input/thumbstick/x" + }, + { + "output": "/actions/controller/in/ThumbstickX", + "path": "/user/hand/right/input/thumbstick/x" + } + ] + }, + "/actions/controller/in/ThumbstickY": { + "sources": [ + { + "output": "/actions/controller/in/ThumbstickY", + "path": "/user/hand/left/input/thumbstick/y" + }, + { + "output": "/actions/controller/in/ThumbstickY", + "path": "/user/hand/right/input/thumbstick/y" + } + ] + }, + "/actions/controller/in/ThumbstickClick": { + "sources": [ + { + "output": "/actions/controller/in/ThumbstickClick", + "path": "/user/hand/left/input/thumbstick/click" + }, + { + "output": "/actions/controller/in/ThumbstickClick", + "path": "/user/hand/right/input/thumbstick/click" + } + ] + }, + "/actions/controller/in/Grip": { + "sources": [ + { + "output": "/actions/controller/in/Grip", + "path": "/user/hand/left/input/grip/value" + }, + { + "output": "/actions/controller/in/Grip", + "path": "/user/hand/right/input/grip/value" + } + ] + }, + "/actions/controller/in/Trigger": { + "sources": [ + { + "output": "/actions/controller/in/Trigger", + "path": "/user/hand/left/input/trigger/value" + }, + { + "output": "/actions/controller/in/Trigger", + "path": "/user/hand/right/input/trigger/value" + } + ] + } + } } From 5e6c9032f89fb75a803f38c39416791b7c246f5a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 22:39:13 -0600 Subject: [PATCH 051/117] Set more properties. --- .../slimevr/resources/input/slimevr_controller_bindings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 693fab7..211fe08 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -81,8 +81,8 @@ "type": "button", "touch": true }, - "/input/thumbstick/x": { "type": "scalar" }, - "/input/thumbstick/y": { "type": "scalar" }, + "/input/thumbstick/x": { "type": "joystick" }, + "/input/thumbstick/y": { "type": "joystick" }, "/input/thumbstick/click": { "type": "button", "click": true From 7c2d0aa185824233afe1c51df029bfe362418da8 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 22:39:28 -0600 Subject: [PATCH 052/117] Set more properties p2 --- src/TrackerDevice.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 1c532f1..b89e90b 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -226,11 +226,15 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set up a model "number" (not needed but good to have) if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, "SLMVR-CTRL"); } else { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, "SLMVR-TRK"); } + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, "SlimeVR"); + //// Hand selection if (is_left_hand_) { GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); @@ -270,12 +274,15 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); + // Set inputs if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; + vr::VRProperties()->SetUint64Property(props, vr::Prop_SupportedButtons_Uint64, supportedButtons); - vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &raw_pose_component_handle_); - vr::VRDriverInput()->CreatePoseComponent(props, "/pose/aim", &aim_pose_component_handle_); + GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/raw", &raw_pose_component_handle_); + GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/tip", &aim_pose_component_handle_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); From 493a9af923bceab7f338b60996bf4e1535dd4216 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 22:43:56 -0600 Subject: [PATCH 053/117] Use our own virtual controller. --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index b89e90b..0bf156a 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -249,7 +249,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) if (is_controller_) { vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "knuckles"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "slimevr_virtual_controller"); vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. } else { From 8d55300a95b649d8d38439cc7fa0dc8cc0dbbfa8 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 23:35:17 -0600 Subject: [PATCH 054/117] Dont offset the pointer position for now. --- src/TrackerDevice.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 0bf156a..da2f650 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -126,9 +126,9 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); vr::HmdMatrix34_t aimPose = poseMatrix; // Move 2cm forward in the controller's local space - aimPose.m[0][3] += -0.02f * poseMatrix.m[2][0]; - aimPose.m[1][3] += -0.02f * poseMatrix.m[2][1]; - aimPose.m[2][3] += -0.02f * poseMatrix.m[2][2]; + //aimPose.m[0][3] += -0.02f * poseMatrix.m[2][0]; + //aimPose.m[1][3] += -0.02f * poseMatrix.m[2][1]; + //aimPose.m[2][3] += -0.02f * poseMatrix.m[2][2]; GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &aimPose, 0.0); } From 00930b78bd24a574a90cf96a91397f404d1a2a6d Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 18 Jan 2026 23:47:03 -0600 Subject: [PATCH 055/117] Whats happens if we dont update the pose component --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index da2f650..25cf532 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -129,8 +129,8 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) //aimPose.m[0][3] += -0.02f * poseMatrix.m[2][0]; //aimPose.m[1][3] += -0.02f * poseMatrix.m[2][1]; //aimPose.m[2][3] += -0.02f * poseMatrix.m[2][2]; - GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); - GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &aimPose, 0.0); + //GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); + //GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &aimPose, 0.0); } GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } From d6a61cdcdcfa3b3568846018b0e6e148a200605a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 00:28:22 -0600 Subject: [PATCH 056/117] Dont really need to check if we're a controller if receiving buttons. --- src/TrackerDevice.cpp | 72 ++++++------------------------------------- 1 file changed, 10 insertions(+), 62 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 25cf532..6b7923c 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -122,30 +122,18 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) // Notify SteamVR that pose was updated last_pose_atomic_ = (last_pose_ = pose); - if (is_controller_) { - vr::HmdMatrix34_t poseMatrix = ToHmdMatrix(pose); - vr::HmdMatrix34_t aimPose = poseMatrix; - // Move 2cm forward in the controller's local space - //aimPose.m[0][3] += -0.02f * poseMatrix.m[2][0]; - //aimPose.m[1][3] += -0.02f * poseMatrix.m[2][1]; - //aimPose.m[2][3] += -0.02f * poseMatrix.m[2][2]; - //GetDriver()->GetInput()->UpdatePoseComponent(raw_pose_component_handle_, &poseMatrix, 0.0); - //GetDriver()->GetInput()->UpdatePoseComponent(aim_pose_component_handle_, &aimPose, 0.0); - } GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { - if (is_controller_) { - // Get inputs from protobuf - GetDriver()->GetInput()->UpdateScalarComponent(trigger_component_, controllerInput.trigger(), 0); - GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); - GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); - GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, controllerInput.button_1(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, controllerInput.button_2(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0); - } + // Get inputs from protobuf + GetDriver()->GetInput()->UpdateScalarComponent(trigger_component_, controllerInput.trigger(), 0); + GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); + GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); + GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, controllerInput.button_1(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, controllerInput.button_2(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0) } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) @@ -274,7 +262,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); - + // Set inputs if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); @@ -366,43 +354,3 @@ int SlimeVRDriver::TrackerDevice::GetDeviceId() { void SlimeVRDriver::TrackerDevice::SetDeviceId(int device_id) { device_id_ = device_id; } -vr::HmdMatrix34_t SlimeVRDriver::TrackerDevice::ToHmdMatrix(const vr::DriverPose_t& pose) { - vr::HmdMatrix34_t m; - - const auto& q = pose.qRotation; - const auto& p = pose.vecPosition; - - float x2 = q.x + q.x; - float y2 = q.y + q.y; - float z2 = q.z + q.z; - - float xx2 = q.x * x2; - float yy2 = q.y * y2; - float zz2 = q.z * z2; - - float xy2 = q.x * y2; - float xz2 = q.x * z2; - float yz2 = q.y * z2; - - float wx2 = q.w * x2; - float wy2 = q.w * y2; - float wz2 = q.w * z2; - - m.m[0][0] = 1.0f - (yy2 + zz2); - m.m[0][1] = xy2 - wz2; - m.m[0][2] = xz2 + wy2; - m.m[0][3] = p[0]; - - m.m[1][0] = xy2 + wz2; - m.m[1][1] = 1.0f - (xx2 + zz2); - m.m[1][2] = yz2 - wx2; - m.m[1][3] = p[1]; - - m.m[2][0] = xz2 - wy2; - m.m[2][1] = yz2 + wx2; - m.m[2][2] = 1.0f - (xx2 + yy2); - m.m[2][3] = p[2]; - - return m; -} - From a2761367f31b7ca448bee91a86e0fe1808bcd6b1 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 00:56:36 -0600 Subject: [PATCH 057/117] Fix syntax error --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 6b7923c..4ddffd6 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -133,7 +133,7 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, controllerInput.button_1(), 0); GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, controllerInput.button_2(), 0); GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0) + GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0); } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) From f5a1a7da4c720b889f8b1a6edb56179d466981db Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 04:50:27 -0600 Subject: [PATCH 058/117] Serial numbers confuse VRChat for some reason --- src/TrackerDevice.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 4ddffd6..66e1637 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -214,11 +214,9 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Set up a model "number" (not needed but good to have) if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, "SLMVR-CTRL"); } else { GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, "SLMVR-TRK"); } GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, "SlimeVR"); From 1584b7a656b0620ec53e561d8d56df61cdf80c49 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 17:46:49 -0600 Subject: [PATCH 059/117] More hecking bindings. More trail and error getting SteamVR to take our bindings. Added trackpad stuff. --- .../input/slimevr_controller_bindings.json | 14 + .../input/vrcompositor_bindings_slimevr.json | 280 +++++++++++++----- src/TrackerDevice.cpp | 16 +- src/TrackerDevice.hpp | 12 +- src/bridge/ProtobufMessages.proto | 15 +- 5 files changed, 254 insertions(+), 83 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 211fe08..91c8729 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -63,6 +63,10 @@ "type": "button", "touch": true }, + "/input/system": { + "type": "button", + "click": true + }, "/input/system/click": { "type": "button", "click": true @@ -81,6 +85,16 @@ "type": "button", "touch": true }, + "/input/trackpad/position": { "type": "joystick" }, + "/input/trackpad/click": { + "type": "button", + "click": true + }, + "/input/trackpad/touch": { + "type": "button", + "touch": true + }, + "/input/thumbstick/x": { "type": "joystick" }, "/input/thumbstick/y": { "type": "joystick" }, "/input/thumbstick/click": { diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json index bb99ef7..6273473 100644 --- a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -9,11 +9,11 @@ "poses": [ { "output": "/actions/lasermouse/in/Pointer", - "path": "/user/hand/left/pose/aim" + "path": "/user/hand/left/pose/tip" }, { "output": "/actions/lasermouse/in/Pointer", - "path": "/user/hand/right/pose/aim" + "path": "/user/hand/right/pose/tip" } ], "haptics": [ @@ -25,137 +25,277 @@ "output": "/actions/lasermouse/out/Haptic", "path": "/user/hand/right/output/haptic" } - ] - }, - "/actions/lasermouse/in/LeftClick": { + ], "sources": [ { - "output": "/actions/lasermouse/in/LeftClick", - "path": "/user/hand/left/input/trigger/value" + "inputs": { "click": { "output": "/actions/lasermouse/in/LeftClick" } }, + "mode": "button", + "parameters": { + "click_activate_threshold": "0.65", + "click_deactivate_threshold": "0.6" + }, + "path": "/user/hand/left/input/trigger" }, { - "output": "/actions/lasermouse/in/LeftClick", - "path": "/user/hand/right/input/trigger/value" - } - ] - }, - "/actions/controller/in/DoubleTap": { - "sources": [ + "inputs": { "click": { "output": "/actions/lasermouse/in/LeftClick" } }, + "mode": "button", + "parameters": { + "click_activate_threshold": "0.65", + "click_deactivate_threshold": "0.6" + }, + "path": "/user/hand/right/input/trigger" + }, { - "output": "/actions/controller/in/DoubleTap", - "path": "/user/hand/left/input/double_tap/click" + "inputs": { "click": { "output": "/actions/lasermouse/in/Back" } }, + "mode": "button", + "path": "/user/hand/left/input/b" }, { - "output": "/actions/controller/in/DoubleTap", - "path": "/user/hand/right/input/double_tap/click" - } - ] - }, - "/actions/controller/in/TripleTap": { - "sources": [ + "inputs": { "click": { "output": "/actions/lasermouse/in/Back" } }, + "mode": "button", + "path": "/user/hand/right/input/b" + }, + { + "inputs": { "click": { "output": "/actions/lasermouse/in/Home" } }, + "mode": "button", + "path": "/user/hand/left/input/a" + }, + { + "inputs": { "click": { "output": "/actions/lasermouse/in/Home" } }, + "mode": "button", + "path": "/user/hand/right/input/a" + }, + { + "inputs": { + "position": { "output": "/actions/lasermouse/in/TrackpadValue" }, + "touch": { "output": "/actions/lasermouse/in/TrackpadTouch" } + }, + "mode": "trackpad", + "path": "/user/hand/left/input/trackpad" + }, + { + "inputs": { + "position": { "output": "/actions/lasermouse/in/TrackpadValue" }, + "touch": { "output": "/actions/lasermouse/in/TrackpadTouch" } + }, + "mode": "trackpad", + "path": "/user/hand/right/input/trackpad" + }, { - "output": "/actions/controller/in/TripleTap", - "path": "/user/hand/left/input/triple_tap/click" + "inputs": { "click": { "output": "/actions/lasermouse/in/MiddleClick" } }, + "mode": "joystick", + "path": "/user/hand/left/input/thumbstick" }, { - "output": "/actions/controller/in/TripleTap", - "path": "/user/hand/right/input/triple_tap/click" + "inputs": { "click": { "output": "/actions/lasermouse/in/MiddleClick" } }, + "mode": "joystick", + "path": "/user/hand/right/input/thumbstick" } ] }, - "/actions/controller/in/A": { + "/actions/lasermouse_secondary": { "sources": [ { - "output": "/actions/controller/in/A", - "path": "/user/hand/left/input/a/click" + "inputs": { "click": { "output": "/actions/lasermouse_secondary/in/SwitchLaserHand" } }, + "mode": "button", + "path": "/user/hand/left/input/trigger" }, { - "output": "/actions/controller/in/A", - "path": "/user/hand/right/input/a/click" + "inputs": { "click": { "output": "/actions/lasermouse_secondary/in/SwitchLaserHand" } }, + "mode": "button", + "path": "/user/hand/right/input/trigger" } ] }, - "/actions/controller/in/B": { + "/actions/scroll_discrete": { "sources": [ { - "output": "/actions/controller/in/B", - "path": "/user/hand/left/input/b/click" + "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, + "mode": "scroll", + "parameters": { "scroll_mode": "discrete" }, + "path": "/user/hand/left/input/thumbstick" }, { - "output": "/actions/controller/in/B", - "path": "/user/hand/right/input/b/click" + "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, + "mode": "scroll", + "parameters": { "scroll_mode": "discrete" }, + "path": "/user/hand/right/input/thumbstick" + }, + { + "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, + "mode": "scroll", + "parameters": { + "scroll_mode": "discrete", + "discrete_scroll_trackpad_accumthreshold_onmove": "0.28", + "discrete_scroll_trackpad_accumthreshold_onreversal": "0.84", + "discrete_scroll_trackpad_accumthreshold_ontouch": "0.78", + "discrete_scroll_trackpad_noisethreshold_onmove": "0.01", + "discrete_scroll_trackpad_noisethreshold_onreversal": "0.045", + "discrete_scroll_trackpad_noisethreshold_ontouch": "0.15", + "discrete_scroll_trackpad_slideandhold_borderbottom": "-0.65", + "discrete_scroll_trackpad_slideandhold_bordertop": "0.55" + }, + "path": "/user/hand/left/input/trackpad" + }, + { + "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, + "mode": "scroll", + "parameters": { + "scroll_mode": "discrete", + "discrete_scroll_trackpad_accumthreshold_onmove": "0.28", + "discrete_scroll_trackpad_accumthreshold_onreversal": "0.84", + "discrete_scroll_trackpad_accumthreshold_ontouch": "0.78", + "discrete_scroll_trackpad_noisethreshold_onmove": "0.01", + "discrete_scroll_trackpad_noisethreshold_onreversal": "0.045", + "discrete_scroll_trackpad_noisethreshold_ontouch": "0.15", + "discrete_scroll_trackpad_slideandhold_borderbottom": "-0.65", + "discrete_scroll_trackpad_slideandhold_bordertop": "0.55" + }, + "path": "/user/hand/right/input/trackpad" } ] }, - "/actions/controller/in/Menu": { + "/actions/scroll_smooth": { "sources": [ { - "output": "/actions/controller/in/Menu", - "path": "/user/hand/left/input/system/click" + "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, + "mode": "scroll", + "parameters": { "scroll_mode": "smooth" }, + "path": "/user/hand/left/input/thumbstick" + }, + { + "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, + "mode": "scroll", + "parameters": { "scroll_mode": "smooth" }, + "path": "/user/hand/right/input/thumbstick" + }, + { + "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, + "mode": "scroll", + "parameters": { "scroll_mode": "smooth" }, + "path": "/user/hand/left/input/trackpad" }, { - "output": "/actions/controller/in/Menu", - "path": "/user/hand/right/input/system/click" + "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, + "mode": "scroll", + "parameters": { "scroll_mode": "smooth" }, + "path": "/user/hand/right/input/trackpad" } ] }, - "/actions/controller/in/ThumbstickX": { - "sources": [ + "/actions/system": { + "chords": [ { - "output": "/actions/controller/in/ThumbstickX", - "path": "/user/hand/left/input/thumbstick/x" + "inputs": [ + [ "/user/hand/left/input/system", "held" ], + [ "/user/hand/left/input/trigger", "click" ] + ], + "output": "/actions/system/in/TakeScreenshot" }, { - "output": "/actions/controller/in/ThumbstickX", - "path": "/user/hand/right/input/thumbstick/x" + "inputs": [ + [ "/user/hand/right/input/system", "held" ], + [ "/user/hand/right/input/trigger", "click" ] + ], + "output": "/actions/system/in/TakeScreenshot" } - ] - }, - "/actions/controller/in/ThumbstickY": { + ], "sources": [ { - "output": "/actions/controller/in/ThumbstickY", - "path": "/user/hand/left/input/thumbstick/y" + "inputs": { + "click": { "output": "/actions/system/in/ToggleDashboard" }, + "double": { "output": "/actions/system/in/ToggleRoomView" }, + "held": { "output": "/actions/system/in/SystemButtonChord" } + }, + "mode": "button", + "path": "/user/hand/left/input/system" }, { - "output": "/actions/controller/in/ThumbstickY", - "path": "/user/hand/right/input/thumbstick/y" + "inputs": { + "click": { "output": "/actions/system/in/ToggleDashboard" }, + "double": { "output": "/actions/system/in/ToggleRoomView" }, + "held": { "output": "/actions/system/in/SystemButtonChord" } + }, + "mode": "button", + "path": "/user/hand/right/input/system" } ] }, - "/actions/controller/in/ThumbstickClick": { + "/actions/quickrecenter": { "sources": [ { - "output": "/actions/controller/in/ThumbstickClick", - "path": "/user/hand/left/input/thumbstick/click" + "inputs": { "long": { "output": "/actions/quickrecenter/in/Recenter" } }, + "parameters": { + "long_press_delay": 1.0, + "long_press_expiry": 3.0 + }, + "mode": "button", + "path": "/user/hand/left/input/system" }, { - "output": "/actions/controller/in/ThumbstickClick", - "path": "/user/hand/right/input/thumbstick/click" + "inputs": { "long": { "output": "/actions/quickrecenter/in/Recenter" } }, + "parameters": { + "long_press_delay": 1.0, + "long_press_expiry": 3.0 + }, + "mode": "button", + "path": "/user/hand/right/input/system" } ] }, - "/actions/controller/in/Grip": { + "/actions/roomsetup_floor": { "sources": [ { - "output": "/actions/controller/in/Grip", - "path": "/user/hand/left/input/grip/value" + "inputs": { "position": { "output": "/actions/roomsetup_floor/in/FloorFineAdjust" } }, + "mode": "joystick", + "path": "/user/hand/left/input/thumbstick" }, { - "output": "/actions/controller/in/Grip", - "path": "/user/hand/right/input/grip/value" + "inputs": { "position": { "output": "/actions/roomsetup_floor/in/FloorFineAdjust" } }, + "mode": "joystick", + "path": "/user/hand/right/input/thumbstick" } ] }, - "/actions/controller/in/Trigger": { + "/actions/dualanalog": { "sources": [ { - "output": "/actions/controller/in/Trigger", - "path": "/user/hand/left/input/trigger/value" + "inputs": { "click": { "output": "/actions/dualanalog/in/ModeSwitch1" } }, + "mode": "button", + "path": "/user/hand/left/input/a" + }, + { + "inputs": { "click": { "output": "/actions/dualanalog/in/ModeSwitch2" } }, + "mode": "button", + "path": "/user/hand/right/input/a" + }, + { + "inputs": { + "click": { "output": "/actions/dualanalog/in/LeftClick" }, + "position": { "output": "/actions/dualanalog/in/LeftValue" }, + "touch": { "output": "/actions/dualanalog/in/LeftTouch" } + }, + "mode": "joystick", + "path": "/user/hand/left/input/thumbstick" + }, + { + "inputs": { + "click": { "output": "/actions/dualanalog/in/RightClick" }, + "position": { "output": "/actions/dualanalog/in/RightValue" }, + "touch": { "output": "/actions/dualanalog/in/RightTouch" } + }, + "mode": "joystick", + "path": "/user/hand/right/input/thumbstick" + }, + { + "inputs": { "click": { "output": "/actions/dualanalog/in/LeftClick" } }, + "mode": "button", + "path": "/user/hand/left/input/trigger" }, { - "output": "/actions/controller/in/Trigger", - "path": "/user/hand/right/input/trigger/value" + "inputs": { "click": { "output": "/actions/dualanalog/in/RightClick" } }, + "mode": "button", + "path": "/user/hand/right/input/trigger" } ] } diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 66e1637..3761954 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -133,7 +133,8 @@ void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerIn GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, controllerInput.button_1(), 0); GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, controllerInput.button_2(), 0); GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(menu_component_, controllerInput.menu_recenter(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(system_component, controllerInput.menu_recenter(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(system_component_chord, controllerInput.menu_recenter(), 0); } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) @@ -267,8 +268,8 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; vr::VRProperties()->SetUint64Property(props, vr::Prop_SupportedButtons_Uint64, supportedButtons); - GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/raw", &raw_pose_component_handle_); - GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/tip", &aim_pose_component_handle_); + GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/raw", &this->raw_pose_component_handle_); + GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/tip", &this->aim_pose_component_handle_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); @@ -276,7 +277,10 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/touch", &this->button_a_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->menu_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_component); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system", &this->system_component); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this>trackpad_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &this->trackpad_touch_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &this->stick_click_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/touch", &this->stick_click_component_touch_); @@ -285,9 +289,11 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_value_component_touch_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/thumbstick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/thumbstick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &haptic_component_); + GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &this->haptic_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 87dfdb7..65278fd 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -74,8 +74,16 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t stick_y_component_ = 0; vr::VRInputComponentHandle_t button_a_component_ = 0; vr::VRInputComponentHandle_t button_b_component_ = 0; + + vr::VRInputComponentHandle_t trackpad_x_component_ = 0; + vr::VRInputComponentHandle_t trackpad_y_component_ = 0; + vr::VRInputComponentHandle_t trackpad_click_component_ = 0; + vr::VRInputComponentHandle_t trackpad_touch_component_ = 0; + + vr::VRInputComponentHandle_t stick_click_component_ = 0; - vr::VRInputComponentHandle_t menu_component_ = 0; + vr::VRInputComponentHandle_t system_component = 0; + vr::VRInputComponentHandle_t system_component_chord = 0; vr::VRInputComponentHandle_t trigger_component_touch_ = 0; vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; @@ -84,7 +92,7 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t button_a_component_touch_ = 0; vr::VRInputComponentHandle_t button_b_component_touch_ = 0; vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; - vr::VRInputComponentHandle_t menu_component_touch_ = 0; + vr::VRInputComponentHandle_t system_component_touch_ = 0; bool is_controller_; bool is_left_hand_; diff --git a/src/bridge/ProtobufMessages.proto b/src/bridge/ProtobufMessages.proto index 179446f..a71171a 100644 --- a/src/bridge/ProtobufMessages.proto +++ b/src/bridge/ProtobufMessages.proto @@ -124,12 +124,15 @@ message ControllerInput { int32 tracker_id = 1; float thumbstick_x = 2; float thumbstick_y = 3; - float trigger = 4; - float grip = 5; - bool button_1 = 6; - bool button_2 = 7; - bool menu_recenter = 8; - bool stick_click = 9; + float trackpad_x = 4; + float trackpad_y = 5; + float trigger = 6; + float grip = 7; + bool button_1 = 8; + bool button_2 = 9; + bool menu_recenter = 10; + bool stick_click = 11; + bool trackpad_click = 12; } message UserAction { From 711f431e1981ab5e192d2e9be50bcdad492c685a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 17:53:28 -0600 Subject: [PATCH 060/117] Fix syntax error. --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 3761954..1fbf8dd 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -279,7 +279,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_component); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system", &this->system_component); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this>trackpad_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this->trackpad_click_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &this->trackpad_touch_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &this->stick_click_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/touch", &this->stick_click_component_touch_); From 0becf5f3cb0407141d3132f962bd34003c52c9f9 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 19:35:44 -0600 Subject: [PATCH 061/117] Tweak mappings slightly se see if that gets mappings to work. --- .../slimevr/resources/input/slimevr_controller_bindings.json | 2 +- .../resources/input/vrcompositor_bindings_slimevr.json | 4 ++-- src/TrackerDevice.cpp | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 91c8729..45ec433 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -76,7 +76,7 @@ "click": true }, "/input/trigger/value": { "type": "trigger" }, - "/input/trigger/touch": { + "/input/trigger/click": { "type": "button", "touch": true }, diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json index 6273473..f7c4caa 100644 --- a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -187,14 +187,14 @@ "chords": [ { "inputs": [ - [ "/user/hand/left/input/system", "held" ], + [ "/user/hand/left/input/system", "click" ], [ "/user/hand/left/input/trigger", "click" ] ], "output": "/actions/system/in/TakeScreenshot" }, { "inputs": [ - [ "/user/hand/right/input/system", "held" ], + [ "/user/hand/right/input/system", "click" ], [ "/user/hand/right/input/trigger", "click" ] ], "output": "/actions/system/in/TakeScreenshot" diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 1fbf8dd..d6e2f2c 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -127,6 +127,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { // Get inputs from protobuf GetDriver()->GetInput()->UpdateScalarComponent(trigger_component_, controllerInput.trigger(), 0); + GetDriver()->GetInput()->UpdateBooleanComponent(trigger_component_touch, controllerInput.trigger() > 0.5f, 0); GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); @@ -278,7 +279,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_component); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system", &this->system_component); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system", &this->system_component_chord); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this->trackpad_click_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &this->trackpad_touch_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &this->stick_click_component_); @@ -286,7 +287,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Scalar components GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/click", &this->trigger_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_value_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); From 5bdd1b2195f9eb0b6a44792f357c50199b6fb130 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Mon, 19 Jan 2026 21:00:50 -0600 Subject: [PATCH 062/117] Fix syntax error. --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index d6e2f2c..381d8ca 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -127,7 +127,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { // Get inputs from protobuf GetDriver()->GetInput()->UpdateScalarComponent(trigger_component_, controllerInput.trigger(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(trigger_component_touch, controllerInput.trigger() > 0.5f, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); From ef7dc1e9f43b5201563667f367d881bfa9ee92d5 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 16:38:45 -0600 Subject: [PATCH 063/117] Add actions.json --- driver/slimevr/resources/input/actions.json | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 driver/slimevr/resources/input/actions.json diff --git a/driver/slimevr/resources/input/actions.json b/driver/slimevr/resources/input/actions.json new file mode 100644 index 0000000..6c62a38 --- /dev/null +++ b/driver/slimevr/resources/input/actions.json @@ -0,0 +1,41 @@ +{ + "action_manifest_version": 1, + "controller_type": "slimevr_virtual_controller", + "interaction_profile": "/interaction_profiles/valve/index_controller", + "description": "SlimeVR virtual controller", + "app_key": "openvr.component.slimevr", + "actions": { + "pose": [ + { "name": "/actions/main/in/PoseRaw" }, + { "name": "/actions/main/in/PoseAim" } + ], + "haptic": [ + { "name": "/actions/main/out/Haptic" } + ], + "boolean": [ + { "name": "/actions/main/in/DoubleTap" }, + { "name": "/actions/main/in/TripleTap" }, + { "name": "/actions/main/in/A" }, + { "name": "/actions/main/in/B" }, + { "name": "/actions/main/in/System" }, + { "name": "/actions/main/in/TriggerClick" }, + { "name": "/actions/main/in/GripTouch" }, + { "name": "/actions/main/in/TrackpadClick" }, + { "name": "/actions/main/in/TrackpadTouch" }, + { "name": "/actions/main/in/ThumbstickClick" }, + { "name": "/actions/main/in/ThumbstickTouch" } + ], + "scalar": [ + { "name": "/actions/main/in/TriggerValue" }, + { "name": "/actions/main/in/GripValue" }, + { "name": "/actions/main/in/TrackpadX" }, + { "name": "/actions/main/in/TrackpadY" }, + { "name": "/actions/main/in/ThumbstickX" }, + { "name": "/actions/main/in/ThumbstickY" } + ], + "skeleton": [ + { "name": "/actions/main/in/SkeletonLeft" }, + { "name": "/actions/main/in/SkeletonRight" } + ] + } +} From 9e1442cbd65f0c62e70d4a5a9548d2fa728c45f9 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 17:47:49 -0600 Subject: [PATCH 064/117] New day, more binding stuff. --- .../input/slimevr_controller_bindings.json | 160 +++++++++--------- .../slimevr_controller_bindings_legacy.json | 24 +++ .../input/vrcompositor_bindings_slimevr.json | 20 +-- .../resources/localization/localization.json | 8 + src/TrackerDevice.cpp | 13 +- src/TrackerDevice.hpp | 3 + 6 files changed, 129 insertions(+), 99 deletions(-) create mode 100644 driver/slimevr/resources/input/slimevr_controller_bindings_legacy.json diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings.json b/driver/slimevr/resources/input/slimevr_controller_bindings.json index 45ec433..aa9f755 100644 --- a/driver/slimevr/resources/input/slimevr_controller_bindings.json +++ b/driver/slimevr/resources/input/slimevr_controller_bindings.json @@ -14,102 +14,96 @@ "image": "{slimevr}/icons/slimevr_tracker_icon.png" }, "input_source": { - "/pose/raw": { - "type": "pose", - "binding_image_point": [ 80, 80 ] - }, - "/pose/aim": { - "type": "pose", - "binding_image_point": [ 80, 80 ] - }, - - "/output/haptic": { - "type": "vibration", - "binding_image_point": [ 80, 80 ] + "/input/system": { + "type": "button", + "binding_image_point": [ 34, 45 ], + "order": 1 }, - - "/input/double_tap": { + "/input/a": { "type": "button", - "click": true + "binding_image_point": [ 26, 42 ], + "order": 5 }, - "/input/triple_tap": { + "/input/b": { "type": "button", - "click": true + "binding_image_point": [ 18, 37 ], + "order": 6 + }, + "/input/trigger": { + "type": "trigger", + "binding_image_point": [ 11, 60 ], + "order": 2 + }, + "/input/trackpad": { + "type": "trackpad", + "binding_image_point": [ 27, 37 ], + "order": 3 + }, + "/input/grip": { + "type": "trigger", + "binding_image_point": [ 47, 86 ], + "order": 7 + }, + "/input/thumbstick": { + "type": "joystick", + "binding_image_point": [ 31, 26 ], + "order": 4 + }, + "/input/pinch": { + "type": "pinch", + "binding_image_point": [ 27, 37 ], + "value_source": "/input/trigger", + "capsense_source": "/input/finger/index", + "force_source": "/input/trackpad", + "order": 7 + }, + + "/input/finger/index": { + "type": "trigger", + "visibility": "InputValueVisibility_AvailableButHidden", + "binding_image_point": [ 56, 86 ], + "order": 7 + }, + "/input/finger/middle": { + "type": "trigger", + "visibility": "InputValueVisibility_AvailableButHidden", + "binding_image_point": [ 56, 86 ], + "order": 8 + }, + "/input/finger/ring": { + "type": "trigger", + "visibility": "InputValueVisibility_AvailableButHidden", + "binding_image_point": [ 56, 86 ], + "order": 9 + }, + "/input/finger/pinky": { + "type": "trigger", + "visibility": "InputValueVisibility_AvailableButHidden", + "binding_image_point": [ 56, 86 ], + "order": 10 }, + "/pose/raw": { + "type": "pose", + "binding_image_point": [ 5, 35 ] + }, "/input/skeleton/left": { "type": "skeleton", "skeleton": "/skeleton/hand/left", - "side": "left" + "side": "left", + "binding_image_point": [ 5, 35 ] + }, "/input/skeleton/right": { "type": "skeleton", "skeleton": "/skeleton/hand/right", - "side": "right" - }, - "/input/a/click": { - "type": "button", - "click": true - }, - "/input/a/touch": { - "type": "button", - "touch": true - }, - "/input/b/click": { - "type": "button", - "click": true - }, - "/input/b/touch": { - "type": "button", - "touch": true - }, - "/input/system": { - "type": "button", - "click": true - }, - "/input/system/click": { - "type": "button", - "click": true - }, - "/input/system/touch": { - "type": "button", - "click": true - }, - "/input/trigger/value": { "type": "trigger" }, - "/input/trigger/click": { - "type": "button", - "touch": true - }, - "/input/grip/value": { "type": "trigger" }, - "/input/grip/touch": { - "type": "button", - "touch": true - }, - "/input/trackpad/position": { "type": "joystick" }, - "/input/trackpad/click": { - "type": "button", - "click": true - }, - "/input/trackpad/touch": { - "type": "button", - "touch": true + "side": "right", + "binding_image_point": [ 5, 35 ] }, - "/input/thumbstick/x": { "type": "joystick" }, - "/input/thumbstick/y": { "type": "joystick" }, - "/input/thumbstick/click": { - "type": "button", - "click": true - }, - "/input/thumbstick/touch": { - "type": "button", - "touch": true - } - }, - "default_bindings": [ - { - "app_key": "openvr.component.vrcompositor", - "binding_url": "vrcompositor_bindings_slimevr.json" + "/output/haptic": { + "type": "vibration", + "binding_image_point": [ 5, 35 ] } - ] -} + } +} \ No newline at end of file diff --git a/driver/slimevr/resources/input/slimevr_controller_bindings_legacy.json b/driver/slimevr/resources/input/slimevr_controller_bindings_legacy.json new file mode 100644 index 0000000..487766b --- /dev/null +++ b/driver/slimevr/resources/input/slimevr_controller_bindings_legacy.json @@ -0,0 +1,24 @@ +{ + "bindings": { + "/actions/legacy": { + "haptics": [ + ], + "poses": [ + { + "output": "/actions/legacy/in/Left_Pose", + "path": "/user/hand/left/pose/raw" + }, + { + "output": "/actions/legacy/in/Right_Pose", + "path": "/user/hand/right/pose/raw" + } + ], + "sources": [ + ] + } + }, + "controller_type": "slimevr_virtual_controller", + "description": "Default binding values for legacy apps using the soft knuckles controller", + "name": "SlimeVR Legacy Defaults", + "simulated_actions": [] +} \ No newline at end of file diff --git a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json index f7c4caa..ef9e0bf 100644 --- a/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json +++ b/driver/slimevr/resources/input/vrcompositor_bindings_slimevr.json @@ -84,12 +84,12 @@ { "inputs": { "click": { "output": "/actions/lasermouse/in/MiddleClick" } }, "mode": "joystick", - "path": "/user/hand/left/input/thumbstick" + "path": "/user/hand/left/input/joystick" }, { "inputs": { "click": { "output": "/actions/lasermouse/in/MiddleClick" } }, "mode": "joystick", - "path": "/user/hand/right/input/thumbstick" + "path": "/user/hand/right/input/joystick" } ] }, @@ -113,13 +113,13 @@ "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, "mode": "scroll", "parameters": { "scroll_mode": "discrete" }, - "path": "/user/hand/left/input/thumbstick" + "path": "/user/hand/left/input/joystick" }, { "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, "mode": "scroll", "parameters": { "scroll_mode": "discrete" }, - "path": "/user/hand/right/input/thumbstick" + "path": "/user/hand/right/input/joystick" }, { "inputs": { "scroll": { "output": "/actions/scroll_discrete/in/Scroll" } }, @@ -161,13 +161,13 @@ "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, "mode": "scroll", "parameters": { "scroll_mode": "smooth" }, - "path": "/user/hand/left/input/thumbstick" + "path": "/user/hand/left/input/joystick" }, { "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, "mode": "scroll", "parameters": { "scroll_mode": "smooth" }, - "path": "/user/hand/right/input/thumbstick" + "path": "/user/hand/right/input/joystick" }, { "inputs": { "scroll": { "output": "/actions/scroll_smooth/in/Scroll" } }, @@ -248,12 +248,12 @@ { "inputs": { "position": { "output": "/actions/roomsetup_floor/in/FloorFineAdjust" } }, "mode": "joystick", - "path": "/user/hand/left/input/thumbstick" + "path": "/user/hand/left/input/joystick" }, { "inputs": { "position": { "output": "/actions/roomsetup_floor/in/FloorFineAdjust" } }, "mode": "joystick", - "path": "/user/hand/right/input/thumbstick" + "path": "/user/hand/right/input/joystick" } ] }, @@ -276,7 +276,7 @@ "touch": { "output": "/actions/dualanalog/in/LeftTouch" } }, "mode": "joystick", - "path": "/user/hand/left/input/thumbstick" + "path": "/user/hand/left/input/joystick" }, { "inputs": { @@ -285,7 +285,7 @@ "touch": { "output": "/actions/dualanalog/in/RightTouch" } }, "mode": "joystick", - "path": "/user/hand/right/input/thumbstick" + "path": "/user/hand/right/input/joystick" }, { "inputs": { "click": { "output": "/actions/dualanalog/in/LeftClick" } }, diff --git a/driver/slimevr/resources/localization/localization.json b/driver/slimevr/resources/localization/localization.json index 75560ce..8e62825 100644 --- a/driver/slimevr/resources/localization/localization.json +++ b/driver/slimevr/resources/localization/localization.json @@ -13,6 +13,14 @@ "slimevr_virtual_controller": "SlimeVR Virtual Controller", "/input/double_tap": "Double tap", "/input/triple_tap": "Triple tap", + "/input/a": "A Button", + "/input/b": "B Button", + "/input/pinch": "Pinch Gesture", + "/input/finger/index": "Index Finger", + "/input/finger/middle": "Middle Finger", + "/input/finger/ring": "Ring Finger", + "/input/finger/pinky": "Pinky Finger", + "/input/thumbstick": "Thumb Stick", "slimevr_tracker" : "SlimeVR Tracker", "slimevr_tracker_left_foot" : "SlimeVR Tracker on Left Foot", "slimevr_tracker_right_foot" : "SlimeVR Tracker on Right Foot", diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 381d8ca..30127b8 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -279,21 +279,22 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_component); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system", &this->system_component_chord); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/touch", &this->system_component_chord); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this->trackpad_click_component_); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &this->trackpad_touch_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &this->stick_click_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/thumbstick/touch", &this->stick_click_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->stick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->stick_click_component_touch_); // Scalar components GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/click", &this->trigger_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_component_touch_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/click", &this->trigger_component_click_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_value_component_touch_); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/thumbstick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/thumbstick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &this->haptic_component_); } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 65278fd..4282be4 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -85,7 +85,10 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t system_component = 0; vr::VRInputComponentHandle_t system_component_chord = 0; + + vr::VRInputComponentHandle_t trigger_component_click_ = 0; vr::VRInputComponentHandle_t trigger_component_touch_ = 0; + vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; From 2ff3a222d55127cb53f783d5449c24d45c651596 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 19:59:45 -0600 Subject: [PATCH 065/117] Log literally everything. --- src/TrackerDevice.cpp | 184 +++++++++++++++++++++++++++--------------- src/TrackerDevice.hpp | 5 +- 2 files changed, 123 insertions(+), 66 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 30127b8..d9bf824 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -1,4 +1,6 @@ #include "TrackerDevice.hpp" +#include +#include SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role) : serial_(serial), @@ -116,9 +118,9 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) pose.result = vr::ETrackingResult::TrackingResult_Running_OK; // Set inputs - GetDriver()->GetInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); - GetDriver()->GetInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); - + vr::VRDriverInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); + vr: // Notify SteamVR that pose was updated last_pose_atomic_ = (last_pose_ = pose); @@ -126,16 +128,16 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { // Get inputs from protobuf - GetDriver()->GetInput()->UpdateScalarComponent(trigger_component_, controllerInput.trigger(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); - GetDriver()->GetInput()->UpdateScalarComponent(grip_value_component_, controllerInput.grip(), 0); - GetDriver()->GetInput()->UpdateScalarComponent(stick_x_component_, controllerInput.thumbstick_x(), 0); - GetDriver()->GetInput()->UpdateScalarComponent(stick_y_component_, controllerInput.thumbstick_y(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_a_component_, controllerInput.button_1(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(button_b_component_, controllerInput.button_2(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(stick_click_component_, controllerInput.stick_click(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(system_component, controllerInput.menu_recenter(), 0); - GetDriver()->GetInput()->UpdateBooleanComponent(system_component_chord, controllerInput.menu_recenter(), 0); + vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, controllerInput.trigger(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); + vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, controllerInput.grip(), 0); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, controllerInput.thumbstick_x(), 0); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, controllerInput.thumbstick_y(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, controllerInput.button_1(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, controllerInput.button_2(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, controllerInput.stick_click(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, controllerInput.menu_recenter(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, controllerInput.menu_recenter(), 0); } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) @@ -207,95 +209,137 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { logger_->Log("Activating tracker %s", serial_.c_str()); - // Get the properties handle - auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); + const std::string log_dir = "C:\\SlimeVR\\Logs\\"; + + // Create directory if it doesn't exist + try { + fs::create_directories(log_dir); + } + catch (...) { + // If this fails, we silently continue (driver must not crash) + } + + // One log file per tracker + const std::string log_path = log_dir + "input_" + serial_ + ".log"; + + input_log_.open(log_path, std::ios::out | std::ios::app); + if (input_log_.is_open()) { + input_log_ << "=== Activating tracker " << serial_ << " ===" << std::endl; + } + + // Get the properties handle + containerHandle_ = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); + // Set some universe ID (Must be 2 or higher) - GetDriver()->GetProperties()->SetUint64Property(props, vr::Prop_CurrentUniverseId_Uint64, 4); + GetDriver()->GetProperties()->SetUint64Property(containerHandle_, vr::Prop_CurrentUniverseId_Uint64, 4); // Set up a model "number" (not needed but good to have) if (is_controller_) { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); } else { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); } - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, "SlimeVR"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ManufacturerName_String, "SlimeVR"); //// Hand selection if (is_left_hand_) { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + GetDriver()->GetProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); } else if (is_right_hand_) { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + GetDriver()->GetProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); } else { - GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); + GetDriver()->GetProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); } // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) if (is_controller_) { - vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "slimevr_virtual_controller"); - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. + vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetStringProperty(containerHandle_, vr::Prop_ControllerType_String, "slimevr_virtual_controller"); + vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. } else { - vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); + vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); } // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) std::string model_path; if (is_controller_) { - vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); + vr::VRProperties()->SetStringProperty(containerHandle_, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); } else { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); } // Set the icons - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{slimevr}/icons/tracker_status_ready.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{slimevr}/icons/tracker_status_off.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{slimevr}/icons/tracker_status_ready.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceReady_String, "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceOff_String, "{slimevr}/icons/tracker_status_off.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceSearching_String, "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); // Set inputs if (is_controller_) { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; - vr::VRProperties()->SetUint64Property(props, vr::Prop_SupportedButtons_Uint64, supportedButtons); - - GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/raw", &this->raw_pose_component_handle_); - GetDriver()->GetInput()->CreatePoseComponent(props, "/pose/tip", &this->aim_pose_component_handle_); - - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/double_tap/click", &this->double_tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/triple_tap/click", &this->triple_tap_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/click", &this->button_a_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/touch", &this->button_a_component_touch_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->button_b_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->button_b_component_touch_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_component); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/touch", &this->system_component_chord); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this->trackpad_click_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &this->trackpad_touch_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->stick_click_component_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->stick_click_component_touch_); + vr::VRProperties()->SetUint64Property(containerHandle_, vr::Prop_SupportedButtons_Uint64, supportedButtons); + + vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &this->raw_pose_component_handle_); + LogInputError(input_error, "/pose/raw"); + input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); + LogInputError(input_error, "/pose/tip"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); + LogInputError(input_error, "/input/double_tap/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); + LogInputError(input_error, "/input/triple_tap/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/click", &this->button_a_component_); + LogInputError(input_error, "/input/a/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/touch", &this->button_a_component_touch_); + LogInputError(input_error, "/input/a/touch"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/click", &this->button_b_component_); + LogInputError(input_error, "/input/b/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/touch", &this->button_b_component_touch_); + LogInputError(input_error, "/input/b/touch"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/click", &this->system_component); + LogInputError(input_error, "/input/system/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/touch", &this->system_component_touch); + LogInputError(input_error, "/input/system/touch"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/click", &this->trackpad_click_component_); + LogInputError(input_error, "/input/trackpad/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/touch", &this->trackpad_touch_component_); + LogInputError(input_error, "/input/trackpad/touch"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/click", &this->stick_click_component_); + LogInputError(input_error, "/input/joystick/click"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/touch", &this->stick_click_component_touch_); + LogInputError(input_error, "/input/joystick/touch"); // Scalar components - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_component_touch_); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/click", &this->trigger_component_click_); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_value_component_touch_); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &this->haptic_component_); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + LogInputError(input_error, "/input/trigger/value"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/touch", &this->trigger_component_touch_); + LogInputError(input_error, "/input/trigger/touch"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/click", &this->trigger_component_click_); + LogInputError(input_error, "/input/trigger/click"); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + LogInputError(input_error, "/input/grip/value"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/grip/touch", &this->grip_value_component_touch_); + LogInputError(input_error, "/input/grip/touch"); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/trackpad/x"); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/trackpad/y"); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/joystick/x"); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/joystick/y"); + input_error = vr::VRDriverInput()->CreateHapticComponent(containerHandle_, "/output/haptic", &this->haptic_component_); + LogInputError(input_error, "/output/haptic"); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) @@ -311,6 +355,18 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { } } + void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path) { + if (err != vr::VRInputError_None && input_log_.is_open()) { + input_log_ + << "[InputError] " + << path + << " -> " + << vr::VRDriverInput()->GetInputErrorNameFromEnum(err) + << " (" << err << ")" + << std::endl; + } + } + // Setup skeletal input for fingertracking if (fingertracking_enabled_) { vr::VRDriverInput()->CreateSkeletonComponent( diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 4282be4..b86ed23 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -21,7 +21,7 @@ namespace SlimeVRDriver { public: TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); ~TrackerDevice() = default; - + void LogInputError(vr::EVRInputError err, const char* path); // Inherited via IVRDevice virtual std::string GetSerial() override; virtual void Update() override; @@ -47,6 +47,7 @@ namespace SlimeVRDriver { std::atomic device_index_ = vr::k_unTrackedDeviceIndexInvalid; std::string serial_; + vr::PropertyContainerHandle_t containerHandle_; int device_id_; TrackerRole tracker_role_; @@ -83,7 +84,7 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t stick_click_component_ = 0; vr::VRInputComponentHandle_t system_component = 0; - vr::VRInputComponentHandle_t system_component_chord = 0; + vr::VRInputComponentHandle_t system_component_touch = 0; vr::VRInputComponentHandle_t trigger_component_click_ = 0; From 44dc905d54498e103e1dce2c7e49a86a9f7c1904 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:08:32 -0600 Subject: [PATCH 066/117] Fix syntax issues. --- src/TrackerDevice.cpp | 45 +++++++++++++++++++++---------------------- src/TrackerDevice.hpp | 1 + 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index d9bf824..e069f09 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -120,7 +120,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) // Set inputs vr::VRDriverInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); vr::VRDriverInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); - vr: + // Notify SteamVR that pose was updated last_pose_atomic_ = (last_pose_ = pose); @@ -144,25 +144,25 @@ void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { return; // Get the properties handle - auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); + auto containerHandle_ = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); vr::ETrackedPropertyError err; // Set that the tracker reports battery level in case it has not already been set to true // It's a given that the tracker supports reporting battery life because otherwise a BatteryMessage would not be received - if (vr::VRProperties()->GetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err) != true) { - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); + if (vr::VRProperties()->GetBoolProperty(containerHandle_, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err) != true) { + vr::VRProperties()->SetBoolProperty(containerHandle_, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); } if (battery.is_charging()) { - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, true); + vr::VRProperties()->SetBoolProperty(containerHandle_, vr::Prop_DeviceIsCharging_Bool, true); } else { - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, false); + vr::VRProperties()->SetBoolProperty(containerHandle_, vr::Prop_DeviceIsCharging_Bool, false); } // Set the battery Level; 0 = 0%, 1 = 100% - vr::VRProperties()->SetFloatProperty(props, vr::Prop_DeviceBatteryPercentage_Float, battery.battery_level()); + vr::VRProperties()->SetFloatProperty(containerHandle_, vr::Prop_DeviceBatteryPercentage_Float, battery.battery_level()); } void SlimeVRDriver::TrackerDevice::StatusMessage(messages::TrackerStatus& status) { @@ -223,7 +223,6 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { const std::string log_path = log_dir + "input_" + serial_ + ".log"; input_log_.open(log_path, std::ios::out | std::ios::app); - if (input_log_.is_open()) { input_log_ << "=== Activating tracker " << serial_ << " ===" << std::endl; } @@ -290,7 +289,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; vr::VRProperties()->SetUint64Property(containerHandle_, vr::Prop_SupportedButtons_Uint64, supportedButtons); - vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(props, "/pose/raw", &this->raw_pose_component_handle_); + vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); LogInputError(input_error, "/pose/raw"); input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); LogInputError(input_error, "/pose/tip"); @@ -346,7 +345,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { if (!is_controller_) { auto role_hint = GetViveRoleHint(tracker_role_); if (role_hint != "") { - GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, role_hint.c_str()); + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ControllerType_String, role_hint.c_str()); } auto role = GetViveRole(tracker_role_); @@ -355,22 +354,10 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { } } - void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path) { - if (err != vr::VRInputError_None && input_log_.is_open()) { - input_log_ - << "[InputError] " - << path - << " -> " - << vr::VRDriverInput()->GetInputErrorNameFromEnum(err) - << " (" << err << ")" - << std::endl; - } - } - // Setup skeletal input for fingertracking if (fingertracking_enabled_) { vr::VRDriverInput()->CreateSkeletonComponent( - props, + containerHandle_, is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", is_right_hand_ ? "/skeleton/hand/right" : "/skeleton/hand/left", "/pose/raw", @@ -388,6 +375,18 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { return vr::EVRInitError::VRInitError_None; } +void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path) { + if (err != vr::VRInputError_None && input_log_.is_open()) { + input_log_ + << "[InputError] " + << path + << " -> " + << vr::VRDriverInput()->GetInputErrorNameFromEnum(err) + << " (" << err << ")" + << std::endl; + } +} + void SlimeVRDriver::TrackerDevice::Deactivate() { device_index_ = vr::k_unTrackedDeviceIndexInvalid; } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index b86ed23..0f1e681 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -43,6 +43,7 @@ namespace SlimeVRDriver { virtual vr::DriverPose_t GetPose() override; vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose); private: + std::ofstream input_log_; std::shared_ptr logger_ = std::make_shared(); std::atomic device_index_ = vr::k_unTrackedDeviceIndexInvalid; From 3aaf30c9d20493200115a278fe41be066ba6f8e2 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:17:46 -0600 Subject: [PATCH 067/117] More syntax adjustments --- src/TrackerDevice.cpp | 17 +++++++++++++---- src/TrackerDevice.hpp | 5 ++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index e069f09..156eeab 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -1,6 +1,7 @@ #include "TrackerDevice.hpp" #include #include +namespace fs = std::filesystem; SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role) : serial_(serial), @@ -229,7 +230,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { // Get the properties handle containerHandle_ = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); - + // Set some universe ID (Must be 2 or higher) GetDriver()->GetProperties()->SetUint64Property(containerHandle_, vr::Prop_CurrentUniverseId_Uint64, 4); @@ -289,7 +290,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; vr::VRProperties()->SetUint64Property(containerHandle_, vr::Prop_SupportedButtons_Uint64, supportedButtons); - vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); + vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); LogInputError(input_error, "/pose/raw"); input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); LogInputError(input_error, "/pose/tip"); @@ -381,12 +382,20 @@ void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const ch << "[InputError] " << path << " -> " - << vr::VRDriverInput()->GetInputErrorNameFromEnum(err) + << vr::VRDriverInput()->GetInputErrorName(err) << " (" << err << ")" << std::endl; } } - +const SlimeVRDriver::TrackerDevice::char* GetInputErrorName(vr::EVRInputError err) { + switch (err) { + case vr::VRInputError_None: return "None"; + case vr::VRInputError_NameNotFound: return "NameNotFound"; + case vr::VRInputError_WrongType: return "WrongType"; + // Add others as needed + default: return "Unknown"; + } +} void SlimeVRDriver::TrackerDevice::Deactivate() { device_index_ = vr::k_unTrackedDeviceIndexInvalid; } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 0f1e681..58c5d5a 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -13,6 +13,9 @@ #include #include #include +#include +#include + #include "TrackerRole.hpp" #include "Logger.hpp" @@ -33,6 +36,7 @@ namespace SlimeVRDriver { virtual void ControllerInputMessage(messages::ControllerInput& position) override; virtual void StatusMessage(messages::TrackerStatus &status) override; virtual void BatteryMessage(messages::Battery &battery) override; + const GetInputErrorName(vr::EVRInputError err) override; // Inherited via ITrackedDeviceServerDriver virtual vr::EVRInitError Activate(uint32_t unObjectId) override; @@ -97,7 +101,6 @@ namespace SlimeVRDriver { vr::VRInputComponentHandle_t button_a_component_touch_ = 0; vr::VRInputComponentHandle_t button_b_component_touch_ = 0; vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; - vr::VRInputComponentHandle_t system_component_touch_ = 0; bool is_controller_; bool is_left_hand_; From 5d326b4430b884e21d3cad5daed41187a0cea390 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:23:07 -0600 Subject: [PATCH 068/117] Fix syntax issues. --- src/TrackerDevice.cpp | 2 +- src/TrackerDevice.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 156eeab..15f20ec 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -387,7 +387,7 @@ void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const ch << std::endl; } } -const SlimeVRDriver::TrackerDevice::char* GetInputErrorName(vr::EVRInputError err) { +const char* SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { switch (err) { case vr::VRInputError_None: return "None"; case vr::VRInputError_NameNotFound: return "NameNotFound"; diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 58c5d5a..ea08029 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -36,7 +36,7 @@ namespace SlimeVRDriver { virtual void ControllerInputMessage(messages::ControllerInput& position) override; virtual void StatusMessage(messages::TrackerStatus &status) override; virtual void BatteryMessage(messages::Battery &battery) override; - const GetInputErrorName(vr::EVRInputError err) override; + const char* GetInputErrorName(vr::EVRInputError err) override; // Inherited via ITrackedDeviceServerDriver virtual vr::EVRInitError Activate(uint32_t unObjectId) override; From 89a414397aa02cb49f4ea92139b91022b8cbaa1e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:30:06 -0600 Subject: [PATCH 069/117] Fix syntax issues. --- src/TrackerDevice.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index ea08029..2775a54 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -36,7 +36,7 @@ namespace SlimeVRDriver { virtual void ControllerInputMessage(messages::ControllerInput& position) override; virtual void StatusMessage(messages::TrackerStatus &status) override; virtual void BatteryMessage(messages::Battery &battery) override; - const char* GetInputErrorName(vr::EVRInputError err) override; + const char* GetInputErrorName(vr::EVRInputError err) // Inherited via ITrackedDeviceServerDriver virtual vr::EVRInitError Activate(uint32_t unObjectId) override; From 8730341aaab77330becde5d581051771cbeb7427 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:30:15 -0600 Subject: [PATCH 070/117] Fix syntax issues. --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 15f20ec..5494aac 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -382,7 +382,7 @@ void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const ch << "[InputError] " << path << " -> " - << vr::VRDriverInput()->GetInputErrorName(err) + << GetInputErrorName(err) << " (" << err << ")" << std::endl; } From eb248615f06842e982ce982def9aad44eadf40b2 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:32:18 -0600 Subject: [PATCH 071/117] Fix syntax issues --- src/TrackerDevice.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 2775a54..b5e42ed 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -36,7 +36,7 @@ namespace SlimeVRDriver { virtual void ControllerInputMessage(messages::ControllerInput& position) override; virtual void StatusMessage(messages::TrackerStatus &status) override; virtual void BatteryMessage(messages::Battery &battery) override; - const char* GetInputErrorName(vr::EVRInputError err) + const char* GetInputErrorName(vr::EVRInputError err); // Inherited via ITrackedDeviceServerDriver virtual vr::EVRInitError Activate(uint32_t unObjectId) override; From ef807379001dab63358830f707937a42a4b4af5c Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:48:53 -0600 Subject: [PATCH 072/117] Increase logging. --- src/TrackerDevice.cpp | 75 +++++++++++++++++++++++++++++++++++++------ src/TrackerDevice.hpp | 1 + 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 5494aac..d936736 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -290,54 +290,101 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; vr::VRProperties()->SetUint64Property(containerHandle_, vr::Prop_SupportedButtons_Uint64, supportedButtons); + LogInfo("Creating /pose/raw component"); vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); LogInputError(input_error, "/pose/raw"); + + LogInfo("Creating /pose/tip component"); input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); LogInputError(input_error, "/pose/tip"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); LogInputError(input_error, "/input/double_tap/click"); + + LogInfo("Creating /input/triple_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); LogInputError(input_error, "/input/triple_tap/click"); + + LogInfo("Creating /input/a/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/click", &this->button_a_component_); LogInputError(input_error, "/input/a/click"); + + LogInfo("Creating /input/a/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/touch", &this->button_a_component_touch_); LogInputError(input_error, "/input/a/touch"); + + LogInfo("Creating /input/b/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/click", &this->button_b_component_); LogInputError(input_error, "/input/b/click"); + + LogInfo("Creating /input/b/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/touch", &this->button_b_component_touch_); LogInputError(input_error, "/input/b/touch"); + + LogInfo("Creating /input/system/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/click", &this->system_component); LogInputError(input_error, "/input/system/click"); + + LogInfo("Creating /input/system/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/touch", &this->system_component_touch); LogInputError(input_error, "/input/system/touch"); + + LogInfo("Creating /input/trackpad/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/click", &this->trackpad_click_component_); LogInputError(input_error, "/input/trackpad/click"); + + LogInfo("Creating /input/trackpad/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/touch", &this->trackpad_touch_component_); LogInputError(input_error, "/input/trackpad/touch"); + + LogInfo("Creating /input/joystick/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/click", &this->stick_click_component_); LogInputError(input_error, "/input/joystick/click"); + + LogInfo("Creating /input/joystick/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/touch", &this->stick_click_component_touch_); LogInputError(input_error, "/input/joystick/touch"); // Scalar components + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); LogInputError(input_error, "/input/trigger/value"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/touch", &this->trigger_component_touch_); LogInputError(input_error, "/input/trigger/touch"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/click", &this->trigger_component_click_); LogInputError(input_error, "/input/trigger/click"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); LogInputError(input_error, "/input/grip/value"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/grip/touch", &this->grip_value_component_touch_); LogInputError(input_error, "/input/grip/touch"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); LogInputError(input_error, "/input/trackpad/x"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); LogInputError(input_error, "/input/trackpad/y"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); LogInputError(input_error, "/input/joystick/x"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); LogInputError(input_error, "/input/joystick/y"); + + LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateHapticComponent(containerHandle_, "/output/haptic", &this->haptic_component_); LogInputError(input_error, "/output/haptic"); } @@ -376,17 +423,27 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { return vr::EVRInitError::VRInitError_None; } -void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path) { - if (err != vr::VRInputError_None && input_log_.is_open()) { - input_log_ - << "[InputError] " - << path - << " -> " - << GetInputErrorName(err) - << " (" << err << ")" - << std::endl; +void SlimeVRDriver::TrackerDevice::LogInfo(const char* message) { + if (input_log_.is_open()) { + input_log_ << "[Info] " << message << std::endl; + input_log_.flush(); } } + +void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path) { + if (!input_log_.is_open()) return; + + input_log_ << "[" + << (err == vr::VRInputError_None ? "Info" : "InputError") + << "] " + << path + << " -> " + << GetInputErrorName(err) + << " (" << err << ")" + << std::endl; + input_log_.flush(); // force write immediately +} + const char* SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { switch (err) { case vr::VRInputError_None: return "None"; diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index b5e42ed..c7e5e7f 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -25,6 +25,7 @@ namespace SlimeVRDriver { TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); ~TrackerDevice() = default; void LogInputError(vr::EVRInputError err, const char* path); + void LogInfo(const char* message); // Inherited via IVRDevice virtual std::string GetSerial() override; virtual void Update() override; From 47c136e89a1a7c90edf2bbdafe8cea6ccf6344a4 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 20:58:40 -0600 Subject: [PATCH 073/117] Adjust path for logging. --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index d936736..dfb4e5b 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -210,7 +210,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { logger_->Log("Activating tracker %s", serial_.c_str()); - const std::string log_dir = "C:\\SlimeVR\\Logs\\"; + const std::string log_dir = "C:\\Temp\\SlimeVRLogs\\"; // Create directory if it doesn't exist try { @@ -221,7 +221,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { } // One log file per tracker - const std::string log_path = log_dir + "input_" + serial_ + ".log"; + const std::string log_path = log_dir + "input.log"; input_log_.open(log_path, std::ios::out | std::ios::app); if (input_log_.is_open()) { From ba828cd4e3ab9c6ad57f712ed34f2f1fe732f682 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 21:11:36 -0600 Subject: [PATCH 074/117] Check that inputs have been activated before trying to use them. --- src/TrackerDevice.cpp | 26 ++++++++++++++------------ src/TrackerDevice.hpp | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index dfb4e5b..7b34b28 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -128,17 +128,19 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { - // Get inputs from protobuf - vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, controllerInput.trigger(), 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); - vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, controllerInput.grip(), 0); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, controllerInput.thumbstick_x(), 0); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, controllerInput.thumbstick_y(), 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, controllerInput.button_1(), 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, controllerInput.button_2(), 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, controllerInput.stick_click(), 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, controllerInput.menu_recenter(), 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, controllerInput.menu_recenter(), 0); + if (was_activated_) { + // Get inputs from protobuf + vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, controllerInput.trigger(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); + vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, controllerInput.grip(), 0); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, controllerInput.thumbstick_x(), 0); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, controllerInput.thumbstick_y(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, controllerInput.button_1(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, controllerInput.button_2(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, controllerInput.stick_click(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, controllerInput.menu_recenter(), 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, controllerInput.menu_recenter(), 0); + } } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) @@ -419,7 +421,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); } - + was_activated_ = true; return vr::EVRInitError::VRInitError_None; } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index c7e5e7f..f80f5c8 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -64,7 +64,7 @@ namespace SlimeVRDriver { bool did_vibrate_ = false; float vibrate_anim_state_ = 0.f; - + bool was_activated_ = false; vr::VRInputComponentHandle_t haptic_component_ = 0; vr::VRInputComponentHandle_t double_tap_component_ = 0; vr::VRInputComponentHandle_t triple_tap_component_ = 0; From 217fb020f5a0978c2e770bf8fa5f8e0cd7b2dab5 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 21:33:30 -0600 Subject: [PATCH 075/117] Log handle references --- src/TrackerDevice.cpp | 109 +++++++++++++++++++++--------------------- src/TrackerDevice.hpp | 2 +- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 7b34b28..6d85cf0 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -294,101 +294,101 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { LogInfo("Creating /pose/raw component"); vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); - LogInputError(input_error, "/pose/raw"); + LogInputError(input_error, "/pose/raw", this->raw_pose_component_handle_); LogInfo("Creating /pose/tip component"); input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); - LogInputError(input_error, "/pose/tip"); + LogInputError(input_error, "/pose/tip", this->aim_pose_component_handle_); LogInfo("Creating /input/double_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); - LogInputError(input_error, "/input/double_tap/click"); + LogInputError(input_error, "/input/double_tap/click", this->double_tap_component_); LogInfo("Creating /input/triple_tap/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); - LogInputError(input_error, "/input/triple_tap/click"); + LogInputError(input_error, "/input/triple_tap/click", this->triple_tap_component_); LogInfo("Creating /input/a/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/click", &this->button_a_component_); - LogInputError(input_error, "/input/a/click"); - + LogInputError(input_error, "/input/a/click", this->button_a_component_); + LogInfo("Creating /input/a/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/touch", &this->button_a_component_touch_); - LogInputError(input_error, "/input/a/touch"); - + LogInputError(input_error, "/input/a/touch", this->button_a_component_touch_); + LogInfo("Creating /input/b/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/click", &this->button_b_component_); - LogInputError(input_error, "/input/b/click"); - + LogInputError(input_error, "/input/b/click", this->button_b_component_); + LogInfo("Creating /input/b/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/touch", &this->button_b_component_touch_); - LogInputError(input_error, "/input/b/touch"); - + LogInputError(input_error, "/input/b/touch", this->button_b_component_touch_); + LogInfo("Creating /input/system/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/click", &this->system_component); - LogInputError(input_error, "/input/system/click"); - + LogInputError(input_error, "/input/system/click", this->system_component); + LogInfo("Creating /input/system/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/touch", &this->system_component_touch); - LogInputError(input_error, "/input/system/touch"); - + LogInputError(input_error, "/input/system/touch", this->system_component_touch); + LogInfo("Creating /input/trackpad/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/click", &this->trackpad_click_component_); - LogInputError(input_error, "/input/trackpad/click"); - + LogInputError(input_error, "/input/trackpad/click", this->trackpad_click_component_); + LogInfo("Creating /input/trackpad/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/touch", &this->trackpad_touch_component_); - LogInputError(input_error, "/input/trackpad/touch"); - + LogInputError(input_error, "/input/trackpad/touch", this->trackpad_touch_component_); + LogInfo("Creating /input/joystick/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/click", &this->stick_click_component_); - LogInputError(input_error, "/input/joystick/click"); - + LogInputError(input_error, "/input/joystick/click", this->stick_click_component_); + LogInfo("Creating /input/joystick/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/touch", &this->stick_click_component_touch_); - LogInputError(input_error, "/input/joystick/touch"); + LogInputError(input_error, "/input/joystick/touch", this->stick_click_component_touch_); // Scalar components - - LogInfo("Creating /input/double_tap/click component"); + + LogInfo("Creating /input/trigger/value component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - LogInputError(input_error, "/input/trigger/value"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/trigger/value", this->trigger_component_); + + LogInfo("Creating /input/trigger/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/touch", &this->trigger_component_touch_); - LogInputError(input_error, "/input/trigger/touch"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/trigger/touch", this->trigger_component_touch_); + + LogInfo("Creating /input/trigger/clickcomponent"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/click", &this->trigger_component_click_); - LogInputError(input_error, "/input/trigger/click"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/trigger/click", this->trigger_component_click_); + + LogInfo("Creating /input/grip/value component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - LogInputError(input_error, "/input/grip/value"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/grip/value", this->grip_value_component_); + + LogInfo("Creating /input/grip/touch component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/grip/touch", &this->grip_value_component_touch_); - LogInputError(input_error, "/input/grip/touch"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/grip/touch", this->grip_value_component_touch_); + + LogInfo("Creating /input/trackpad/x component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/trackpad/x"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/trackpad/x", this->trackpad_x_component_); + + LogInfo("Creating /input/trackpad/y component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/trackpad/y"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/trackpad/y", this->trackpad_y_component_); + + LogInfo("Creating /input/joystick/x component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/joystick/x"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/joystick/x", this->stick_x_component); + + LogInfo("Creating /input/joystick/y component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/joystick/y"); - - LogInfo("Creating /input/double_tap/click component"); + LogInputError(input_error, "/input/joystick/y", this->stick_y_component_); + + LogInfo("Creating /output/haptic component"); input_error = vr::VRDriverInput()->CreateHapticComponent(containerHandle_, "/output/haptic", &this->haptic_component_); - LogInputError(input_error, "/output/haptic"); + LogInputError(input_error, "/output/haptic", this->haptic_component_); } // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) @@ -432,13 +432,14 @@ void SlimeVRDriver::TrackerDevice::LogInfo(const char* message) { } } -void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path) { +void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle) { if (!input_log_.is_open()) return; input_log_ << "[" << (err == vr::VRInputError_None ? "Info" : "InputError") << "] " << path + << componentHandle << " -> " << GetInputErrorName(err) << " (" << err << ")" diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index f80f5c8..ecbb413 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -24,7 +24,7 @@ namespace SlimeVRDriver { public: TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); ~TrackerDevice() = default; - void LogInputError(vr::EVRInputError err, const char* path); + void LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle); void LogInfo(const char* message); // Inherited via IVRDevice virtual std::string GetSerial() override; From 6ca5847e14ed969016d0c8533651ca13ce2bf772 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 21:38:07 -0600 Subject: [PATCH 076/117] Fix syntax error. --- src/TrackerDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 6d85cf0..7e7afa2 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -380,7 +380,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { LogInfo("Creating /input/joystick/x component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/joystick/x", this->stick_x_component); + LogInputError(input_error, "/input/joystick/x", this->stick_x_component_); LogInfo("Creating /input/joystick/y component"); input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); From 453773bfc8f79ba86733cd8317372271408212aa Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 22:15:54 -0600 Subject: [PATCH 077/117] Even more logging --- src/TrackerDevice.cpp | 44 +++++++++++++++++++++++++++++++++++++++++-- src/TrackerDevice.hpp | 1 + 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 7e7afa2..bd87d16 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -130,15 +130,34 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { if (was_activated_) { // Get inputs from protobuf + LogInput("Check handle for trigger before update.", this->trigger_component_); vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, controllerInput.trigger(), 0); + + LogInput("Check handle for trigger touch before update.", this->trigger_component_touch_); vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); + + LogInput("Check handle for grip before update.", this->grip_value_component_); vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, controllerInput.grip(), 0); + + LogInput("Check handle for stick x before update.", this->stick_x_component_); vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, controllerInput.thumbstick_x(), 0); + + LogInput("Check handle for stick y before update.", this->stick_y_component_); vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, controllerInput.thumbstick_y(), 0); + + LogInput("Check handle for button a before update.", this->button_a_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, controllerInput.button_1(), 0); + + LogInput("Check handle for button b before update.", this->button_b_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, controllerInput.button_2(), 0); + + LogInput("Check handle for stick click before update.", this->stick_click_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, controllerInput.stick_click(), 0); + + LogInput("Check handle for system before update.", this->system_component); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, controllerInput.menu_recenter(), 0); + + LogInput("Check handle for system touch before update.", this->system_component_touch); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, controllerInput.menu_recenter(), 0); } } @@ -435,17 +454,38 @@ void SlimeVRDriver::TrackerDevice::LogInfo(const char* message) { void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle) { if (!input_log_.is_open()) return; - input_log_ << "[" + bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle + + input_log_ << "[" << (err == vr::VRInputError_None ? "Info" : "InputError") << "] " << path + << "\r\n Handle: " << componentHandle - << " -> " + << "\r\n Handle Is Valid: " + << (validHandle ? "true" : "false") + << "\r\m Failure Result: " << GetInputErrorName(err) << " (" << err << ")" << std::endl; input_log_.flush(); // force write immediately } +void SlimeVRDriver::TrackerDevice::LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle) { + if (!input_log_.is_open()) return; + + bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle + + input_log_ << "[" + << "Info") + << "] " + << path + << "\r\n Handle: " + << componentHandle + << "\r\n Handle Is Valid: " + << (validHandle ? "true" : "false") + << std::endl; + input_log_.flush(); // force write immediately +} const char* SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { switch (err) { diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index ecbb413..c105752 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -24,6 +24,7 @@ namespace SlimeVRDriver { public: TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); ~TrackerDevice() = default; + void LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle); void LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle); void LogInfo(const char* message); // Inherited via IVRDevice From 4b3826b01b1fda69f6040a04b087fa06ea9cf5f6 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 22:24:09 -0600 Subject: [PATCH 078/117] Fix syntax errors --- src/TrackerDevice.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index bd87d16..4d552e2 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -454,9 +454,8 @@ void SlimeVRDriver::TrackerDevice::LogInfo(const char* message) { void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle) { if (!input_log_.is_open()) return; - bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle - - input_log_ << "[" + bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle; + input_log_ << "[" << (err == vr::VRInputError_None ? "Info" : "InputError") << "] " << path @@ -473,10 +472,9 @@ void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const ch void SlimeVRDriver::TrackerDevice::LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle) { if (!input_log_.is_open()) return; - bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle - - input_log_ << "[" - << "Info") + bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle; + input_log_ << "[" + << "Info" << "] " << path << "\r\n Handle: " @@ -484,7 +482,7 @@ void SlimeVRDriver::TrackerDevice::LogInput(const char* path, vr::VRInputCompone << "\r\n Handle Is Valid: " << (validHandle ? "true" : "false") << std::endl; - input_log_.flush(); // force write immediately + input_log_.flush(); // force write immediately } const char* SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { From 26e1b31bb2739ce24d94a3d1754cfe279f8b0b03 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 22:51:00 -0600 Subject: [PATCH 079/117] Additonal logging, move input updates to RunFrame --- src/TrackerDevice.cpp | 80 +++++++++------ src/TrackerDevice.hpp | 226 ++++++++++++++++++++++-------------------- 2 files changed, 165 insertions(+), 141 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 4d552e2..8caace1 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -44,6 +44,39 @@ void SlimeVRDriver::TrackerDevice::Update() { vibrate_anim_state_ = 0.0f; } } + + if (was_activated_) { + // Get inputs from protobuf + LogInput("Check handle for trigger before update.", this->trigger_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, trigger_value_, 0); + + LogInput("Check handle for trigger touch before update.", this->trigger_component_touch_); + vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, trigger_value_click, 0); + + LogInput("Check handle for grip before update.", this->grip_value_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, grip_value, 0); + + LogInput("Check handle for stick x before update.", this->stick_x_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, thumbstick_x_value, 0); + + LogInput("Check handle for stick y before update.", this->stick_y_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, thumbstick_y_value, 0); + + LogInput("Check handle for button a before update.", this->button_a_component_); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, button_1_value, 0); + + LogInput("Check handle for button b before update.", this->button_b_component_); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, button_2_value, 0); + + LogInput("Check handle for stick click before update.", this->stick_click_component_); + vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, stick_click_value, 0); + + LogInput("Check handle for system before update.", this->system_component); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, system_click_value, 0); + + LogInput("Check handle for system touch before update.", this->system_component_touch); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, system_click_value, 0); + } } void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) { @@ -129,36 +162,17 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { if (was_activated_) { - // Get inputs from protobuf - LogInput("Check handle for trigger before update.", this->trigger_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, controllerInput.trigger(), 0); - - LogInput("Check handle for trigger touch before update.", this->trigger_component_touch_); - vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, controllerInput.trigger() > 0.5f, 0); - - LogInput("Check handle for grip before update.", this->grip_value_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, controllerInput.grip(), 0); - - LogInput("Check handle for stick x before update.", this->stick_x_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, controllerInput.thumbstick_x(), 0); - - LogInput("Check handle for stick y before update.", this->stick_y_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, controllerInput.thumbstick_y(), 0); - - LogInput("Check handle for button a before update.", this->button_a_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, controllerInput.button_1(), 0); - - LogInput("Check handle for button b before update.", this->button_b_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, controllerInput.button_2(), 0); - - LogInput("Check handle for stick click before update.", this->stick_click_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, controllerInput.stick_click(), 0); - - LogInput("Check handle for system before update.", this->system_component); - vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, controllerInput.menu_recenter(), 0); - - LogInput("Check handle for system touch before update.", this->system_component_touch); - vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, controllerInput.menu_recenter(), 0); + // Get inputs from protobuf, store them for Update which is called during RunFrame + trigger_value_ = controllerInput.trigger(); + trigger_value_click = controllerInput.trigger() > 0.5f; + grip_value = controllerInput.grip(); + thumbstick_x_value = controllerInput.thumbstick_x(); + thumbstick_y_value = controllerInput.thumbstick_y(); + button_1_value = controllerInput.button_1(); + button_2_value = controllerInput.button_2(); + stick_click_value = controllerInput.stick_click(); + system_value = controllerInput.menu_recenter(); + system_click_value = controllerInput.menu_recenter(); } } void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { @@ -454,7 +468,7 @@ void SlimeVRDriver::TrackerDevice::LogInfo(const char* message) { void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle) { if (!input_log_.is_open()) return; - bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle; + bool validHandle = componentHandle != vr::k_ulInvalidInputComponentHandle; input_log_ << "[" << (err == vr::VRInputError_None ? "Info" : "InputError") << "] " @@ -472,7 +486,7 @@ void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const ch void SlimeVRDriver::TrackerDevice::LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle) { if (!input_log_.is_open()) return; - bool validHandle = componentHandle == vr::k_ulInvalidInputComponentHandle; + bool validHandle = componentHandle != vr::k_ulInvalidInputComponentHandle; input_log_ << "[" << "Info" << "] " @@ -482,7 +496,7 @@ void SlimeVRDriver::TrackerDevice::LogInput(const char* path, vr::VRInputCompone << "\r\n Handle Is Valid: " << (validHandle ? "true" : "false") << std::endl; - input_log_.flush(); // force write immediately + input_log_.flush(); // force write immediately } const char* SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index c105752..5b3ce4e 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -20,112 +20,122 @@ #include "Logger.hpp" namespace SlimeVRDriver { - class TrackerDevice : public IVRDevice { - public: - TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); - ~TrackerDevice() = default; - void LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle); - void LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle); - void LogInfo(const char* message); - // Inherited via IVRDevice - virtual std::string GetSerial() override; - virtual void Update() override; - virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; - virtual DeviceType GetDeviceType() override; - virtual int GetDeviceId() override; - virtual void SetDeviceId(int device_id) override; - virtual void PositionMessage(messages::Position &position) override; - virtual void ControllerInputMessage(messages::ControllerInput& position) override; - virtual void StatusMessage(messages::TrackerStatus &status) override; - virtual void BatteryMessage(messages::Battery &battery) override; - const char* GetInputErrorName(vr::EVRInputError err); - - // Inherited via ITrackedDeviceServerDriver - virtual vr::EVRInitError Activate(uint32_t unObjectId) override; - virtual void Deactivate() override; - virtual void EnterStandby() override; - virtual void* GetComponent(const char* pchComponentNameAndVersion) override; - virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; - virtual vr::DriverPose_t GetPose() override; - vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose); - private: - std::ofstream input_log_; - std::shared_ptr logger_ = std::make_shared(); - - std::atomic device_index_ = vr::k_unTrackedDeviceIndexInvalid; - std::string serial_; - vr::PropertyContainerHandle_t containerHandle_; - - int device_id_; - TrackerRole tracker_role_; - bool fingertracking_enabled_; - - vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); - std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); - - bool did_vibrate_ = false; - float vibrate_anim_state_ = 0.f; - bool was_activated_ = false; - vr::VRInputComponentHandle_t haptic_component_ = 0; - vr::VRInputComponentHandle_t double_tap_component_ = 0; - vr::VRInputComponentHandle_t triple_tap_component_ = 0; - - vr::VRInputComponentHandle_t ignored = 0; - vr::VRInputComponentHandle_t pose_component_handle_ = 0; - - vr::VRInputComponentHandle_t raw_pose_component_handle_ = 0; - vr::VRInputComponentHandle_t aim_pose_component_handle_ = 0; - - vr::VRInputComponentHandle_t trigger_component_ = 0; - vr::VRInputComponentHandle_t grip_value_component_ = 0; - vr::VRInputComponentHandle_t stick_x_component_ = 0; - vr::VRInputComponentHandle_t stick_y_component_ = 0; - vr::VRInputComponentHandle_t button_a_component_ = 0; - vr::VRInputComponentHandle_t button_b_component_ = 0; - - vr::VRInputComponentHandle_t trackpad_x_component_ = 0; - vr::VRInputComponentHandle_t trackpad_y_component_ = 0; - vr::VRInputComponentHandle_t trackpad_click_component_ = 0; - vr::VRInputComponentHandle_t trackpad_touch_component_ = 0; - - - vr::VRInputComponentHandle_t stick_click_component_ = 0; - vr::VRInputComponentHandle_t system_component = 0; - vr::VRInputComponentHandle_t system_component_touch = 0; - - - vr::VRInputComponentHandle_t trigger_component_click_ = 0; - vr::VRInputComponentHandle_t trigger_component_touch_ = 0; - - vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; - vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; - vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; - vr::VRInputComponentHandle_t button_a_component_touch_ = 0; - vr::VRInputComponentHandle_t button_b_component_touch_ = 0; - vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; - - bool is_controller_; - bool is_left_hand_; - bool is_right_hand_; - - vr::VRInputComponentHandle_t skeletal_component_handle_; - - const int protobuf_fingers_to_openvr[15] = { - 2, // THUMB_METACARPAL → eBone_Thumb1 - 3, // THUMB_PROXIMAL → eBone_Thumb2 - 4, // THUMB_DISTAL → eBone_Thumb3 - 6, // INDEX_PROXIMAL → eBone_IndexFinger1 - 7, // INDEX_INTERMEDIATE → eBone_IndexFinger2 - 8, // INDEX_DISTAL → eBone_IndexFinger3 - 11, // MIDDLE_PROXIMAL → eBone_MiddleFinger1 - 12, // MIDDLE_INTERMEDIATE → eBone_MiddleFinger2 - 13, // MIDDLE_DISTAL → eBone_MiddleFinger3 - 16, // RING_PROXIMAL → eBone_RingFinger1 - 17, // RING_INTERMEDIATE → eBone_RingFinger2 - 18, // RING_DISTAL → eBone_RingFinger3 - 21, // LITTLE_PROXIMAL → eBone_PinkyFinger1 - 22, // LITTLE_INTERMEDIATE → eBone_PinkyFinger2 - 23 // LITTLE_DISTAL → eBone_PinkyFinger3 - }; - }; + class TrackerDevice : public IVRDevice { + public: + TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); + ~TrackerDevice() = default; + void LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle); + void LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle); + void LogInfo(const char* message); + // Inherited via IVRDevice + virtual std::string GetSerial() override; + virtual void Update() override; + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; + virtual DeviceType GetDeviceType() override; + virtual int GetDeviceId() override; + virtual void SetDeviceId(int device_id) override; + virtual void PositionMessage(messages::Position& position) override; + virtual void ControllerInputMessage(messages::ControllerInput& position) override; + virtual void StatusMessage(messages::TrackerStatus& status) override; + virtual void BatteryMessage(messages::Battery& battery) override; + const char* GetInputErrorName(vr::EVRInputError err); + + // Inherited via ITrackedDeviceServerDriver + virtual vr::EVRInitError Activate(uint32_t unObjectId) override; + virtual void Deactivate() override; + virtual void EnterStandby() override; + virtual void* GetComponent(const char* pchComponentNameAndVersion) override; + virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; + virtual vr::DriverPose_t GetPose() override; + vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose); + private: + std::ofstream input_log_; + std::shared_ptr logger_ = std::make_shared(); + + std::atomic device_index_ = vr::k_unTrackedDeviceIndexInvalid; + std::string serial_; + vr::PropertyContainerHandle_t containerHandle_; + + int device_id_; + TrackerRole tracker_role_; + bool fingertracking_enabled_; + + vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); + std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); + + bool did_vibrate_ = false; + float vibrate_anim_state_ = 0.f; + bool was_activated_ = false; + vr::VRInputComponentHandle_t haptic_component_ = 0; + vr::VRInputComponentHandle_t double_tap_component_ = 0; + vr::VRInputComponentHandle_t triple_tap_component_ = 0; + + vr::VRInputComponentHandle_t ignored = 0; + vr::VRInputComponentHandle_t pose_component_handle_ = 0; + + vr::VRInputComponentHandle_t raw_pose_component_handle_ = 0; + vr::VRInputComponentHandle_t aim_pose_component_handle_ = 0; + + vr::VRInputComponentHandle_t trigger_component_ = 0; + vr::VRInputComponentHandle_t grip_value_component_ = 0; + vr::VRInputComponentHandle_t stick_x_component_ = 0; + vr::VRInputComponentHandle_t stick_y_component_ = 0; + vr::VRInputComponentHandle_t button_a_component_ = 0; + vr::VRInputComponentHandle_t button_b_component_ = 0; + + vr::VRInputComponentHandle_t trackpad_x_component_ = 0; + vr::VRInputComponentHandle_t trackpad_y_component_ = 0; + vr::VRInputComponentHandle_t trackpad_click_component_ = 0; + vr::VRInputComponentHandle_t trackpad_touch_component_ = 0; + + + vr::VRInputComponentHandle_t stick_click_component_ = 0; + vr::VRInputComponentHandle_t system_component = 0; + vr::VRInputComponentHandle_t system_component_touch = 0; + + + vr::VRInputComponentHandle_t trigger_component_click_ = 0; + vr::VRInputComponentHandle_t trigger_component_touch_ = 0; + + vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; + vr::VRInputComponentHandle_t button_a_component_touch_ = 0; + vr::VRInputComponentHandle_t button_b_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; + + bool is_controller_; + bool is_left_hand_; + bool is_right_hand_; + + vr::VRInputComponentHandle_t skeletal_component_handle_; + + float trigger_value_ = 0; + bool trigger_value_click = false; + float grip_value = 0; + float thumbstick_x_value = 0; + float thumbstick_y_value = 0; + bool button_1_value = false; + bool button_2_value = false; + bool stick_click_value = false; + bool system_value = false; + bool system_click_value = false; + const int protobuf_fingers_to_openvr[15] = { + 2, // THUMB_METACARPAL → eBone_Thumb1 + 3, // THUMB_PROXIMAL → eBone_Thumb2 + 4, // THUMB_DISTAL → eBone_Thumb3 + 6, // INDEX_PROXIMAL → eBone_IndexFinger1 + 7, // INDEX_INTERMEDIATE → eBone_IndexFinger2 + 8, // INDEX_DISTAL → eBone_IndexFinger3 + 11, // MIDDLE_PROXIMAL → eBone_MiddleFinger1 + 12, // MIDDLE_INTERMEDIATE → eBone_MiddleFinger2 + 13, // MIDDLE_DISTAL → eBone_MiddleFinger3 + 16, // RING_PROXIMAL → eBone_RingFinger1 + 17, // RING_INTERMEDIATE → eBone_RingFinger2 + 18, // RING_DISTAL → eBone_RingFinger3 + 21, // LITTLE_PROXIMAL → eBone_PinkyFinger1 + 22, // LITTLE_INTERMEDIATE → eBone_PinkyFinger2 + 23 // LITTLE_DISTAL → eBone_PinkyFinger3 + }; + }; }; \ No newline at end of file From 9f280f00065749e256857466350195fd34320af6 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 23:22:07 -0600 Subject: [PATCH 080/117] Only do input updates if we're a controller. --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 8caace1..b0b6d22 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -45,7 +45,7 @@ void SlimeVRDriver::TrackerDevice::Update() { } } - if (was_activated_) { + if (was_activated_ && is_controller_) { // Get inputs from protobuf LogInput("Check handle for trigger before update.", this->trigger_component_); vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, trigger_value_, 0); @@ -161,7 +161,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { - if (was_activated_) { + if (was_activated_ && is_controller_) { // Get inputs from protobuf, store them for Update which is called during RunFrame trigger_value_ = controllerInput.trigger(); trigger_value_click = controllerInput.trigger() > 0.5f; From c38dd45c98ff2971a90c7a300d8de81ee2a915a2 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 23:38:23 -0600 Subject: [PATCH 081/117] Debug input values. --- src/TrackerDevice.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index b0b6d22..a8d62e0 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -47,28 +47,28 @@ void SlimeVRDriver::TrackerDevice::Update() { if (was_activated_ && is_controller_) { // Get inputs from protobuf - LogInput("Check handle for trigger before update.", this->trigger_component_); + LogInput("Check handle for trigger before update. Value is " + trigger_value_, this->trigger_component_); vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, trigger_value_, 0); - LogInput("Check handle for trigger touch before update.", this->trigger_component_touch_); + LogInput("Check handle for trigger touch before update. Value is " + trigger_value_click, this->trigger_component_touch_); vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, trigger_value_click, 0); - LogInput("Check handle for grip before update.", this->grip_value_component_); + LogInput("Check handle for grip before update. Value is " + grip_value, this->grip_value_component_); vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, grip_value, 0); - LogInput("Check handle for stick x before update.", this->stick_x_component_); + LogInput("Check handle for stick x before update. Value is " + thumbstick_x_value, this->stick_x_component_); vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, thumbstick_x_value, 0); - LogInput("Check handle for stick y before update.", this->stick_y_component_); + LogInput("Check handle for stick y before update. Value is " + thumbstick_y_value, this->stick_y_component_); vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, thumbstick_y_value, 0); - LogInput("Check handle for button a before update.", this->button_a_component_); + LogInput("Check handle for button a before update. Value is " + button_1_value, this->button_a_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, button_1_value, 0); - LogInput("Check handle for button b before update.", this->button_b_component_); + LogInput("Check handle for button b before update. Value is " + button_2_value, this->button_b_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, button_2_value, 0); - LogInput("Check handle for stick click before update.", this->stick_click_component_); + LogInput("Check handle for stick click before update. Value is " + stick_click_value this->stick_click_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, stick_click_value, 0); LogInput("Check handle for system before update.", this->system_component); From 741e7f84ddfef5fe1da45bc205cd2f37b91b2083 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 20 Jan 2026 23:56:38 -0600 Subject: [PATCH 082/117] Adjust log messages. --- src/TrackerDevice.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index a8d62e0..45fbf96 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -47,34 +47,34 @@ void SlimeVRDriver::TrackerDevice::Update() { if (was_activated_ && is_controller_) { // Get inputs from protobuf - LogInput("Check handle for trigger before update. Value is " + trigger_value_, this->trigger_component_); + LogInput(("Check handle for trigger before update. Value is " + std::to_string(trigger_value_)).c_str(), this->trigger_component_); vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, trigger_value_, 0); - LogInput("Check handle for trigger touch before update. Value is " + trigger_value_click, this->trigger_component_touch_); + LogInput(("Check handle for trigger touch before update. Value is " + std::to_string(trigger_value_click)).c_str(), this->trigger_component_touch_); vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, trigger_value_click, 0); - LogInput("Check handle for grip before update. Value is " + grip_value, this->grip_value_component_); + LogInput(("Check handle for grip before update. Value is " + std::to_string(grip_value)).c_str(), this->grip_value_component_); vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, grip_value, 0); - LogInput("Check handle for stick x before update. Value is " + thumbstick_x_value, this->stick_x_component_); + LogInput(("Check handle for stick x before update. Value is " + std::to_string(thumbstick_x_value)).c_str(), this->stick_x_component_); vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, thumbstick_x_value, 0); - LogInput("Check handle for stick y before update. Value is " + thumbstick_y_value, this->stick_y_component_); + LogInput(("Check handle for stick y before update. Value is " + std::to_string(thumbstick_y_value)).c_str(), this->stick_y_component_); vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, thumbstick_y_value, 0); - LogInput("Check handle for button a before update. Value is " + button_1_value, this->button_a_component_); + LogInput(("Check handle for button a before update. Value is " + std::to_string(button_1_value)).c_str(), this->button_a_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, button_1_value, 0); - LogInput("Check handle for button b before update. Value is " + button_2_value, this->button_b_component_); + LogInput(("Check handle for button b before update. Value is " + std::to_string(button_2_value)).c_str(), this->button_b_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, button_2_value, 0); - LogInput("Check handle for stick click before update. Value is " + stick_click_value this->stick_click_component_); + LogInput(("Check handle for stick click before update. Value is " + std::to_string(stick_click_value)).c_str(), this->stick_click_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, stick_click_value, 0); - LogInput("Check handle for system before update.", this->system_component); + LogInput(("Check handle for system before update. Value is " + std::to_string(system_component)).c_str(), this->system_component); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, system_click_value, 0); - LogInput("Check handle for system touch before update.", this->system_component_touch); + LogInput(("Check handle for system touch before update. Value is " + std::to_string(system_component_touch)).c_str(), this->system_component_touch); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, system_click_value, 0); } } @@ -477,7 +477,7 @@ void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const ch << componentHandle << "\r\n Handle Is Valid: " << (validHandle ? "true" : "false") - << "\r\m Failure Result: " + << "\r\n Failure Result: " << GetInputErrorName(err) << " (" << err << ")" << std::endl; From ae6e7f80526962309b4b40e3a27fdd36421d89d1 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Wed, 21 Jan 2026 00:50:22 -0600 Subject: [PATCH 083/117] Let tap inputs be available regardless of being a controller. --- src/TrackerDevice.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 45fbf96..83d1f02 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -319,6 +319,14 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); + LogInfo("Creating /input/double_tap/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); + LogInputError(input_error, "/input/double_tap/click", this->double_tap_component_); + + LogInfo("Creating /input/triple_tap/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); + LogInputError(input_error, "/input/triple_tap/click", this->triple_tap_component_); + // Set inputs if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); @@ -333,14 +341,6 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); LogInputError(input_error, "/pose/tip", this->aim_pose_component_handle_); - LogInfo("Creating /input/double_tap/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); - LogInputError(input_error, "/input/double_tap/click", this->double_tap_component_); - - LogInfo("Creating /input/triple_tap/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); - LogInputError(input_error, "/input/triple_tap/click", this->triple_tap_component_); - LogInfo("Creating /input/a/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/click", &this->button_a_component_); LogInputError(input_error, "/input/a/click", this->button_a_component_); From 185706813e5ee6d2f4fd37ae46243ffc9817f103 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Wed, 21 Jan 2026 01:27:43 -0600 Subject: [PATCH 084/117] Only controllers should do steam input. --- src/TrackerDevice.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 83d1f02..0811186 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -151,9 +151,11 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) pose.poseIsValid = true; pose.result = vr::ETrackingResult::TrackingResult_Running_OK; - // Set inputs - vr::VRDriverInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); + if (is_controller_) { + // Set inputs + vr::VRDriverInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); + } // Notify SteamVR that pose was updated last_pose_atomic_ = (last_pose_ = pose); @@ -319,14 +321,6 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); - LogInfo("Creating /input/double_tap/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); - LogInputError(input_error, "/input/double_tap/click", this->double_tap_component_); - - LogInfo("Creating /input/triple_tap/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); - LogInputError(input_error, "/input/triple_tap/click", this->triple_tap_component_); - // Set inputs if (is_controller_) { GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); @@ -341,6 +335,14 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); LogInputError(input_error, "/pose/tip", this->aim_pose_component_handle_); + LogInfo("Creating /input/double_tap/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); + LogInputError(input_error, "/input/double_tap/click", this->double_tap_component_); + + LogInfo("Creating /input/triple_tap/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); + LogInputError(input_error, "/input/triple_tap/click", this->triple_tap_component_); + LogInfo("Creating /input/a/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/click", &this->button_a_component_); LogInputError(input_error, "/input/a/click", this->button_a_component_); From 7169931f15a3eee81ff4cf59a4a9cc2c998481b0 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Wed, 21 Jan 2026 03:19:26 -0600 Subject: [PATCH 085/117] Fix system click debug --- src/TrackerDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 0811186..a2133f8 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -71,10 +71,10 @@ void SlimeVRDriver::TrackerDevice::Update() { LogInput(("Check handle for stick click before update. Value is " + std::to_string(stick_click_value)).c_str(), this->stick_click_component_); vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, stick_click_value, 0); - LogInput(("Check handle for system before update. Value is " + std::to_string(system_component)).c_str(), this->system_component); + LogInput(("Check handle for system before update. Value is " + std::to_string(system_click_value)).c_str(), this->system_component); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, system_click_value, 0); - LogInput(("Check handle for system touch before update. Value is " + std::to_string(system_component_touch)).c_str(), this->system_component_touch); + LogInput(("Check handle for system touch before update. Value is " + std::to_string(system_click_value)).c_str(), this->system_component_touch); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, system_click_value, 0); } } From 400e33df91e34ee94ea476e742d28e7ad903e9e8 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Wed, 21 Jan 2026 04:38:11 -0600 Subject: [PATCH 086/117] Change stick mapping to thumbstick --- src/TrackerDevice.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index a2133f8..4218214 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -379,9 +379,9 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/click", &this->stick_click_component_); LogInputError(input_error, "/input/joystick/click", this->stick_click_component_); - LogInfo("Creating /input/joystick/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/touch", &this->stick_click_component_touch_); - LogInputError(input_error, "/input/joystick/touch", this->stick_click_component_touch_); + LogInfo("Creating /input/thumbstick/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/thumbstick/touch", &this->stick_click_component_touch_); + LogInputError(input_error, "/input/thumbstick/touch", this->stick_click_component_touch_); // Scalar components @@ -393,7 +393,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/touch", &this->trigger_component_touch_); LogInputError(input_error, "/input/trigger/touch", this->trigger_component_touch_); - LogInfo("Creating /input/trigger/clickcomponent"); + LogInfo("Creating /input/trigger/click component"); input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/click", &this->trigger_component_click_); LogInputError(input_error, "/input/trigger/click", this->trigger_component_click_); @@ -413,13 +413,13 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); LogInputError(input_error, "/input/trackpad/y", this->trackpad_y_component_); - LogInfo("Creating /input/joystick/x component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/joystick/x", this->stick_x_component_); + LogInfo("Creating /input/thumbstick/x component"); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/thumbstick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/thumbstick/x", this->stick_x_component_); LogInfo("Creating /input/joystick/y component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/joystick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/joystick/y", this->stick_y_component_); + input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/thumbstick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/thumbstick/y", this->stick_y_component_); LogInfo("Creating /output/haptic component"); input_error = vr::VRDriverInput()->CreateHapticComponent(containerHandle_, "/output/haptic", &this->haptic_component_); From c4575c9e8283265f585de11c8c710a97175c1653 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 6 Feb 2026 17:53:22 -0600 Subject: [PATCH 087/117] Re-introduce finger mappings. --- src/TrackerDevice.cpp | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 4218214..6636e58 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -123,29 +123,29 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) bool double_tap = false; bool triple_tap = false; - //if (fingertracking_enabled_) { - // // Set finger rotations - // vr::VRBoneTransform_t finger_skeleton_[31]{}; - // for (int i = 0; i < position.finger_bone_rotations_size(); i++) - // { - // // Get data from protobuf - // auto fingerData = position.finger_bone_rotations(i); - // int fingerBoneName = static_cast(fingerData.name()); - - // // Map from our 15 bones to OpenVR's 31 bones - // int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; - // finger_skeleton_[boneIndex].orientation = { - // fingerData.w(), - // fingerData.x(), - // fingerData.y(), - // fingerData.z() - // }; - // } - - // // Update the finger skeleton for this hand. With and without controller have the same pose. - // vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); - // vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); - //} + if (fingertracking_enabled_) { + // Set finger rotations + vr::VRBoneTransform_t finger_skeleton_[31]{}; + for (int i = 0; i < position.finger_bone_rotations_size(); i++) + { + // Get data from protobuf + auto fingerData = position.finger_bone_rotations(i); + int fingerBoneName = static_cast(fingerData.name()); + + // Map from our 15 bones to OpenVR's 31 bones + int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; + finger_skeleton_[boneIndex].orientation = { + fingerData.w(), + fingerData.x(), + fingerData.y(), + fingerData.z() + }; + } + + // Update the finger skeleton for this hand. With and without controller have the same pose. + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); + } pose.deviceIsConnected = true; pose.poseIsValid = true; From 511295926cfeeff12c2b1b1fb07320a2a6da558e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 6 Feb 2026 18:15:19 -0600 Subject: [PATCH 088/117] Add missing finger bone rotations to protobuff. --- src/bridge/ProtobufMessages.proto | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bridge/ProtobufMessages.proto b/src/bridge/ProtobufMessages.proto index a71171a..5e13851 100644 --- a/src/bridge/ProtobufMessages.proto +++ b/src/bridge/ProtobufMessages.proto @@ -115,6 +115,9 @@ message Position { FULL = 3; } optional DataSource data_source = 9; + + repeated FingerBoneRotation finger_bone_rotations = 10; + optional float vx = 10; optional float vy = 11; optional float vz = 12; From a8d8c44fb316715d26213e9c311f5369f63a33a8 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 6 Feb 2026 18:21:31 -0600 Subject: [PATCH 089/117] Add missing input message type --- src/bridge/ProtobufMessages.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bridge/ProtobufMessages.proto b/src/bridge/ProtobufMessages.proto index 5e13851..1ab2802 100644 --- a/src/bridge/ProtobufMessages.proto +++ b/src/bridge/ProtobufMessages.proto @@ -118,6 +118,7 @@ message Position { repeated FingerBoneRotation finger_bone_rotations = 10; + repeated Input input = 11; optional float vx = 10; optional float vy = 11; optional float vz = 12; From af999064e99fd9e6795248b3317073a9bf8fdc64 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Thu, 19 Feb 2026 20:15:27 -0600 Subject: [PATCH 090/117] Experiment with swapping pose input to Quest hand tracking when hands are in view of an HMD. --- src/IVRDriver.hpp | 150 ++--- src/TrackerDevice.cpp | 1232 +++++++++++++++++++++++++---------------- src/TrackerDevice.hpp | 272 ++++----- src/VRDriver.cpp | 694 ++++++++++++++++------- src/VRDriver.hpp | 122 ++-- 5 files changed, 1533 insertions(+), 937 deletions(-) diff --git a/src/IVRDriver.hpp b/src/IVRDriver.hpp index 240cc54..31c396f 100644 --- a/src/IVRDriver.hpp +++ b/src/IVRDriver.hpp @@ -1,33 +1,35 @@ #pragma once -#include -#include +#include "IVRDevice.hpp" #include -#include -#include +#include #include -#include "IVRDevice.hpp" +#include #include +#include +#include + namespace SlimeVRDriver { - class UniverseTranslation { - public: - // TODO: do we want to store this differently? - vr::HmdVector3_t translation; - float yaw; +class UniverseTranslation { +public: + // TODO: do we want to store this differently? + vr::HmdVector3_t translation; + float yaw; - static UniverseTranslation parse(simdjson::ondemand::object &obj); - }; + static UniverseTranslation parse(simdjson::ondemand::object &obj); +}; - typedef std::variant SettingsValue; +typedef std::variant + SettingsValue; - class IVRDriver : protected vr::IServerTrackedDeviceProvider { - public: - /** - * Returns all devices being managed by this driver. - * - * @return A vector of shared pointers to all managed devices. - */ - virtual std::vector> GetDevices() = 0; +class IVRDriver : protected vr::IServerTrackedDeviceProvider { +public: + /** + * Returns all devices being managed by this driver. + * + * @return A vector of shared pointers to all managed devices. + */ + virtual std::vector> GetDevices() = 0; /** * Returns all OpenVR events that happened on the current frame. @@ -36,59 +38,69 @@ namespace SlimeVRDriver { */ virtual const std::vector& GetOpenVREvents() = 0; - /** - * Returns the milliseconds between last frame and this frame. - * - * @return Milliseconds between last frame and this frame. - */ - virtual std::chrono::milliseconds GetLastFrameTime() = 0; + /** + * Returns the milliseconds between last frame and this frame. + * + * @return Milliseconds between last frame and this frame. + */ + virtual std::chrono::milliseconds GetLastFrameTime() = 0; - /** - * Adds a device to the driver. - * - * @param device A shared pointer to the device to be added. - * @return True on success, false on failure. - */ - virtual bool AddDevice(std::shared_ptr device) = 0; + /** + * Adds a device to the driver. + * + * @param device A shared pointer to the device to be added. + * @return True on success, false on failure. + */ + virtual bool AddDevice(std::shared_ptr device) = 0; - /** - * Returns the value of a settings key. - * - * @param key The settings key - * @return Value of the key, std::monostate if the value is malformed or missing. - */ - virtual SettingsValue GetSettingsValue(std::string key) = 0; + /** + * Returns the value of a settings key. + * + * @param key The settings key + * @return Value of the key, std::monostate if the value is malformed or + * missing. + */ + virtual SettingsValue GetSettingsValue(std::string key) = 0; - /** - * Gets the OpenVR VRDriverInput pointer. - * - * @return OpenVR VRDriverInput pointer. - */ - virtual vr::IVRDriverInput* GetInput() = 0; + /** + * Gets the OpenVR VRDriverInput pointer. + * + * @return OpenVR VRDriverInput pointer. + */ + virtual vr::IVRDriverInput *GetInput() = 0; - /** - * Gets the OpenVR VRDriverProperties pointer. - * - * @return OpenVR VRDriverProperties pointer. - */ - virtual vr::CVRPropertyHelpers* GetProperties() = 0; + /** + * Gets the OpenVR VRDriverProperties pointer. + * + * @return OpenVR VRDriverProperties pointer. + */ + virtual vr::CVRPropertyHelpers *GetProperties() = 0; - /** - * Gets the OpenVR VRServerDriverHost pointer. - * - * @return OpenVR VRServerDriverHost pointer. - */ - virtual vr::IVRServerDriverHost* GetDriverHost() = 0; + /** + * Gets the OpenVR VRServerDriverHost pointer. + * + * @return OpenVR VRServerDriverHost pointer. + */ + virtual vr::IVRServerDriverHost *GetDriverHost() = 0; - /** - * Gets the current UniverseTranslation. - */ - virtual std::optional GetCurrentUniverse() = 0; + /** + * Gets the current UniverseTranslation. + */ + virtual std::optional GetCurrentUniverse() = 0; + + /** + * Gets the current pose from an external controller (e.g. Virtual Desktop / + * Steam Link on Quest) for the given hand, if one is connected and tracked. + * Used to prefer external hand position when in view, falling back to SlimeVR + * when not. + */ + virtual std::optional + GetExternalPoseForHand(bool left_hand) = 0; - virtual inline const char* const* GetInterfaceVersions() override { - return vr::k_InterfaceVersions; - }; + virtual inline const char *const *GetInterfaceVersions() override { + return vr::k_InterfaceVersions; + }; - virtual ~IVRDriver() {} - }; -} \ No newline at end of file + virtual ~IVRDriver() {} +}; +} // namespace SlimeVRDriver \ No newline at end of file diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 6636e58..2592a96 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -1,100 +1,193 @@ #include "TrackerDevice.hpp" -#include #include -namespace fs = std::filesystem; +#include -SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role) : - serial_(serial), - tracker_role_(tracker_role), - device_id_(device_id), - is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND), - is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_HAND), - fingertracking_enabled_(is_left_hand_ || is_right_hand_), - is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || tracker_role_ == TrackerRole::RIGHT_CONTROLLER || tracker_role_ == TrackerRole::LEFT_HAND || tracker_role_ == TrackerRole::RIGHT_HAND), - last_pose_(MakeDefaultPose()), - last_pose_atomic_(MakeDefaultPose()) -{ -} +namespace fs = std::filesystem; -std::string SlimeVRDriver::TrackerDevice::GetSerial() { - return serial_; -} +SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, + TrackerRole tracker_role) + : serial_(serial), tracker_role_(tracker_role), device_id_(device_id), + is_left_hand_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || + tracker_role_ == TrackerRole::LEFT_HAND), + is_right_hand_(tracker_role_ == TrackerRole::RIGHT_CONTROLLER || + tracker_role_ == TrackerRole::RIGHT_HAND), + fingertracking_enabled_(is_left_hand_ || is_right_hand_), + is_controller_(tracker_role_ == TrackerRole::LEFT_CONTROLLER || + tracker_role_ == TrackerRole::RIGHT_CONTROLLER || + tracker_role_ == TrackerRole::LEFT_HAND || + tracker_role_ == TrackerRole::RIGHT_HAND), + last_pose_(MakeDefaultPose()), last_pose_atomic_(MakeDefaultPose()) {} + +std::string SlimeVRDriver::TrackerDevice::GetSerial() { return serial_; } void SlimeVRDriver::TrackerDevice::Update() { - if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; - - // Check if this device was asked to be identified - auto events = GetDriver()->GetOpenVREvents(); - for (auto event : events) { - // Note here, event.trackedDeviceIndex does not necessarily equal device_index_, not sure why, but the component handle will match so we can just use that instead - //if (event.trackedDeviceIndex == device_index_) { - if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { - if (event.data.hapticVibration.componentHandle == haptic_component_) { - did_vibrate_ = true; - } - } - //} - } - - // Check if we need to keep vibrating - if (did_vibrate_) { - vibrate_anim_state_ += GetDriver()->GetLastFrameTime().count() / 1000.f; - if (vibrate_anim_state_ > 1.0f) { - did_vibrate_ = false; - vibrate_anim_state_ = 0.0f; - } - } - - if (was_activated_ && is_controller_) { - // Get inputs from protobuf - LogInput(("Check handle for trigger before update. Value is " + std::to_string(trigger_value_)).c_str(), this->trigger_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, trigger_value_, 0); - - LogInput(("Check handle for trigger touch before update. Value is " + std::to_string(trigger_value_click)).c_str(), this->trigger_component_touch_); - vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, trigger_value_click, 0); - - LogInput(("Check handle for grip before update. Value is " + std::to_string(grip_value)).c_str(), this->grip_value_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, grip_value, 0); - - LogInput(("Check handle for stick x before update. Value is " + std::to_string(thumbstick_x_value)).c_str(), this->stick_x_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, thumbstick_x_value, 0); - - LogInput(("Check handle for stick y before update. Value is " + std::to_string(thumbstick_y_value)).c_str(), this->stick_y_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, thumbstick_y_value, 0); - - LogInput(("Check handle for button a before update. Value is " + std::to_string(button_1_value)).c_str(), this->button_a_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, button_1_value, 0); - - LogInput(("Check handle for button b before update. Value is " + std::to_string(button_2_value)).c_str(), this->button_b_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, button_2_value, 0); - - LogInput(("Check handle for stick click before update. Value is " + std::to_string(stick_click_value)).c_str(), this->stick_click_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, stick_click_value, 0); - - LogInput(("Check handle for system before update. Value is " + std::to_string(system_click_value)).c_str(), this->system_component); - vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, system_click_value, 0); - - LogInput(("Check handle for system touch before update. Value is " + std::to_string(system_click_value)).c_str(), this->system_component_touch); - vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, system_click_value, 0); - } -} - -void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) { - if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; + if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + // Check if this device was asked to be identified + auto events = GetDriver()->GetOpenVREvents(); + for (auto event : events) { + // Note here, event.trackedDeviceIndex does not necessarily equal + // device_index_, not sure why, but the component handle will match so we + // can just use that instead + // if (event.trackedDeviceIndex == device_index_) { + if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { + if (event.data.hapticVibration.componentHandle == haptic_component_) { + did_vibrate_ = true; + } + } + //} + } + + // Check if we need to keep vibrating + if (did_vibrate_) { + vibrate_anim_state_ += GetDriver()->GetLastFrameTime().count() / 1000.f; + if (vibrate_anim_state_ > 1.0f) { + did_vibrate_ = false; + vibrate_anim_state_ = 0.0f; + } + } + + if (was_activated_ && is_controller_) { + // Get inputs from protobuf + LogInput(("Check handle for trigger before update. Value is " + + std::to_string(trigger_value_)) + .c_str(), + this->trigger_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, + trigger_value_, 0); + + LogInput(("Check handle for trigger touch before update. Value is " + + std::to_string(trigger_value_click)) + .c_str(), + this->trigger_component_touch_); + vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, + trigger_value_click, 0); + + LogInput(("Check handle for grip before update. Value is " + + std::to_string(grip_value)) + .c_str(), + this->grip_value_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, + grip_value, 0); + + LogInput(("Check handle for stick x before update. Value is " + + std::to_string(thumbstick_x_value)) + .c_str(), + this->stick_x_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, + thumbstick_x_value, 0); + + LogInput(("Check handle for stick y before update. Value is " + + std::to_string(thumbstick_y_value)) + .c_str(), + this->stick_y_component_); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, + thumbstick_y_value, 0); + + LogInput(("Check handle for button a before update. Value is " + + std::to_string(button_1_value)) + .c_str(), + this->button_a_component_); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, + button_1_value, 0); + + LogInput(("Check handle for button b before update. Value is " + + std::to_string(button_2_value)) + .c_str(), + this->button_b_component_); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, + button_2_value, 0); + + LogInput(("Check handle for stick click before update. Value is " + + std::to_string(stick_click_value)) + .c_str(), + this->stick_click_component_); + vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, + stick_click_value, 0); + + LogInput(("Check handle for system before update. Value is " + + std::to_string(system_click_value)) + .c_str(), + this->system_component); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, + system_click_value, 0); + + LogInput(("Check handle for system touch before update. Value is " + + std::to_string(system_click_value)) + .c_str(), + this->system_component_touch); + vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, + system_click_value, 0); + } + + // For controllers: prefer external hand pose (e.g. Virtual Desktop / Steam + // Link on Quest) when in view; when they disconnect or go out of view, use + // SlimeVR positional data. Smoothly blend when swapping between sources. + if (is_controller_) { + auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); + bool want_external = external.has_value() && external->poseIsValid; + vr::DriverPose_t slimevr_pose = last_pose_atomic_.load(); + vr::DriverPose_t target_pose = want_external ? *external : slimevr_pose; + + auto now = std::chrono::steady_clock::now(); + vr::DriverPose_t pose_to_use; + + if (pose_blending_) { + float elapsed_ms = + static_cast( + std::chrono::duration_cast( + now - pose_blend_start_) + .count()) / + 1000.f; + float t = elapsed_ms / static_cast(pose_blend_duration_.count()); + if (t >= 1.0f) { + pose_blending_ = false; + using_external_pose_ = want_external; + pose_to_use = pose_blend_to_; + } else { + // Smooth step for easier-in-out + float s = t * t * (3.0f - 2.0f * t); + pose_to_use = BlendPoses(pose_blend_from_, pose_blend_to_, s); + } + } else { + if (want_external != using_external_pose_) { + pose_blending_ = true; + pose_blend_start_ = now; + pose_blend_from_ = last_output_pose_; + pose_blend_to_ = target_pose; + float s = 0.0f; + pose_to_use = BlendPoses(pose_blend_from_, pose_blend_to_, s); + } else { + pose_to_use = target_pose; + using_external_pose_ = want_external; + } + } - // Setup pose for this frame - auto pose = last_pose_; - //send the new position and rotation from the pipe to the tracker object - if (position.has_x()) { - pose.vecPosition[0] = position.x(); - pose.vecPosition[1] = position.y(); - pose.vecPosition[2] = position.z(); - } + last_output_pose_ = pose_to_use; + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( + device_index_, pose_to_use, sizeof(vr::DriverPose_t)); + } +} - pose.qRotation.w = position.qw(); - pose.qRotation.x = position.qx(); - pose.qRotation.y = position.qy(); - pose.qRotation.z = position.qz(); +void SlimeVRDriver::TrackerDevice::PositionMessage( + messages::Position &position) { + if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + // Setup pose for this frame + auto pose = last_pose_; + // send the new position and rotation from the pipe to the tracker object + if (position.has_x()) { + pose.vecPosition[0] = position.x(); + pose.vecPosition[1] = position.y(); + pose.vecPosition[2] = position.z(); + } + + pose.qRotation.w = position.qw(); + pose.qRotation.x = position.qx(); + pose.qRotation.y = position.qy(); + pose.qRotation.z = position.qz(); if (position.has_vx()) { pose.vecVelocity[0] = position.vx(); @@ -109,432 +202,607 @@ void SlimeVRDriver::TrackerDevice::PositionMessage(messages::Position& position) if (current_universe.has_value()) { auto trans = current_universe.value(); - // TODO: set this once, somewhere? - pose.vecWorldFromDriverTranslation[0] = -trans.translation.v[0]; - pose.vecWorldFromDriverTranslation[1] = -trans.translation.v[1]; - pose.vecWorldFromDriverTranslation[2] = -trans.translation.v[2]; - - pose.qWorldFromDriverRotation.w = cos(trans.yaw / 2); - pose.qWorldFromDriverRotation.x = 0; - pose.qWorldFromDriverRotation.y = sin(trans.yaw / 2); - pose.qWorldFromDriverRotation.z = 0; - } - - bool double_tap = false; - bool triple_tap = false; - - if (fingertracking_enabled_) { - // Set finger rotations - vr::VRBoneTransform_t finger_skeleton_[31]{}; - for (int i = 0; i < position.finger_bone_rotations_size(); i++) - { - // Get data from protobuf - auto fingerData = position.finger_bone_rotations(i); - int fingerBoneName = static_cast(fingerData.name()); - - // Map from our 15 bones to OpenVR's 31 bones - int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; - finger_skeleton_[boneIndex].orientation = { - fingerData.w(), - fingerData.x(), - fingerData.y(), - fingerData.z() - }; - } - - // Update the finger skeleton for this hand. With and without controller have the same pose. - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); - } - - pose.deviceIsConnected = true; - pose.poseIsValid = true; - pose.result = vr::ETrackingResult::TrackingResult_Running_OK; - - if (is_controller_) { - // Set inputs - vr::VRDriverInput()->UpdateBooleanComponent(this->double_tap_component_, double_tap, 0); - vr::VRDriverInput()->UpdateBooleanComponent(this->triple_tap_component_, triple_tap, 0); - } - - // Notify SteamVR that pose was updated - last_pose_atomic_ = (last_pose_ = pose); - - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); + // TODO: set this once, somewhere? + pose.vecWorldFromDriverTranslation[0] = -trans.translation.v[0]; + pose.vecWorldFromDriverTranslation[1] = -trans.translation.v[1]; + pose.vecWorldFromDriverTranslation[2] = -trans.translation.v[2]; + + pose.qWorldFromDriverRotation.w = cos(trans.yaw / 2); + pose.qWorldFromDriverRotation.x = 0; + pose.qWorldFromDriverRotation.y = sin(trans.yaw / 2); + pose.qWorldFromDriverRotation.z = 0; + } + + bool double_tap = false; + bool triple_tap = false; + + if (fingertracking_enabled_) { + // Set finger rotations + vr::VRBoneTransform_t finger_skeleton_[31]{}; + for (int i = 0; i < position.finger_bone_rotations_size(); i++) { + // Get data from protobuf + auto fingerData = position.finger_bone_rotations(i); + int fingerBoneName = static_cast(fingerData.name()); + + // Map from our 15 bones to OpenVR's 31 bones + int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; + finger_skeleton_[boneIndex].orientation = { + fingerData.w(), fingerData.x(), fingerData.y(), fingerData.z()}; + } + + // Update the finger skeleton for this hand. With and without controller + // have the same pose. + vr::VRDriverInput()->UpdateSkeletonComponent( + skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, + finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent( + skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, + finger_skeleton_, 31); + } + + pose.deviceIsConnected = true; + pose.poseIsValid = true; + pose.result = vr::ETrackingResult::TrackingResult_Running_OK; + + if (is_controller_) { + // Set inputs + vr::VRDriverInput()->UpdateBooleanComponent(this->double_tap_component_, + double_tap, 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->triple_tap_component_, + triple_tap, 0); + } + + // Notify SteamVR that pose was updated + last_pose_atomic_ = (last_pose_ = pose); + + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( + device_index_, pose, sizeof(vr::DriverPose_t)); } -void SlimeVRDriver::TrackerDevice::ControllerInputMessage(messages::ControllerInput& controllerInput) { - if (was_activated_ && is_controller_) { - // Get inputs from protobuf, store them for Update which is called during RunFrame - trigger_value_ = controllerInput.trigger(); - trigger_value_click = controllerInput.trigger() > 0.5f; - grip_value = controllerInput.grip(); - thumbstick_x_value = controllerInput.thumbstick_x(); - thumbstick_y_value = controllerInput.thumbstick_y(); - button_1_value = controllerInput.button_1(); - button_2_value = controllerInput.button_2(); - stick_click_value = controllerInput.stick_click(); - system_value = controllerInput.menu_recenter(); - system_click_value = controllerInput.menu_recenter(); - } +void SlimeVRDriver::TrackerDevice::ControllerInputMessage( + messages::ControllerInput &controllerInput) { + if (was_activated_ && is_controller_) { + // Get inputs from protobuf, store them for Update which is called during + // RunFrame + trigger_value_ = controllerInput.trigger(); + trigger_value_click = controllerInput.trigger() > 0.5f; + grip_value = controllerInput.grip(); + thumbstick_x_value = controllerInput.thumbstick_x(); + thumbstick_y_value = controllerInput.thumbstick_y(); + button_1_value = controllerInput.button_1(); + button_2_value = controllerInput.button_2(); + stick_click_value = controllerInput.stick_click(); + system_value = controllerInput.menu_recenter(); + system_click_value = controllerInput.menu_recenter(); + } } -void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery& battery) { - if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) - return; - - // Get the properties handle - auto containerHandle_ = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); - - vr::ETrackedPropertyError err; - - // Set that the tracker reports battery level in case it has not already been set to true - // It's a given that the tracker supports reporting battery life because otherwise a BatteryMessage would not be received - if (vr::VRProperties()->GetBoolProperty(containerHandle_, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err) != true) { - vr::VRProperties()->SetBoolProperty(containerHandle_, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); - } - - if (battery.is_charging()) { - vr::VRProperties()->SetBoolProperty(containerHandle_, vr::Prop_DeviceIsCharging_Bool, true); - } - else { - vr::VRProperties()->SetBoolProperty(containerHandle_, vr::Prop_DeviceIsCharging_Bool, false); - } - - // Set the battery Level; 0 = 0%, 1 = 100% - vr::VRProperties()->SetFloatProperty(containerHandle_, vr::Prop_DeviceBatteryPercentage_Float, battery.battery_level()); +void SlimeVRDriver::TrackerDevice::BatteryMessage(messages::Battery &battery) { + if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + // Get the properties handle + auto containerHandle_ = + GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer( + this->device_index_); + + vr::ETrackedPropertyError err; + + // Set that the tracker reports battery level in case it has not already been + // set to true It's a given that the tracker supports reporting battery life + // because otherwise a BatteryMessage would not be received + if (vr::VRProperties()->GetBoolProperty( + containerHandle_, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err) != + true) { + vr::VRProperties()->SetBoolProperty( + containerHandle_, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); + } + + if (battery.is_charging()) { + vr::VRProperties()->SetBoolProperty(containerHandle_, + vr::Prop_DeviceIsCharging_Bool, true); + } else { + vr::VRProperties()->SetBoolProperty(containerHandle_, + vr::Prop_DeviceIsCharging_Bool, false); + } + + // Set the battery Level; 0 = 0%, 1 = 100% + vr::VRProperties()->SetFloatProperty(containerHandle_, + vr::Prop_DeviceBatteryPercentage_Float, + battery.battery_level()); } -void SlimeVRDriver::TrackerDevice::StatusMessage(messages::TrackerStatus& status) { - if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; - - vr::DriverPose_t pose = last_pose_; - switch (status.status()) { - case messages::TrackerStatus_Status_OK: - pose.deviceIsConnected = true; - pose.poseIsValid = true; - break; - case messages::TrackerStatus_Status_DISCONNECTED: - pose.deviceIsConnected = false; - pose.poseIsValid = false; - break; - case messages::TrackerStatus_Status_ERROR: - case messages::TrackerStatus_Status_BUSY: - default: - pose.deviceIsConnected = true; - pose.poseIsValid = false; - break; - } - - // TODO: send position/rotation of 0 instead of last pose? - - last_pose_atomic_ = (last_pose_ = pose); - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(device_index_, pose, sizeof(vr::DriverPose_t)); +void SlimeVRDriver::TrackerDevice::StatusMessage( + messages::TrackerStatus &status) { + if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + vr::DriverPose_t pose = last_pose_; + switch (status.status()) { + case messages::TrackerStatus_Status_OK: + pose.deviceIsConnected = true; + pose.poseIsValid = true; + break; + case messages::TrackerStatus_Status_DISCONNECTED: + pose.deviceIsConnected = false; + pose.poseIsValid = false; + break; + case messages::TrackerStatus_Status_ERROR: + case messages::TrackerStatus_Status_BUSY: + default: + pose.deviceIsConnected = true; + pose.poseIsValid = false; + break; + } + + // TODO: send position/rotation of 0 instead of last pose? + + last_pose_atomic_ = (last_pose_ = pose); + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( + device_index_, pose, sizeof(vr::DriverPose_t)); } DeviceType SlimeVRDriver::TrackerDevice::GetDeviceType() { - if (is_controller_) { - return DeviceType::CONTROLLER; - } - return DeviceType::TRACKER; + if (is_controller_) { + return DeviceType::CONTROLLER; + } + return DeviceType::TRACKER; } - vr::TrackedDeviceIndex_t SlimeVRDriver::TrackerDevice::GetDeviceIndex() { - return device_index_; + return device_index_; } vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { - device_index_ = unObjectId; - - logger_->Log("Activating tracker %s", serial_.c_str()); - - const std::string log_dir = "C:\\Temp\\SlimeVRLogs\\"; - - // Create directory if it doesn't exist - try { - fs::create_directories(log_dir); - } - catch (...) { - // If this fails, we silently continue (driver must not crash) - } - - // One log file per tracker - const std::string log_path = log_dir + "input.log"; - - input_log_.open(log_path, std::ios::out | std::ios::app); - if (input_log_.is_open()) { - input_log_ << "=== Activating tracker " << serial_ << " ===" << std::endl; - } - - // Get the properties handle - containerHandle_ = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(device_index_); - - // Set some universe ID (Must be 2 or higher) - GetDriver()->GetProperties()->SetUint64Property(containerHandle_, vr::Prop_CurrentUniverseId_Uint64, 4); - - // Set up a model "number" (not needed but good to have) - if (is_controller_) { - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ModelNumber_String, "SlimeVR Virtual Controller"); - } - else { - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ModelNumber_String, "SlimeVR Virtual Tracker"); - } - - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ManufacturerName_String, "SlimeVR"); - - //// Hand selection - if (is_left_hand_) { - GetDriver()->GetProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); - } - else if (is_right_hand_) { - GetDriver()->GetProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); - } - else { - GetDriver()->GetProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); - } - - // Should be treated as controller or as tracker? (Hand = Tracker and Controller = Controller) - if (is_controller_) { - vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetStringProperty(containerHandle_, vr::Prop_ControllerType_String, "slimevr_virtual_controller"); - vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_ControllerHandSelectionPriority_Int32, 2147483647); // Prioritizes our controller over whatever else. - } - else { - vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker); - } - - // Set up a render model path (index controllers for controllers and vive trackers 1.0 for trackers) - std::string model_path; - if (is_controller_) { - vr::VRProperties()->SetStringProperty(containerHandle_, vr::Prop_RenderModelName_String, is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); - } - else { - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_RenderModelName_String, "{htc}/rendermodels/vr_tracker_vive_1_0"); - } - - // Set the icons - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceReady_String, "{slimevr}/icons/tracker_status_ready.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceOff_String, "{slimevr}/icons/tracker_status_off.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceSearching_String, "{slimevr}/icons/tracker_status_ready.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{slimevr}/icons/tracker_status_ready_alert.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceNotReady_String, "{slimevr}/icons/tracker_status_error.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceStandby_String, "{slimevr}/icons/tracker_status_standby.png"); - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_NamedIconPathDeviceAlertLow_String, "{slimevr}/icons/tracker_status_ready_low.png"); - - // Set inputs - if (is_controller_) { - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_InputProfilePath_String, "{slimevr}/input/slimevr_controller_bindings.json"); - uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; - vr::VRProperties()->SetUint64Property(containerHandle_, vr::Prop_SupportedButtons_Uint64, supportedButtons); - - LogInfo("Creating /pose/raw component"); - vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); - LogInputError(input_error, "/pose/raw", this->raw_pose_component_handle_); - - LogInfo("Creating /pose/tip component"); - input_error = vr::VRDriverInput()->CreatePoseComponent(containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); - LogInputError(input_error, "/pose/tip", this->aim_pose_component_handle_); - - LogInfo("Creating /input/double_tap/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/double_tap/click", &this->double_tap_component_); - LogInputError(input_error, "/input/double_tap/click", this->double_tap_component_); - - LogInfo("Creating /input/triple_tap/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/triple_tap/click", &this->triple_tap_component_); - LogInputError(input_error, "/input/triple_tap/click", this->triple_tap_component_); - - LogInfo("Creating /input/a/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/click", &this->button_a_component_); - LogInputError(input_error, "/input/a/click", this->button_a_component_); - - LogInfo("Creating /input/a/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/a/touch", &this->button_a_component_touch_); - LogInputError(input_error, "/input/a/touch", this->button_a_component_touch_); - - LogInfo("Creating /input/b/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/click", &this->button_b_component_); - LogInputError(input_error, "/input/b/click", this->button_b_component_); - - LogInfo("Creating /input/b/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/b/touch", &this->button_b_component_touch_); - LogInputError(input_error, "/input/b/touch", this->button_b_component_touch_); - - LogInfo("Creating /input/system/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/click", &this->system_component); - LogInputError(input_error, "/input/system/click", this->system_component); - - LogInfo("Creating /input/system/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/system/touch", &this->system_component_touch); - LogInputError(input_error, "/input/system/touch", this->system_component_touch); - - LogInfo("Creating /input/trackpad/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/click", &this->trackpad_click_component_); - LogInputError(input_error, "/input/trackpad/click", this->trackpad_click_component_); - - LogInfo("Creating /input/trackpad/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trackpad/touch", &this->trackpad_touch_component_); - LogInputError(input_error, "/input/trackpad/touch", this->trackpad_touch_component_); - - LogInfo("Creating /input/joystick/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/joystick/click", &this->stick_click_component_); - LogInputError(input_error, "/input/joystick/click", this->stick_click_component_); - - LogInfo("Creating /input/thumbstick/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/thumbstick/touch", &this->stick_click_component_touch_); - LogInputError(input_error, "/input/thumbstick/touch", this->stick_click_component_touch_); - - // Scalar components - - LogInfo("Creating /input/trigger/value component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trigger/value", &this->trigger_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - LogInputError(input_error, "/input/trigger/value", this->trigger_component_); - - LogInfo("Creating /input/trigger/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/touch", &this->trigger_component_touch_); - LogInputError(input_error, "/input/trigger/touch", this->trigger_component_touch_); - - LogInfo("Creating /input/trigger/click component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/trigger/click", &this->trigger_component_click_); - LogInputError(input_error, "/input/trigger/click", this->trigger_component_click_); - - LogInfo("Creating /input/grip/value component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/grip/value", &this->grip_value_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - LogInputError(input_error, "/input/grip/value", this->grip_value_component_); - - LogInfo("Creating /input/grip/touch component"); - input_error = vr::VRDriverInput()->CreateBooleanComponent(containerHandle_, "/input/grip/touch", &this->grip_value_component_touch_); - LogInputError(input_error, "/input/grip/touch", this->grip_value_component_touch_); - - LogInfo("Creating /input/trackpad/x component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/x", &this->trackpad_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/trackpad/x", this->trackpad_x_component_); - - LogInfo("Creating /input/trackpad/y component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/trackpad/y", &this->trackpad_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/trackpad/y", this->trackpad_y_component_); - - LogInfo("Creating /input/thumbstick/x component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/thumbstick/x", &this->stick_x_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/thumbstick/x", this->stick_x_component_); - - LogInfo("Creating /input/joystick/y component"); - input_error = vr::VRDriverInput()->CreateScalarComponent(containerHandle_, "/input/thumbstick/y", &this->stick_y_component_, vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - LogInputError(input_error, "/input/thumbstick/y", this->stick_y_component_); - - LogInfo("Creating /output/haptic component"); - input_error = vr::VRDriverInput()->CreateHapticComponent(containerHandle_, "/output/haptic", &this->haptic_component_); - LogInputError(input_error, "/output/haptic", this->haptic_component_); - } - - // Automatically select vive tracker roles and set hints for games that need it (Beat Saber avatar mod, for example) - if (!is_controller_) { - auto role_hint = GetViveRoleHint(tracker_role_); - if (role_hint != "") { - GetDriver()->GetProperties()->SetStringProperty(containerHandle_, vr::Prop_ControllerType_String, role_hint.c_str()); - } - - auto role = GetViveRole(tracker_role_); - if (role != "") { - vr::VRSettings()->SetString(vr::k_pch_Trackers_Section, ("/devices/slimevr/" + serial_).c_str(), role.c_str()); - } - } - - // Setup skeletal input for fingertracking - if (fingertracking_enabled_) { - vr::VRDriverInput()->CreateSkeletonComponent( - containerHandle_, - is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", - is_right_hand_ ? "/skeleton/hand/right" : "/skeleton/hand/left", - "/pose/raw", - vr::EVRSkeletalTrackingLevel::VRSkeletalTracking_Full, - NULL, // Fist - 31, - &skeletal_component_handle_); - - // Update the skeleton so steamvr knows we have an active skeletal input device - vr::VRBoneTransform_t finger_skeleton_[31]{}; - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); - vr::VRDriverInput()->UpdateSkeletonComponent(skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, finger_skeleton_, 31); - } - was_activated_ = true; - return vr::EVRInitError::VRInitError_None; + device_index_ = unObjectId; + + logger_->Log("Activating tracker %s", serial_.c_str()); + + const std::string log_dir = "C:\\Temp\\SlimeVRLogs\\"; + + // Create directory if it doesn't exist + try { + fs::create_directories(log_dir); + } catch (...) { + // If this fails, we silently continue (driver must not crash) + } + + // One log file per tracker + const std::string log_path = log_dir + "input.log"; + + input_log_.open(log_path, std::ios::out | std::ios::app); + if (input_log_.is_open()) { + input_log_ << "=== Activating tracker " << serial_ << " ===" << std::endl; + } + + // Get the properties handle + containerHandle_ = + GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer( + device_index_); + + // Set some universe ID (Must be 2 or higher) + GetDriver()->GetProperties()->SetUint64Property( + containerHandle_, vr::Prop_CurrentUniverseId_Uint64, 4); + + // Set up a model "number" (not needed but good to have) + if (is_controller_) { + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_ModelNumber_String, + "SlimeVR Virtual Controller"); + } else { + GetDriver()->GetProperties()->SetStringProperty(containerHandle_, + vr::Prop_ModelNumber_String, + "SlimeVR Virtual Tracker"); + } + + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_ManufacturerName_String, "SlimeVR"); + + //// Hand selection + if (is_left_hand_) { + GetDriver()->GetProperties()->SetInt32Property( + containerHandle_, vr::Prop_ControllerRoleHint_Int32, + vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + } else if (is_right_hand_) { + GetDriver()->GetProperties()->SetInt32Property( + containerHandle_, vr::Prop_ControllerRoleHint_Int32, + vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + } else { + GetDriver()->GetProperties()->SetInt32Property( + containerHandle_, vr::Prop_ControllerRoleHint_Int32, + vr::ETrackedControllerRole::TrackedControllerRole_OptOut); + } + + // Should be treated as controller or as tracker? (Hand = Tracker and + // Controller = Controller) + if (is_controller_) { + vr::VRProperties()->SetInt32Property(containerHandle_, + vr::Prop_DeviceClass_Int32, + vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetStringProperty(containerHandle_, + vr::Prop_ControllerType_String, + "slimevr_virtual_controller"); + vr::VRProperties()->SetInt32Property( + containerHandle_, vr::Prop_ControllerHandSelectionPriority_Int32, + 2147483647); // Prioritizes our controller over whatever else. + } else { + vr::VRProperties()->SetInt32Property(containerHandle_, + vr::Prop_DeviceClass_Int32, + vr::TrackedDeviceClass_GenericTracker); + } + + // Set up a render model path (index controllers for controllers and vive + // trackers 1.0 for trackers) + std::string model_path; + if (is_controller_) { + vr::VRProperties()->SetStringProperty( + containerHandle_, vr::Prop_RenderModelName_String, + is_right_hand_ ? "{indexcontroller}valve_controller_knu_1_0_right" + : "{indexcontroller}valve_controller_knu_1_0_left"); + } else { + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_RenderModelName_String, + "{htc}/rendermodels/vr_tracker_vive_1_0"); + } + + // Set the icons + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceReady_String, + "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceOff_String, + "{slimevr}/icons/tracker_status_off.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceSearching_String, + "{slimevr}/icons/tracker_status_ready.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceSearchingAlert_String, + "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceReadyAlert_String, + "{slimevr}/icons/tracker_status_ready_alert.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceNotReady_String, + "{slimevr}/icons/tracker_status_error.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceStandby_String, + "{slimevr}/icons/tracker_status_standby.png"); + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_NamedIconPathDeviceAlertLow_String, + "{slimevr}/icons/tracker_status_ready_low.png"); + + // Set inputs + if (is_controller_) { + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_InputProfilePath_String, + "{slimevr}/input/slimevr_controller_bindings.json"); + uint64_t supportedButtons = 0xFFFFFFFFFFFFFFFFULL; + vr::VRProperties()->SetUint64Property( + containerHandle_, vr::Prop_SupportedButtons_Uint64, supportedButtons); + + LogInfo("Creating /pose/raw component"); + vr::EVRInputError input_error = vr::VRDriverInput()->CreatePoseComponent( + containerHandle_, "/pose/raw", &this->raw_pose_component_handle_); + LogInputError(input_error, "/pose/raw", this->raw_pose_component_handle_); + + LogInfo("Creating /pose/tip component"); + input_error = vr::VRDriverInput()->CreatePoseComponent( + containerHandle_, "/pose/tip", &this->aim_pose_component_handle_); + LogInputError(input_error, "/pose/tip", this->aim_pose_component_handle_); + + LogInfo("Creating /input/double_tap/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/double_tap/click", + &this->double_tap_component_); + LogInputError(input_error, "/input/double_tap/click", + this->double_tap_component_); + + LogInfo("Creating /input/triple_tap/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/triple_tap/click", + &this->triple_tap_component_); + LogInputError(input_error, "/input/triple_tap/click", + this->triple_tap_component_); + + LogInfo("Creating /input/a/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/a/click", &this->button_a_component_); + LogInputError(input_error, "/input/a/click", this->button_a_component_); + + LogInfo("Creating /input/a/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/a/touch", &this->button_a_component_touch_); + LogInputError(input_error, "/input/a/touch", + this->button_a_component_touch_); + + LogInfo("Creating /input/b/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/b/click", &this->button_b_component_); + LogInputError(input_error, "/input/b/click", this->button_b_component_); + + LogInfo("Creating /input/b/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/b/touch", &this->button_b_component_touch_); + LogInputError(input_error, "/input/b/touch", + this->button_b_component_touch_); + + LogInfo("Creating /input/system/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/system/click", &this->system_component); + LogInputError(input_error, "/input/system/click", this->system_component); + + LogInfo("Creating /input/system/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/system/touch", &this->system_component_touch); + LogInputError(input_error, "/input/system/touch", + this->system_component_touch); + + LogInfo("Creating /input/trackpad/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/trackpad/click", + &this->trackpad_click_component_); + LogInputError(input_error, "/input/trackpad/click", + this->trackpad_click_component_); + + LogInfo("Creating /input/trackpad/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/trackpad/touch", + &this->trackpad_touch_component_); + LogInputError(input_error, "/input/trackpad/touch", + this->trackpad_touch_component_); + + LogInfo("Creating /input/joystick/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/joystick/click", + &this->stick_click_component_); + LogInputError(input_error, "/input/joystick/click", + this->stick_click_component_); + + LogInfo("Creating /input/thumbstick/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/thumbstick/touch", + &this->stick_click_component_touch_); + LogInputError(input_error, "/input/thumbstick/touch", + this->stick_click_component_touch_); + + // Scalar components + + LogInfo("Creating /input/trigger/value component"); + input_error = vr::VRDriverInput()->CreateScalarComponent( + containerHandle_, "/input/trigger/value", &this->trigger_component_, + vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + LogInputError(input_error, "/input/trigger/value", + this->trigger_component_); + + LogInfo("Creating /input/trigger/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/trigger/touch", + &this->trigger_component_touch_); + LogInputError(input_error, "/input/trigger/touch", + this->trigger_component_touch_); + + LogInfo("Creating /input/trigger/click component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/trigger/click", + &this->trigger_component_click_); + LogInputError(input_error, "/input/trigger/click", + this->trigger_component_click_); + + LogInfo("Creating /input/grip/value component"); + input_error = vr::VRDriverInput()->CreateScalarComponent( + containerHandle_, "/input/grip/value", &this->grip_value_component_, + vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); + LogInputError(input_error, "/input/grip/value", + this->grip_value_component_); + + LogInfo("Creating /input/grip/touch component"); + input_error = vr::VRDriverInput()->CreateBooleanComponent( + containerHandle_, "/input/grip/touch", + &this->grip_value_component_touch_); + LogInputError(input_error, "/input/grip/touch", + this->grip_value_component_touch_); + + LogInfo("Creating /input/trackpad/x component"); + input_error = vr::VRDriverInput()->CreateScalarComponent( + containerHandle_, "/input/trackpad/x", &this->trackpad_x_component_, + vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/trackpad/x", + this->trackpad_x_component_); + + LogInfo("Creating /input/trackpad/y component"); + input_error = vr::VRDriverInput()->CreateScalarComponent( + containerHandle_, "/input/trackpad/y", &this->trackpad_y_component_, + vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/trackpad/y", + this->trackpad_y_component_); + + LogInfo("Creating /input/thumbstick/x component"); + input_error = vr::VRDriverInput()->CreateScalarComponent( + containerHandle_, "/input/thumbstick/x", &this->stick_x_component_, + vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/thumbstick/x", this->stick_x_component_); + + LogInfo("Creating /input/joystick/y component"); + input_error = vr::VRDriverInput()->CreateScalarComponent( + containerHandle_, "/input/thumbstick/y", &this->stick_y_component_, + vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); + LogInputError(input_error, "/input/thumbstick/y", this->stick_y_component_); + + LogInfo("Creating /output/haptic component"); + input_error = vr::VRDriverInput()->CreateHapticComponent( + containerHandle_, "/output/haptic", &this->haptic_component_); + LogInputError(input_error, "/output/haptic", this->haptic_component_); + } + + // Automatically select vive tracker roles and set hints for games that need + // it (Beat Saber avatar mod, for example) + if (!is_controller_) { + auto role_hint = GetViveRoleHint(tracker_role_); + if (role_hint != "") { + GetDriver()->GetProperties()->SetStringProperty( + containerHandle_, vr::Prop_ControllerType_String, role_hint.c_str()); + } + + auto role = GetViveRole(tracker_role_); + if (role != "") { + vr::VRSettings()->SetString(vr::k_pch_Trackers_Section, + ("/devices/slimevr/" + serial_).c_str(), + role.c_str()); + } + } + + // Setup skeletal input for fingertracking + if (fingertracking_enabled_) { + vr::VRDriverInput()->CreateSkeletonComponent( + containerHandle_, + is_right_hand_ ? "/input/skeleton/right" : "/input/skeleton/left", + is_right_hand_ ? "/skeleton/hand/right" : "/skeleton/hand/left", + "/pose/raw", vr::EVRSkeletalTrackingLevel::VRSkeletalTracking_Full, + NULL, // Fist + 31, &skeletal_component_handle_); + + // Update the skeleton so steamvr knows we have an active skeletal input + // device + vr::VRBoneTransform_t finger_skeleton_[31]{}; + vr::VRDriverInput()->UpdateSkeletonComponent( + skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, + finger_skeleton_, 31); + vr::VRDriverInput()->UpdateSkeletonComponent( + skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, + finger_skeleton_, 31); + } + was_activated_ = true; + return vr::EVRInitError::VRInitError_None; } -void SlimeVRDriver::TrackerDevice::LogInfo(const char* message) { - if (input_log_.is_open()) { - input_log_ << "[Info] " << message << std::endl; - input_log_.flush(); - } +void SlimeVRDriver::TrackerDevice::LogInfo(const char *message) { + if (input_log_.is_open()) { + input_log_ << "[Info] " << message << std::endl; + input_log_.flush(); + } } -void SlimeVRDriver::TrackerDevice::LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle) { - if (!input_log_.is_open()) return; - - bool validHandle = componentHandle != vr::k_ulInvalidInputComponentHandle; - input_log_ << "[" - << (err == vr::VRInputError_None ? "Info" : "InputError") - << "] " - << path - << "\r\n Handle: " - << componentHandle - << "\r\n Handle Is Valid: " - << (validHandle ? "true" : "false") - << "\r\n Failure Result: " - << GetInputErrorName(err) - << " (" << err << ")" - << std::endl; - input_log_.flush(); // force write immediately +void SlimeVRDriver::TrackerDevice::LogInputError( + vr::EVRInputError err, const char *path, + vr::VRInputComponentHandle_t componentHandle) { + if (!input_log_.is_open()) + return; + + bool validHandle = componentHandle != vr::k_ulInvalidInputComponentHandle; + input_log_ << "[" << (err == vr::VRInputError_None ? "Info" : "InputError") + << "] " << path << "\r\n Handle: " << componentHandle + << "\r\n Handle Is Valid: " << (validHandle ? "true" : "false") + << "\r\n Failure Result: " << GetInputErrorName(err) << " (" << err + << ")" << std::endl; + input_log_.flush(); // force write immediately } -void SlimeVRDriver::TrackerDevice::LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle) { - if (!input_log_.is_open()) return; - - bool validHandle = componentHandle != vr::k_ulInvalidInputComponentHandle; - input_log_ << "[" - << "Info" - << "] " - << path - << "\r\n Handle: " - << componentHandle - << "\r\n Handle Is Valid: " - << (validHandle ? "true" : "false") - << std::endl; - input_log_.flush(); // force write immediately +void SlimeVRDriver::TrackerDevice::LogInput( + const char *path, vr::VRInputComponentHandle_t componentHandle) { + if (!input_log_.is_open()) + return; + + bool validHandle = componentHandle != vr::k_ulInvalidInputComponentHandle; + input_log_ << "[" + << "Info" + << "] " << path << "\r\n Handle: " << componentHandle + << "\r\n Handle Is Valid: " << (validHandle ? "true" : "false") + << std::endl; + input_log_.flush(); // force write immediately } -const char* SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { - switch (err) { - case vr::VRInputError_None: return "None"; - case vr::VRInputError_NameNotFound: return "NameNotFound"; - case vr::VRInputError_WrongType: return "WrongType"; - // Add others as needed - default: return "Unknown"; - } +const char * +SlimeVRDriver::TrackerDevice::GetInputErrorName(vr::EVRInputError err) { + switch (err) { + case vr::VRInputError_None: + return "None"; + case vr::VRInputError_NameNotFound: + return "NameNotFound"; + case vr::VRInputError_WrongType: + return "WrongType"; + // Add others as needed + default: + return "Unknown"; + } } void SlimeVRDriver::TrackerDevice::Deactivate() { - device_index_ = vr::k_unTrackedDeviceIndexInvalid; + device_index_ = vr::k_unTrackedDeviceIndexInvalid; } -void SlimeVRDriver::TrackerDevice::EnterStandby() { +void SlimeVRDriver::TrackerDevice::EnterStandby() {} + +void *SlimeVRDriver::TrackerDevice::GetComponent( + const char *pchComponentNameAndVersion) { + return nullptr; } -void* SlimeVRDriver::TrackerDevice::GetComponent(const char* pchComponentNameAndVersion) { - return nullptr; +void SlimeVRDriver::TrackerDevice::DebugRequest(const char *pchRequest, + char *pchResponseBuffer, + uint32_t unResponseBufferSize) { + if (unResponseBufferSize >= 1) { + pchResponseBuffer[0] = 0; + } } -void SlimeVRDriver::TrackerDevice::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) { - if (unResponseBufferSize >= 1) { - pchResponseBuffer[0] = 0; - } +vr::DriverPose_t +SlimeVRDriver::TrackerDevice::BlendPoses(const vr::DriverPose_t &from, + const vr::DriverPose_t &to, float t) { + vr::DriverPose_t out = + to; // copy world-from-driver, validity, etc. from target + + out.vecPosition[0] = + from.vecPosition[0] + t * (to.vecPosition[0] - from.vecPosition[0]); + out.vecPosition[1] = + from.vecPosition[1] + t * (to.vecPosition[1] - from.vecPosition[1]); + out.vecPosition[2] = + from.vecPosition[2] + t * (to.vecPosition[2] - from.vecPosition[2]); + + double dot = + from.qRotation.w * to.qRotation.w + from.qRotation.x * to.qRotation.x + + from.qRotation.y * to.qRotation.y + from.qRotation.z * to.qRotation.z; + double to_w = to.qRotation.w, to_x = to.qRotation.x, to_y = to.qRotation.y, + to_z = to.qRotation.z; + if (dot < 0.0) { + to_w = -to_w; + to_x = -to_x; + to_y = -to_y; + to_z = -to_z; + dot = -dot; + } + dot = (dot > 1.0) ? 1.0 : (dot < -1.0 ? -1.0 : dot); + double theta = std::acos(dot); + double sin_theta = std::sin(theta); + if (sin_theta > 1e-6) { + double a = std::sin((1.0 - static_cast(t)) * theta) / sin_theta; + double b = std::sin(static_cast(t) * theta) / sin_theta; + out.qRotation.w = static_cast(a * from.qRotation.w + b * to_w); + out.qRotation.x = static_cast(a * from.qRotation.x + b * to_x); + out.qRotation.y = static_cast(a * from.qRotation.y + b * to_y); + out.qRotation.z = static_cast(a * from.qRotation.z + b * to_z); + } + + out.vecVelocity[0] = + from.vecVelocity[0] + t * (to.vecVelocity[0] - from.vecVelocity[0]); + out.vecVelocity[1] = + from.vecVelocity[1] + t * (to.vecVelocity[1] - from.vecVelocity[1]); + out.vecVelocity[2] = + from.vecVelocity[2] + t * (to.vecVelocity[2] - from.vecVelocity[2]); + out.vecAngularVelocity[0] = + from.vecAngularVelocity[0] + + t * (to.vecAngularVelocity[0] - from.vecAngularVelocity[0]); + out.vecAngularVelocity[1] = + from.vecAngularVelocity[1] + + t * (to.vecAngularVelocity[1] - from.vecAngularVelocity[1]); + out.vecAngularVelocity[2] = + from.vecAngularVelocity[2] + + t * (to.vecAngularVelocity[2] - from.vecAngularVelocity[2]); + + return out; } vr::DriverPose_t SlimeVRDriver::TrackerDevice::GetPose() { - return last_pose_atomic_; + return last_pose_atomic_; } -int SlimeVRDriver::TrackerDevice::GetDeviceId() { - return device_id_; -} +int SlimeVRDriver::TrackerDevice::GetDeviceId() { return device_id_; } void SlimeVRDriver::TrackerDevice::SetDeviceId(int device_id) { - device_id_ = device_id; + device_id_ = device_id; } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 5b3ce4e..4bed984 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -1,141 +1,161 @@ #pragma once +#include #include #include -#include + #include -#include #include +#include -#include -#include + +#include +#include #include +#include #include -#include -#include +#include + -#include "TrackerRole.hpp" #include "Logger.hpp" +#include "TrackerRole.hpp" + namespace SlimeVRDriver { - class TrackerDevice : public IVRDevice { - public: - TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); - ~TrackerDevice() = default; - void LogInput(const char* path, vr::VRInputComponentHandle_t componentHandle); - void LogInputError(vr::EVRInputError err, const char* path, vr::VRInputComponentHandle_t componentHandle); - void LogInfo(const char* message); - // Inherited via IVRDevice - virtual std::string GetSerial() override; - virtual void Update() override; - virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; - virtual DeviceType GetDeviceType() override; - virtual int GetDeviceId() override; - virtual void SetDeviceId(int device_id) override; - virtual void PositionMessage(messages::Position& position) override; - virtual void ControllerInputMessage(messages::ControllerInput& position) override; - virtual void StatusMessage(messages::TrackerStatus& status) override; - virtual void BatteryMessage(messages::Battery& battery) override; - const char* GetInputErrorName(vr::EVRInputError err); - - // Inherited via ITrackedDeviceServerDriver - virtual vr::EVRInitError Activate(uint32_t unObjectId) override; - virtual void Deactivate() override; - virtual void EnterStandby() override; - virtual void* GetComponent(const char* pchComponentNameAndVersion) override; - virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; - virtual vr::DriverPose_t GetPose() override; - vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t& pose); - private: - std::ofstream input_log_; - std::shared_ptr logger_ = std::make_shared(); - - std::atomic device_index_ = vr::k_unTrackedDeviceIndexInvalid; - std::string serial_; - vr::PropertyContainerHandle_t containerHandle_; - - int device_id_; - TrackerRole tracker_role_; - bool fingertracking_enabled_; - - vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); - std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); - - bool did_vibrate_ = false; - float vibrate_anim_state_ = 0.f; - bool was_activated_ = false; - vr::VRInputComponentHandle_t haptic_component_ = 0; - vr::VRInputComponentHandle_t double_tap_component_ = 0; - vr::VRInputComponentHandle_t triple_tap_component_ = 0; - - vr::VRInputComponentHandle_t ignored = 0; - vr::VRInputComponentHandle_t pose_component_handle_ = 0; - - vr::VRInputComponentHandle_t raw_pose_component_handle_ = 0; - vr::VRInputComponentHandle_t aim_pose_component_handle_ = 0; - - vr::VRInputComponentHandle_t trigger_component_ = 0; - vr::VRInputComponentHandle_t grip_value_component_ = 0; - vr::VRInputComponentHandle_t stick_x_component_ = 0; - vr::VRInputComponentHandle_t stick_y_component_ = 0; - vr::VRInputComponentHandle_t button_a_component_ = 0; - vr::VRInputComponentHandle_t button_b_component_ = 0; - - vr::VRInputComponentHandle_t trackpad_x_component_ = 0; - vr::VRInputComponentHandle_t trackpad_y_component_ = 0; - vr::VRInputComponentHandle_t trackpad_click_component_ = 0; - vr::VRInputComponentHandle_t trackpad_touch_component_ = 0; - - - vr::VRInputComponentHandle_t stick_click_component_ = 0; - vr::VRInputComponentHandle_t system_component = 0; - vr::VRInputComponentHandle_t system_component_touch = 0; - - - vr::VRInputComponentHandle_t trigger_component_click_ = 0; - vr::VRInputComponentHandle_t trigger_component_touch_ = 0; - - vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; - vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; - vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; - vr::VRInputComponentHandle_t button_a_component_touch_ = 0; - vr::VRInputComponentHandle_t button_b_component_touch_ = 0; - vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; - - bool is_controller_; - bool is_left_hand_; - bool is_right_hand_; - - vr::VRInputComponentHandle_t skeletal_component_handle_; - - float trigger_value_ = 0; - bool trigger_value_click = false; - float grip_value = 0; - float thumbstick_x_value = 0; - float thumbstick_y_value = 0; - bool button_1_value = false; - bool button_2_value = false; - bool stick_click_value = false; - bool system_value = false; - bool system_click_value = false; - const int protobuf_fingers_to_openvr[15] = { - 2, // THUMB_METACARPAL → eBone_Thumb1 - 3, // THUMB_PROXIMAL → eBone_Thumb2 - 4, // THUMB_DISTAL → eBone_Thumb3 - 6, // INDEX_PROXIMAL → eBone_IndexFinger1 - 7, // INDEX_INTERMEDIATE → eBone_IndexFinger2 - 8, // INDEX_DISTAL → eBone_IndexFinger3 - 11, // MIDDLE_PROXIMAL → eBone_MiddleFinger1 - 12, // MIDDLE_INTERMEDIATE → eBone_MiddleFinger2 - 13, // MIDDLE_DISTAL → eBone_MiddleFinger3 - 16, // RING_PROXIMAL → eBone_RingFinger1 - 17, // RING_INTERMEDIATE → eBone_RingFinger2 - 18, // RING_DISTAL → eBone_RingFinger3 - 21, // LITTLE_PROXIMAL → eBone_PinkyFinger1 - 22, // LITTLE_INTERMEDIATE → eBone_PinkyFinger2 - 23 // LITTLE_DISTAL → eBone_PinkyFinger3 - }; - }; -}; \ No newline at end of file +class TrackerDevice : public IVRDevice { +public: + TrackerDevice(std::string serial, int device_id, TrackerRole tracker_role); + ~TrackerDevice() = default; + void LogInput(const char *path, vr::VRInputComponentHandle_t componentHandle); + void LogInputError(vr::EVRInputError err, const char *path, + vr::VRInputComponentHandle_t componentHandle); + void LogInfo(const char *message); + // Inherited via IVRDevice + virtual std::string GetSerial() override; + virtual void Update() override; + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; + virtual DeviceType GetDeviceType() override; + virtual int GetDeviceId() override; + virtual void SetDeviceId(int device_id) override; + virtual void PositionMessage(messages::Position &position) override; + virtual void + ControllerInputMessage(messages::ControllerInput &position) override; + virtual void StatusMessage(messages::TrackerStatus &status) override; + virtual void BatteryMessage(messages::Battery &battery) override; + const char *GetInputErrorName(vr::EVRInputError err); + + // Inherited via ITrackedDeviceServerDriver + virtual vr::EVRInitError Activate(uint32_t unObjectId) override; + virtual void Deactivate() override; + virtual void EnterStandby() override; + virtual void *GetComponent(const char *pchComponentNameAndVersion) override; + virtual void DebugRequest(const char *pchRequest, char *pchResponseBuffer, + uint32_t unResponseBufferSize) override; + virtual vr::DriverPose_t GetPose() override; + vr::HmdMatrix34_t ToHmdMatrix(const vr::DriverPose_t &pose); + +private: + std::ofstream input_log_; + std::shared_ptr logger_ = std::make_shared(); + + std::atomic device_index_ = + vr::k_unTrackedDeviceIndexInvalid; + std::string serial_; + vr::PropertyContainerHandle_t containerHandle_; + + int device_id_; + TrackerRole tracker_role_; + bool fingertracking_enabled_; + + vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); + std::atomic last_pose_atomic_ = + IVRDevice::MakeDefaultPose(); + + bool did_vibrate_ = false; + float vibrate_anim_state_ = 0.f; + bool was_activated_ = false; + vr::VRInputComponentHandle_t haptic_component_ = 0; + vr::VRInputComponentHandle_t double_tap_component_ = 0; + vr::VRInputComponentHandle_t triple_tap_component_ = 0; + + vr::VRInputComponentHandle_t ignored = 0; + vr::VRInputComponentHandle_t pose_component_handle_ = 0; + + vr::VRInputComponentHandle_t raw_pose_component_handle_ = 0; + vr::VRInputComponentHandle_t aim_pose_component_handle_ = 0; + + vr::VRInputComponentHandle_t trigger_component_ = 0; + vr::VRInputComponentHandle_t grip_value_component_ = 0; + vr::VRInputComponentHandle_t stick_x_component_ = 0; + vr::VRInputComponentHandle_t stick_y_component_ = 0; + vr::VRInputComponentHandle_t button_a_component_ = 0; + vr::VRInputComponentHandle_t button_b_component_ = 0; + + vr::VRInputComponentHandle_t trackpad_x_component_ = 0; + vr::VRInputComponentHandle_t trackpad_y_component_ = 0; + vr::VRInputComponentHandle_t trackpad_click_component_ = 0; + vr::VRInputComponentHandle_t trackpad_touch_component_ = 0; + + vr::VRInputComponentHandle_t stick_click_component_ = 0; + vr::VRInputComponentHandle_t system_component = 0; + vr::VRInputComponentHandle_t system_component_touch = 0; + + vr::VRInputComponentHandle_t trigger_component_click_ = 0; + vr::VRInputComponentHandle_t trigger_component_touch_ = 0; + + vr::VRInputComponentHandle_t grip_value_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_x_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_y_component_touch_ = 0; + vr::VRInputComponentHandle_t button_a_component_touch_ = 0; + vr::VRInputComponentHandle_t button_b_component_touch_ = 0; + vr::VRInputComponentHandle_t stick_click_component_touch_ = 0; + + bool is_controller_; + bool is_left_hand_; + bool is_right_hand_; + + // Smooth transition when swapping between external (VD/Steam Link) and + // SlimeVR pose + bool using_external_pose_ = false; + bool pose_blending_ = false; + std::chrono::steady_clock::time_point pose_blend_start_; + vr::DriverPose_t pose_blend_from_; + vr::DriverPose_t pose_blend_to_; + vr::DriverPose_t last_output_pose_ = IVRDevice::MakeDefaultPose(); + static constexpr std::chrono::milliseconds pose_blend_duration_{200}; + static vr::DriverPose_t BlendPoses(const vr::DriverPose_t &from, + const vr::DriverPose_t &to, float t); + + vr::VRInputComponentHandle_t skeletal_component_handle_; + + float trigger_value_ = 0; + bool trigger_value_click = false; + float grip_value = 0; + float thumbstick_x_value = 0; + float thumbstick_y_value = 0; + bool button_1_value = false; + bool button_2_value = false; + bool stick_click_value = false; + bool system_value = false; + bool system_click_value = false; + const int protobuf_fingers_to_openvr[15] = { + 2, // THUMB_METACARPAL → eBone_Thumb1 + 3, // THUMB_PROXIMAL → eBone_Thumb2 + 4, // THUMB_DISTAL → eBone_Thumb3 + 6, // INDEX_PROXIMAL → eBone_IndexFinger1 + 7, // INDEX_INTERMEDIATE → eBone_IndexFinger2 + 8, // INDEX_DISTAL → eBone_IndexFinger3 + 11, // MIDDLE_PROXIMAL → eBone_MiddleFinger1 + 12, // MIDDLE_INTERMEDIATE → eBone_MiddleFinger2 + 13, // MIDDLE_DISTAL → eBone_MiddleFinger3 + 16, // RING_PROXIMAL → eBone_RingFinger1 + 17, // RING_INTERMEDIATE → eBone_RingFinger2 + 18, // RING_DISTAL → eBone_RingFinger3 + 21, // LITTLE_PROXIMAL → eBone_PinkyFinger1 + 22, // LITTLE_INTERMEDIATE → eBone_PinkyFinger2 + 23 // LITTLE_DISTAL → eBone_PinkyFinger3 + }; +}; +}; // namespace SlimeVRDriver \ No newline at end of file diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index ecb8e98..5269ad2 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -1,47 +1,54 @@ #include "VRDriver.hpp" -#include #include "TrackerRole.hpp" +#include "VRPaths_openvr.hpp" +#include #include #include -#include "VRPaths_openvr.hpp" - -vr::EVRInitError SlimeVRDriver::VRDriver::Init(vr::IVRDriverContext* pDriverContext) { - // Perform driver context initialisation - if (vr::EVRInitError init_error = vr::InitServerDriverContext(pDriverContext); init_error != vr::EVRInitError::VRInitError_None) { - return init_error; - } - - logger_->Log("Activating SlimeVR Driver..."); - - try { - auto json = simdjson::padded_string::load(GetVRPathRegistryFilename()); // load VR Path Registry - simdjson::ondemand::document doc = json_parser_.iterate(json); - auto path = std::string { doc.get_object()["config"].at(0).get_string().value() }; - default_chap_path_ = GetDefaultChaperoneFromConfigPath(path); - } catch (simdjson::simdjson_error& e) { - logger_->Log("Error getting VR Config path, continuing (error code {})", std::to_string(e.error())); - } +#include - logger_->Log("SlimeVR Driver Loaded Successfully"); - bridge_ = std::make_shared( - std::static_pointer_cast(std::make_shared("Bridge")), - std::bind(&SlimeVRDriver::VRDriver::OnBridgeMessage, this, std::placeholders::_1) - ); - bridge_->Start(); +vr::EVRInitError +SlimeVRDriver::VRDriver::Init(vr::IVRDriverContext *pDriverContext) { + // Perform driver context initialisation + if (vr::EVRInitError init_error = vr::InitServerDriverContext(pDriverContext); + init_error != vr::EVRInitError::VRInitError_None) { + return init_error; + } - exiting_pose_request_thread_ = false; - pose_request_thread_ = - std::make_unique(&SlimeVRDriver::VRDriver::RunPoseRequestThread, this); + logger_->Log("Activating SlimeVR Driver..."); - return vr::VRInitError_None; + try { + auto json = simdjson::padded_string::load( + GetVRPathRegistryFilename()); // load VR Path Registry + simdjson::ondemand::document doc = json_parser_.iterate(json); + auto path = + std::string{doc.get_object()["config"].at(0).get_string().value()}; + default_chap_path_ = GetDefaultChaperoneFromConfigPath(path); + } catch (simdjson::simdjson_error &e) { + logger_->Log("Error getting VR Config path, continuing (error code {})", + std::to_string(e.error())); + } + + logger_->Log("SlimeVR Driver Loaded Successfully"); + + bridge_ = std::make_shared( + std::static_pointer_cast(std::make_shared("Bridge")), + std::bind(&SlimeVRDriver::VRDriver::OnBridgeMessage, this, + std::placeholders::_1)); + bridge_->Start(); + + exiting_pose_request_thread_ = false; + pose_request_thread_ = std::make_unique( + &SlimeVRDriver::VRDriver::RunPoseRequestThread, this); + + return vr::VRInitError_None; } void SlimeVRDriver::VRDriver::Cleanup() { - exiting_pose_request_thread_ = true; - pose_request_thread_->join(); - pose_request_thread_.reset(); - bridge_->Stop(); + exiting_pose_request_thread_ = true; + pose_request_thread_->join(); + pose_request_thread_.reset(); + bridge_->Stop(); } void SlimeVRDriver::VRDriver::RunPoseRequestThread() { @@ -182,90 +189,236 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { std::this_thread::sleep_for(std::chrono::milliseconds(2)); } - logger_->Log("Pose request thread exited"); -} -void SlimeVRDriver::VRDriver::RunFrame() { - // Collect events - vr::VREvent_t event; - std::vector events; - while (vr::VRServerDriverHost()->PollNextEvent(&event, sizeof(event))) { - events.push_back(event); + messages::ProtobufMessage *message = + google::protobuf::Arena::CreateMessage( + &arena_); + + if (!sent_hmd_add_message_) { + // Send add message for HMD + messages::TrackerAdded *tracker_added = + google::protobuf::Arena::CreateMessage( + &arena_); + message->set_allocated_tracker_added(tracker_added); + tracker_added->set_tracker_id(0); + tracker_added->set_tracker_role(TrackerRole::HMD); + tracker_added->set_tracker_serial("HMD"); + tracker_added->set_tracker_name("HMD"); + bridge_->SendBridgeMessage(*message); + + messages::TrackerStatus *tracker_status = + google::protobuf::Arena::CreateMessage( + &arena_); + message->set_allocated_tracker_status(tracker_status); + tracker_status->set_tracker_id(0); + tracker_status->set_status( + messages::TrackerStatus_Status::TrackerStatus_Status_OK); + bridge_->SendBridgeMessage(*message); + + sent_hmd_add_message_ = true; + logger_->Log("Sent HMD hello message"); } - openvr_events_ = std::move(events); - - // Update frame timing - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - frame_timing_ = std::chrono::duration_cast(now - last_frame_time_); - last_frame_time_ = now; - - // Update devices - { - std::lock_guard lock(devices_mutex_); - for (auto& device : devices_) { - device->Update(); + + vr::PropertyContainerHandle_t hmd_prop_container = + vr::VRProperties()->TrackedDeviceToPropertyContainer( + vr::k_unTrackedDeviceIndex_Hmd); + + vr::ETrackedPropertyError universe_error; + uint64_t universe = vr::VRProperties()->GetUint64Property( + hmd_prop_container, vr::Prop_CurrentUniverseId_Uint64, &universe_error); + if (universe_error == vr::ETrackedPropertyError::TrackedProp_Success) { + if (!current_universe_.has_value() || + current_universe_.value().first != universe) { + auto result = SearchUniverses(universe); + if (result.has_value()) { + current_universe_.emplace(universe, result.value()); + logger_->Log("Found current universe"); + } else { + logger_->Log("Failed to find current universe!"); } + } + } else if (universe_error != last_universe_error_) { + logger_->Log( + "Failed to find current universe: Prop_CurrentUniverseId_Uint64 " + "error = {}", + vr::VRPropertiesRaw()->GetPropErrorNameFromEnum(universe_error)); + } + last_universe_error_ = universe_error; + + vr::TrackedDevicePose_t hmd_pose; + vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0.0f, &hmd_pose, 1); + + vr::HmdQuaternion_t q = GetRotation(hmd_pose.mDeviceToAbsoluteTracking); + vr::HmdVector3_t pos = GetPosition(hmd_pose.mDeviceToAbsoluteTracking); + + if (current_universe_.has_value()) { + auto trans = current_universe_.value().second; + pos.v[0] += trans.translation.v[0]; + pos.v[1] += trans.translation.v[1]; + pos.v[2] += trans.translation.v[2]; + + // rotate by quaternion w = cos(-trans.yaw / 2), x = 0, y = sin(-trans.yaw + // / 2), z = 0 + auto tmp_w = cos(-trans.yaw / 2); + auto tmp_y = sin(-trans.yaw / 2); + auto new_w = tmp_w * q.w - tmp_y * q.y; + auto new_x = tmp_w * q.x + tmp_y * q.z; + auto new_y = tmp_w * q.y + tmp_y * q.w; + auto new_z = tmp_w * q.z - tmp_y * q.x; + + q.w = new_w; + q.x = new_x; + q.y = new_y; + q.z = new_z; + + // rotate point on the xz plane by -trans.yaw radians + // this is equivilant to the quaternion multiplication, after applying the + // double angle formula. + float tmp_sin = sin(-trans.yaw); + float tmp_cos = cos(-trans.yaw); + auto pos_x = pos.v[0] * tmp_cos + pos.v[2] * tmp_sin; + auto pos_z = pos.v[0] * -tmp_sin + pos.v[2] * tmp_cos; + + pos.v[0] = pos_x; + pos.v[2] = pos_z; + } + + messages::Position *hmd_position = + google::protobuf::Arena::CreateMessage(&arena_); + message->set_allocated_position(hmd_position); + hmd_position->set_tracker_id(0); + hmd_position->set_data_source(messages::Position_DataSource_FULL); + hmd_position->set_x(pos.v[0]); + hmd_position->set_y(pos.v[1]); + hmd_position->set_z(pos.v[2]); + hmd_position->set_qx((float)q.x); + hmd_position->set_qy((float)q.y); + hmd_position->set_qz((float)q.z); + hmd_position->set_qw((float)q.w); + bridge_->SendBridgeMessage(*message); + + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - + battery_sent_at_) + .count() > 100) { + vr::ETrackedPropertyError err; + if (vr::VRProperties()->GetBoolProperty( + hmd_prop_container, vr::Prop_DeviceProvidesBatteryStatus_Bool, + &err)) { + messages::Battery *hmdBattery = + google::protobuf::Arena::CreateMessage(&arena_); + message->set_allocated_battery(hmdBattery); + hmdBattery->set_tracker_id(0); + hmdBattery->set_battery_level( + vr::VRProperties()->GetFloatProperty( + hmd_prop_container, vr::Prop_DeviceBatteryPercentage_Float, + &err) * + 100); + hmdBattery->set_is_charging(vr::VRProperties()->GetBoolProperty( + hmd_prop_container, vr::Prop_DeviceIsCharging_Bool, &err)); + bridge_->SendBridgeMessage(*message); + } + battery_sent_at_ = now; } + + arena_.Reset(); + + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + logger_->Log("Pose request thread exited"); } -void SlimeVRDriver::VRDriver::OnBridgeMessage(const messages::ProtobufMessage& message) { +void SlimeVRDriver::VRDriver::RunFrame() { + // Collect events + vr::VREvent_t event; + std::vector events; + while (vr::VRServerDriverHost()->PollNextEvent(&event, sizeof(event))) { + events.push_back(event); + } + openvr_events_ = std::move(events); + + // Update frame timing + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + frame_timing_ = std::chrono::duration_cast( + now - last_frame_time_); + last_frame_time_ = now; + + // Update external controller poses (e.g. Virtual Desktop / Steam Link on + // Quest) so we can prefer their hand position when in view and fall back to + // SlimeVR when not. + { std::lock_guard lock(devices_mutex_); - if (message.has_tracker_added()) { - messages::TrackerAdded ta = message.tracker_added(); - AddDevice(std::make_shared(ta.tracker_serial(), ta.tracker_id(), static_cast(ta.tracker_role()))); - } else if (message.has_position()) { - messages::Position pos = message.position(); - auto device = devices_by_id_.find(pos.tracker_id()); - if (device != devices_by_id_.end()) { - device->second->PositionMessage(pos); - } + UpdateExternalControllerPoses(); + } + + // Update devices + { + std::lock_guard lock(devices_mutex_); + for (auto &device : devices_) { + device->Update(); } - else if (message.has_controller_input()) { - messages::ControllerInput controllerInput = message.controller_input(); - auto device = devices_by_id_.find(controllerInput.tracker_id()); - if (device != devices_by_id_.end()) { - device->second->ControllerInputMessage(controllerInput); - } + } +} + +void SlimeVRDriver::VRDriver::OnBridgeMessage( + const messages::ProtobufMessage &message) { + std::lock_guard lock(devices_mutex_); + if (message.has_tracker_added()) { + messages::TrackerAdded ta = message.tracker_added(); + AddDevice(std::make_shared( + ta.tracker_serial(), ta.tracker_id(), + static_cast(ta.tracker_role()))); + } else if (message.has_position()) { + messages::Position pos = message.position(); + auto device = devices_by_id_.find(pos.tracker_id()); + if (device != devices_by_id_.end()) { + device->second->PositionMessage(pos); } - else if (message.has_tracker_status()) { - messages::TrackerStatus status = message.tracker_status(); - auto device = devices_by_id_.find(status.tracker_id()); - if (device != devices_by_id_.end()) { - device->second->StatusMessage(status); - static const std::unordered_map status_map = { - { messages::TrackerStatus_Status_OK, "OK" }, - { messages::TrackerStatus_Status_DISCONNECTED, "DISCONNECTED" }, - { messages::TrackerStatus_Status_ERROR, "ERROR" }, - { messages::TrackerStatus_Status_BUSY, "BUSY" }, - }; - if (status_map.count(status.status())) { - logger_->Log("Tracker status id {} status {}", status.tracker_id(), status_map.at(status.status())); - } - } - } else if (message.has_battery()) { - messages::Battery bat = message.battery(); - auto device = this->devices_by_id_.find(bat.tracker_id()); - if (device != this->devices_by_id_.end()) { - device->second->BatteryMessage(bat); - } + } else if (message.has_controller_input()) { + messages::ControllerInput controllerInput = message.controller_input(); + auto device = devices_by_id_.find(controllerInput.tracker_id()); + if (device != devices_by_id_.end()) { + device->second->ControllerInputMessage(controllerInput); } + } else if (message.has_tracker_status()) { + messages::TrackerStatus status = message.tracker_status(); + auto device = devices_by_id_.find(status.tracker_id()); + if (device != devices_by_id_.end()) { + device->second->StatusMessage(status); + static const std::unordered_map + status_map = { + {messages::TrackerStatus_Status_OK, "OK"}, + {messages::TrackerStatus_Status_DISCONNECTED, "DISCONNECTED"}, + {messages::TrackerStatus_Status_ERROR, "ERROR"}, + {messages::TrackerStatus_Status_BUSY, "BUSY"}, + }; + if (status_map.count(status.status())) { + logger_->Log("Tracker status id {} status {}", status.tracker_id(), + status_map.at(status.status())); + } + } + } else if (message.has_battery()) { + messages::Battery bat = message.battery(); + auto device = this->devices_by_id_.find(bat.tracker_id()); + if (device != this->devices_by_id_.end()) { + device->second->BatteryMessage(bat); + } + } } -bool SlimeVRDriver::VRDriver::ShouldBlockStandbyMode() { - return false; -} +bool SlimeVRDriver::VRDriver::ShouldBlockStandbyMode() { return false; } -void SlimeVRDriver::VRDriver::EnterStandby() { -} +void SlimeVRDriver::VRDriver::EnterStandby() {} -void SlimeVRDriver::VRDriver::LeaveStandby() { -} +void SlimeVRDriver::VRDriver::LeaveStandby() {} -std::vector> SlimeVRDriver::VRDriver::GetDevices() { - std::lock_guard lock(devices_mutex_); - std::vector> devices; - devices.assign(devices.begin(), devices.end()); - return devices; +std::vector> +SlimeVRDriver::VRDriver::GetDevices() { + std::lock_guard lock(devices_mutex_); + std::vector> devices; + devices.assign(devices.begin(), devices.end()); + return devices; } const std::vector& SlimeVRDriver::VRDriver::GetOpenVREvents() { @@ -273,136 +426,157 @@ const std::vector& SlimeVRDriver::VRDriver::GetOpenVREvents() { } std::chrono::milliseconds SlimeVRDriver::VRDriver::GetLastFrameTime() { - return frame_timing_; + return frame_timing_; } bool SlimeVRDriver::VRDriver::AddDevice(std::shared_ptr device) { - vr::ETrackedDeviceClass openvr_device_class; - // Remember to update this switch when new device types are added - switch (device->GetDeviceType()) { - case DeviceType::CONTROLLER: - openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_Controller; - break; - case DeviceType::HMD: - openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_HMD; - break; - case DeviceType::TRACKER: - openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker; - break; - case DeviceType::TRACKING_REFERENCE: - openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference; - break; - default: - return false; + vr::ETrackedDeviceClass openvr_device_class; + // Remember to update this switch when new device types are added + switch (device->GetDeviceType()) { + case DeviceType::CONTROLLER: + openvr_device_class = + vr::ETrackedDeviceClass::TrackedDeviceClass_Controller; + break; + case DeviceType::HMD: + openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_HMD; + break; + case DeviceType::TRACKER: + openvr_device_class = + vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker; + break; + case DeviceType::TRACKING_REFERENCE: + openvr_device_class = + vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference; + break; + default: + return false; + } + if (!devices_by_serial_.count(device->GetSerial())) { + bool result = vr::VRServerDriverHost()->TrackedDeviceAdded( + device->GetSerial().c_str(), openvr_device_class, device.get()); + if (result) { + devices_.push_back(device); + devices_by_id_[device->GetDeviceId()] = device; + devices_by_serial_[device->GetSerial()] = device; + logger_->Log("New tracker device added {} (id {})", device->GetSerial(), + device->GetDeviceId()); + } else { + logger_->Log("Failed to add tracker device {} (id {})", + device->GetSerial(), device->GetDeviceId()); + return false; } - if (!devices_by_serial_.count(device->GetSerial())) { - bool result = vr::VRServerDriverHost()->TrackedDeviceAdded(device->GetSerial().c_str(), openvr_device_class, device.get()); - if (result) { - devices_.push_back(device); - devices_by_id_[device->GetDeviceId()] = device; - devices_by_serial_[device->GetSerial()] = device; - logger_->Log("New tracker device added {} (id {})", device->GetSerial(), device->GetDeviceId()); - } else { - logger_->Log("Failed to add tracker device {} (id {})", device->GetSerial(), device->GetDeviceId()); - return false; - } + } else { + std::shared_ptr oldDevice = + devices_by_serial_[device->GetSerial()]; + if (oldDevice->GetDeviceId() != device->GetDeviceId()) { + devices_by_id_[device->GetDeviceId()] = oldDevice; + oldDevice->SetDeviceId(device->GetDeviceId()); + logger_->Log("Device overridden from id {} to {} for serial {}", + oldDevice->GetDeviceId(), device->GetDeviceId(), + device->GetSerial()); } else { - std::shared_ptr oldDevice = devices_by_serial_[device->GetSerial()]; - if (oldDevice->GetDeviceId() != device->GetDeviceId()) { - devices_by_id_[device->GetDeviceId()] = oldDevice; - oldDevice->SetDeviceId(device->GetDeviceId()); - logger_->Log("Device overridden from id {} to {} for serial {}", oldDevice->GetDeviceId(), device->GetDeviceId(), device->GetSerial()); - } else { - logger_->Log("Device readded id {}, serial {}", device->GetDeviceId(), device->GetSerial()); - } + logger_->Log("Device readded id {}, serial {}", device->GetDeviceId(), + device->GetSerial()); } - return true; + } + return true; } -SlimeVRDriver::SettingsValue SlimeVRDriver::VRDriver::GetSettingsValue(std::string key) { - vr::EVRSettingsError err = vr::EVRSettingsError::VRSettingsError_None; - int int_value = vr::VRSettings()->GetInt32(settings_key_.c_str(), key.c_str(), &err); - if (err == vr::EVRSettingsError::VRSettingsError_None) { - return int_value; - } - err = vr::EVRSettingsError::VRSettingsError_None; - float float_value = vr::VRSettings()->GetFloat(settings_key_.c_str(), key.c_str(), &err); - if (err == vr::EVRSettingsError::VRSettingsError_None) { - return float_value; - } - err = vr::EVRSettingsError::VRSettingsError_None; - bool bool_value = vr::VRSettings()->GetBool(settings_key_.c_str(), key.c_str(), &err); - if (err == vr::EVRSettingsError::VRSettingsError_None) { - return bool_value; - } - std::string str_value; - str_value.reserve(1024); - vr::VRSettings()->GetString(settings_key_.c_str(), key.c_str(), str_value.data(), 1024, &err); - if (err == vr::EVRSettingsError::VRSettingsError_None) { - return str_value; - } - err = vr::EVRSettingsError::VRSettingsError_None; - - return SettingsValue(); +SlimeVRDriver::SettingsValue +SlimeVRDriver::VRDriver::GetSettingsValue(std::string key) { + vr::EVRSettingsError err = vr::EVRSettingsError::VRSettingsError_None; + int int_value = + vr::VRSettings()->GetInt32(settings_key_.c_str(), key.c_str(), &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return int_value; + } + err = vr::EVRSettingsError::VRSettingsError_None; + float float_value = + vr::VRSettings()->GetFloat(settings_key_.c_str(), key.c_str(), &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return float_value; + } + err = vr::EVRSettingsError::VRSettingsError_None; + bool bool_value = + vr::VRSettings()->GetBool(settings_key_.c_str(), key.c_str(), &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return bool_value; + } + std::string str_value; + str_value.reserve(1024); + vr::VRSettings()->GetString(settings_key_.c_str(), key.c_str(), + str_value.data(), 1024, &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return str_value; + } + err = vr::EVRSettingsError::VRSettingsError_None; + + return SettingsValue(); } -vr::IVRDriverInput* SlimeVRDriver::VRDriver::GetInput() { - return vr::VRDriverInput(); +vr::IVRDriverInput *SlimeVRDriver::VRDriver::GetInput() { + return vr::VRDriverInput(); } -vr::CVRPropertyHelpers* SlimeVRDriver::VRDriver::GetProperties() { - return vr::VRProperties(); +vr::CVRPropertyHelpers *SlimeVRDriver::VRDriver::GetProperties() { + return vr::VRProperties(); } -vr::IVRServerDriverHost* SlimeVRDriver::VRDriver::GetDriverHost() { - return vr::VRServerDriverHost(); +vr::IVRServerDriverHost *SlimeVRDriver::VRDriver::GetDriverHost() { + return vr::VRServerDriverHost(); } //----------------------------------------------------------------------------- // Purpose: Calculates quaternion (qw,qx,qy,qz) representing the rotation -// from: https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp +// from: +// https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp //----------------------------------------------------------------------------- -vr::HmdQuaternion_t SlimeVRDriver::VRDriver::GetRotation(vr::HmdMatrix34_t &matrix) { - vr::HmdQuaternion_t q; - - q.w = sqrt(fmax(0, 1 + matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2])) / 2; - q.x = sqrt(fmax(0, 1 + matrix.m[0][0] - matrix.m[1][1] - matrix.m[2][2])) / 2; - q.y = sqrt(fmax(0, 1 - matrix.m[0][0] + matrix.m[1][1] - matrix.m[2][2])) / 2; - q.z = sqrt(fmax(0, 1 - matrix.m[0][0] - matrix.m[1][1] + matrix.m[2][2])) / 2; - q.x = copysign(q.x, matrix.m[2][1] - matrix.m[1][2]); - q.y = copysign(q.y, matrix.m[0][2] - matrix.m[2][0]); - q.z = copysign(q.z, matrix.m[1][0] - matrix.m[0][1]); - return q; +vr::HmdQuaternion_t +SlimeVRDriver::VRDriver::GetRotation(vr::HmdMatrix34_t &matrix) { + vr::HmdQuaternion_t q; + + q.w = sqrt(fmax(0, 1 + matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2])) / 2; + q.x = sqrt(fmax(0, 1 + matrix.m[0][0] - matrix.m[1][1] - matrix.m[2][2])) / 2; + q.y = sqrt(fmax(0, 1 - matrix.m[0][0] + matrix.m[1][1] - matrix.m[2][2])) / 2; + q.z = sqrt(fmax(0, 1 - matrix.m[0][0] - matrix.m[1][1] + matrix.m[2][2])) / 2; + q.x = copysign(q.x, matrix.m[2][1] - matrix.m[1][2]); + q.y = copysign(q.y, matrix.m[0][2] - matrix.m[2][0]); + q.z = copysign(q.z, matrix.m[1][0] - matrix.m[0][1]); + return q; } //----------------------------------------------------------------------------- // Purpose: Extracts position (x,y,z). -// from: https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp +// from: +// https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp //----------------------------------------------------------------------------- -vr::HmdVector3_t SlimeVRDriver::VRDriver::GetPosition(vr::HmdMatrix34_t &matrix) { - vr::HmdVector3_t vector; +vr::HmdVector3_t +SlimeVRDriver::VRDriver::GetPosition(vr::HmdMatrix34_t &matrix) { + vr::HmdVector3_t vector; - vector.v[0] = matrix.m[0][3]; - vector.v[1] = matrix.m[1][3]; - vector.v[2] = matrix.m[2][3]; + vector.v[0] = matrix.m[0][3]; + vector.v[1] = matrix.m[1][3]; + vector.v[2] = matrix.m[2][3]; - return vector; + return vector; } -SlimeVRDriver::UniverseTranslation SlimeVRDriver::UniverseTranslation::parse(simdjson::ondemand::object &obj) { - SlimeVRDriver::UniverseTranslation res; - int iii = 0; - for (auto component: obj["translation"]) { - if (iii > 2) { - break; // TODO: 4 components in a translation vector? should this be an error? - } - res.translation.v[iii] = static_cast(component.get_double()); - iii += 1; +SlimeVRDriver::UniverseTranslation +SlimeVRDriver::UniverseTranslation::parse(simdjson::ondemand::object &obj) { + SlimeVRDriver::UniverseTranslation res; + int iii = 0; + for (auto component : obj["translation"]) { + if (iii > 2) { + break; // TODO: 4 components in a translation vector? should this be an + // error? } - res.yaw = static_cast(obj["yaw"].get_double()); + res.translation.v[iii] = static_cast(component.get_double()); + iii += 1; + } + res.yaw = static_cast(obj["yaw"].get_double()); - return res; + return res; } std::optional SlimeVRDriver::VRDriver::SearchUniverse(const simdjson::padded_string &json, uint64_t target) { @@ -425,8 +599,12 @@ std::optional SlimeVRDriver::VRDriver::Searc return SlimeVRDriver::UniverseTranslation::parse(standing_uni.value()); } } - + } catch (simdjson::simdjson_error &e) { + logger_->Log("Error getting universes from {}: {}", path, e.what()); return std::nullopt; + } + + return std::nullopt; } std::optional SlimeVRDriver::VRDriver::SearchUniverses(uint64_t target) { @@ -443,6 +621,7 @@ std::optional SlimeVRDriver::VRDriver::Searc logger_->Log("Error loading driver-provided chaperone JSON: {}", e.what()); } } + } auto driver_chap_path = vr::VRProperties()->GetStringProperty(hmd_prop_container, vr::Prop_DriverProvidedChaperonePath_String); if (driver_chap_path != "") { @@ -469,10 +648,111 @@ std::optional SlimeVRDriver::VRDriver::Searc return std::nullopt; } -std::optional SlimeVRDriver::VRDriver::GetCurrentUniverse() { - if (current_universe_.has_value()) { - return current_universe_.value().second; - } +std::optional +SlimeVRDriver::VRDriver::GetCurrentUniverse() { + if (current_universe_.has_value()) { + return current_universe_.value().second; + } - return std::nullopt; + return std::nullopt; +} + +std::optional +SlimeVRDriver::VRDriver::GetExternalPoseForHand(bool left_hand) { + if (left_hand && external_left_pose_.has_value()) { + return external_left_pose_; + } + if (!left_hand && external_right_pose_.has_value()) { + return external_right_pose_; + } + return std::nullopt; +} + +vr::DriverPose_t SlimeVRDriver::VRDriver::DriverPoseFromTrackedDevicePose( + const vr::TrackedDevicePose_t &raw) { + vr::DriverPose_t pose = + IVRDevice::MakeDefaultPose(raw.bDeviceIsConnected, raw.bPoseIsValid); + pose.poseTimeOffset = 0.0; + pose.result = raw.eTrackingResult; + + vr::HmdMatrix34_t m = raw.mDeviceToAbsoluteTracking; + pose.qRotation = GetRotation(m); + vr::HmdVector3_t pos = GetPosition(m); + pose.vecPosition[0] = pos.v[0]; + pose.vecPosition[1] = pos.v[1]; + pose.vecPosition[2] = pos.v[2]; + + pose.vecVelocity[0] = raw.vVelocity.v[0]; + pose.vecVelocity[1] = raw.vVelocity.v[1]; + pose.vecVelocity[2] = raw.vVelocity.v[2]; + pose.vecAngularVelocity[0] = raw.vAngularVelocity.v[0]; + pose.vecAngularVelocity[1] = raw.vAngularVelocity.v[1]; + pose.vecAngularVelocity[2] = raw.vAngularVelocity.v[2]; + + // External poses are already in tracking space; use identity + // world-from-driver + pose.qWorldFromDriverRotation.w = 1.0; + pose.qWorldFromDriverRotation.x = 0.0; + pose.qWorldFromDriverRotation.y = 0.0; + pose.qWorldFromDriverRotation.z = 0.0; + pose.vecWorldFromDriverTranslation[0] = 0.0; + pose.vecWorldFromDriverTranslation[1] = 0.0; + pose.vecWorldFromDriverTranslation[2] = 0.0; + pose.qDriverFromHeadRotation.w = 1.0; + pose.qDriverFromHeadRotation.x = 0.0; + pose.qDriverFromHeadRotation.y = 0.0; + pose.qDriverFromHeadRotation.z = 0.0; + pose.vecDriverFromHeadTranslation[0] = 0.0; + pose.vecDriverFromHeadTranslation[1] = 0.0; + pose.vecDriverFromHeadTranslation[2] = 0.0; + + return pose; +} + +void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { + vr::TrackedDevicePose_t raw_poses[vr::k_unMaxTrackedDeviceCount]; + vr::VRServerDriverHost()->GetRawTrackedDevicePoses( + 0.0f, raw_poses, vr::k_unMaxTrackedDeviceCount); + + std::unordered_set our_indices; + for (const auto &device : devices_) { + vr::TrackedDeviceIndex_t idx = device->GetDeviceIndex(); + if (idx != vr::k_unTrackedDeviceIndexInvalid) { + our_indices.insert(idx); + } + } + + external_left_pose_ = std::nullopt; + external_right_pose_ = std::nullopt; + + auto *props = GetProperties(); + for (uint32_t i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { + if (our_indices.count(i)) + continue; + const vr::TrackedDevicePose_t &p = raw_poses[i]; + if (!p.bDeviceIsConnected || !p.bPoseIsValid) + continue; + + vr::PropertyContainerHandle_t container = + props->TrackedDeviceToPropertyContainer(i); + vr::ETrackedPropertyError err = + vr::ETrackedPropertyError::TrackedProp_Success; + int32_t device_class = + props->GetInt32Property(container, vr::Prop_DeviceClass_Int32, &err); + if (err != vr::ETrackedPropertyError::TrackedProp_Success || + device_class != vr::TrackedDeviceClass_Controller) { + continue; + } + int32_t role = props->GetInt32Property( + container, vr::Prop_ControllerRoleHint_Int32, &err); + if (err != vr::ETrackedPropertyError::TrackedProp_Success) + continue; + + vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); + if (role == vr::TrackedControllerRole_LeftHand) { + external_left_pose_ = driver_pose; + } else if (role == vr::TrackedControllerRole_RightHand) { + external_right_pose_ = driver_pose; + } + } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index a0a6d82..bceaa50 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -1,19 +1,22 @@ #pragma once #define NOMINMAX -#include #include #include +#include + #include -#include #include +#include + #include -#include "bridge/BridgeClient.hpp" #include "Logger.hpp" +#include "bridge/BridgeClient.hpp" + namespace SlimeVRDriver { class VRDriver : public IVRDriver { @@ -25,53 +28,66 @@ namespace SlimeVRDriver { virtual bool AddDevice(std::shared_ptr device) override; virtual SettingsValue GetSettingsValue(std::string key) override; - virtual vr::IVRDriverInput* GetInput() override; - virtual vr::CVRPropertyHelpers* GetProperties() override; - virtual vr::IVRServerDriverHost* GetDriverHost() override; - - // Inherited via IServerTrackedDeviceProvider - virtual vr::EVRInitError Init(vr::IVRDriverContext* pDriverContext) override; - virtual void Cleanup() override; - virtual void RunFrame() override; - virtual bool ShouldBlockStandbyMode() override; - virtual void EnterStandby() override; - virtual void LeaveStandby() override; - virtual ~VRDriver() = default; - - virtual std::optional GetCurrentUniverse() override; - - void OnBridgeMessage(const messages::ProtobufMessage& message); - void RunPoseRequestThread(); - - private: - std::unique_ptr pose_request_thread_ = nullptr; - std::atomic exiting_pose_request_thread_ = false; - - std::shared_ptr bridge_ = nullptr; - google::protobuf::Arena arena_; - std::shared_ptr logger_ = std::make_shared(); - std::mutex devices_mutex_; - std::vector> devices_; - std::vector openvr_events_; - std::map> devices_by_id_; - std::map> devices_by_serial_; - std::chrono::milliseconds frame_timing_ = std::chrono::milliseconds(16); - std::chrono::steady_clock::time_point last_frame_time_ = std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point battery_sent_at_ = std::chrono::steady_clock::now(); - std::string settings_key_ = "driver_slimevr"; - - vr::HmdQuaternion_t GetRotation(vr::HmdMatrix34_t &matrix); - vr::HmdVector3_t GetPosition(vr::HmdMatrix34_t &matrix); - - bool sent_hmd_add_message_ = false; - - simdjson::ondemand::parser json_parser_; - std::optional default_chap_path_ = std::nullopt; - //std::map universes; - - vr::ETrackedPropertyError last_universe_error_; - std::optional> current_universe_ = std::nullopt; - std::optional SearchUniverse(const simdjson::padded_string &json, uint64_t target); - std::optional SearchUniverses(uint64_t target); - }; -}; \ No newline at end of file + virtual vr::IVRDriverInput *GetInput() override; + virtual vr::CVRPropertyHelpers *GetProperties() override; + virtual vr::IVRServerDriverHost *GetDriverHost() override; + + // Inherited via IServerTrackedDeviceProvider + virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext) override; + virtual void Cleanup() override; + virtual void RunFrame() override; + virtual bool ShouldBlockStandbyMode() override; + virtual void EnterStandby() override; + virtual void LeaveStandby() override; + virtual ~VRDriver() = default; + + virtual std::optional GetCurrentUniverse() override; + + virtual std::optional + GetExternalPoseForHand(bool left_hand) override; + + void OnBridgeMessage(const messages::ProtobufMessage &message); + void RunPoseRequestThread(); + +private: + std::unique_ptr pose_request_thread_ = nullptr; + std::atomic exiting_pose_request_thread_ = false; + + std::shared_ptr bridge_ = nullptr; + google::protobuf::Arena arena_; + std::shared_ptr logger_ = std::make_shared(); + std::mutex devices_mutex_; + std::vector> devices_; + std::vector openvr_events_; + std::map> devices_by_id_; + std::map> devices_by_serial_; + std::chrono::milliseconds frame_timing_ = std::chrono::milliseconds(16); + std::chrono::steady_clock::time_point last_frame_time_ = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point battery_sent_at_ = + std::chrono::steady_clock::now(); + std::string settings_key_ = "driver_slimevr"; + + vr::HmdQuaternion_t GetRotation(vr::HmdMatrix34_t &matrix); + vr::HmdVector3_t GetPosition(vr::HmdMatrix34_t &matrix); + + bool sent_hmd_add_message_ = false; + + simdjson::ondemand::parser json_parser_; + std::optional default_chap_path_ = std::nullopt; + // std::map universes; + + vr::ETrackedPropertyError last_universe_error_; + std::optional> current_universe_ = + std::nullopt; + std::optional SearchUniverse(std::string path, + uint64_t target); + std::optional SearchUniverses(uint64_t target); + + std::optional external_left_pose_; + std::optional external_right_pose_; + void UpdateExternalControllerPoses(); + static vr::DriverPose_t + DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); +}; +}; // namespace SlimeVRDriver From d7e594c0db79177cd224be013392efb280f85215 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Thu, 19 Feb 2026 20:29:02 -0600 Subject: [PATCH 091/117] Add static keywords. --- src/VRDriver.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index bceaa50..2aa01fd 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -5,19 +5,16 @@ #include #include - #include #include #include - #include #include "Logger.hpp" #include "bridge/BridgeClient.hpp" - namespace SlimeVRDriver { class VRDriver : public IVRDriver { public: @@ -68,8 +65,8 @@ namespace SlimeVRDriver { std::chrono::steady_clock::now(); std::string settings_key_ = "driver_slimevr"; - vr::HmdQuaternion_t GetRotation(vr::HmdMatrix34_t &matrix); - vr::HmdVector3_t GetPosition(vr::HmdMatrix34_t &matrix); + static vr::HmdQuaternion_t GetRotation(vr::HmdMatrix34_t &matrix); + static vr::HmdVector3_t GetPosition(vr::HmdMatrix34_t &matrix); bool sent_hmd_add_message_ = false; From 9e978b17312d1d48f21365933c1ff6f7f4ed7ed6 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 02:06:32 -0600 Subject: [PATCH 092/117] Add check before swapping pose data. --- src/TrackerDevice.cpp | 25 ++++++++++++++++++++----- src/TrackerDevice.hpp | 9 +++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 2592a96..6d95351 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -121,14 +121,29 @@ void SlimeVRDriver::TrackerDevice::Update() { system_click_value, 0); } - // For controllers: prefer external hand pose (e.g. Virtual Desktop / Steam - // Link on Quest) when in view; when they disconnect or go out of view, use - // SlimeVR positional data. Smoothly blend when swapping between sources. + /* For controllers : prefer external hand pose(e.g.Virtual Desktop / Steam + Link on Quest) when in view; when they disconnect or go out of view, use + SlimeVR positional data. Smoothly blend when swapping between sources. + Hysteresis avoids rapid swapping when external pose validity flickers.*/ if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); - bool want_external = external.has_value() && external->poseIsValid; + bool external_valid = external.has_value() && external->poseIsValid; + if (external_valid) { + pose_source_frames_external_valid_++; + pose_source_frames_external_invalid_ = 0; + } else { + pose_source_frames_external_invalid_++; + pose_source_frames_external_valid_ = 0; + } + // Only switch to external after N consecutive valid frames; only switch + // away after N consecutive invalid frames. + bool want_external = + (using_external_pose_ && + pose_source_frames_external_invalid_ < kPoseSourceFramesToSwitch) || + (pose_source_frames_external_valid_ >= kPoseSourceFramesToSwitch); vr::DriverPose_t slimevr_pose = last_pose_atomic_.load(); - vr::DriverPose_t target_pose = want_external ? *external : slimevr_pose; + vr::DriverPose_t target_pose = + want_external && external_valid ? *external : slimevr_pose; auto now = std::chrono::steady_clock::now(); vr::DriverPose_t pose_to_use; diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 4bed984..81e5336 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -4,13 +4,11 @@ #include #include - #include #include #include - #include #include #include @@ -18,11 +16,9 @@ #include #include - #include "Logger.hpp" #include "TrackerRole.hpp" - namespace SlimeVRDriver { class TrackerDevice : public IVRDevice { public: @@ -127,6 +123,11 @@ class TrackerDevice : public IVRDevice { static constexpr std::chrono::milliseconds pose_blend_duration_{200}; static vr::DriverPose_t BlendPoses(const vr::DriverPose_t &from, const vr::DriverPose_t &to, float t); + // Hysteresis: require consecutive frames before switching source to avoid + // rapid flicker when external pose validity is unstable + int pose_source_frames_external_valid_ = 0; + int pose_source_frames_external_invalid_ = 0; + static constexpr int kPoseSourceFramesToSwitch = 3; vr::VRInputComponentHandle_t skeletal_component_handle_; From b668aaa43c20c05c37f54e5564e15c687d6af542 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 03:03:22 -0600 Subject: [PATCH 093/117] Adjust blending logic between SlimeVR to VD Controllers. --- src/TrackerDevice.cpp | 6 +++++- src/TrackerDevice.hpp | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 6d95351..5bb0410 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -158,7 +158,9 @@ void SlimeVRDriver::TrackerDevice::Update() { float t = elapsed_ms / static_cast(pose_blend_duration_.count()); if (t >= 1.0f) { pose_blending_ = false; - using_external_pose_ = want_external; + // Use the blend target we committed to at blend start, not current + // want_external (which may have flickered during the blend) + using_external_pose_ = blending_to_external_; pose_to_use = pose_blend_to_; } else { // Smooth step for easier-in-out @@ -168,6 +170,7 @@ void SlimeVRDriver::TrackerDevice::Update() { } else { if (want_external != using_external_pose_) { pose_blending_ = true; + blending_to_external_ = want_external; pose_blend_start_ = now; pose_blend_from_ = last_output_pose_; pose_blend_to_ = target_pose; @@ -175,6 +178,7 @@ void SlimeVRDriver::TrackerDevice::Update() { pose_to_use = BlendPoses(pose_blend_from_, pose_blend_to_, s); } else { pose_to_use = target_pose; + // Only sync when not blending; don't overwrite every frame using_external_pose_ = want_external; } } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 81e5336..da7e1af 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -116,6 +116,8 @@ class TrackerDevice : public IVRDevice { // SlimeVR pose bool using_external_pose_ = false; bool pose_blending_ = false; + bool blending_to_external_ = + false; // target of current blend; used at blend end std::chrono::steady_clock::time_point pose_blend_start_; vr::DriverPose_t pose_blend_from_; vr::DriverPose_t pose_blend_to_; From a59eb60477f32b9cccf9a8187e1a7805b065515e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 03:49:35 -0600 Subject: [PATCH 094/117] Remove smoothing transition logic. --- src/TrackerDevice.cpp | 107 ++---------------------------------------- src/TrackerDevice.hpp | 16 +------ 2 files changed, 7 insertions(+), 116 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 5bb0410..fc89fe9 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -121,10 +121,9 @@ void SlimeVRDriver::TrackerDevice::Update() { system_click_value, 0); } - /* For controllers : prefer external hand pose(e.g.Virtual Desktop / Steam - Link on Quest) when in view; when they disconnect or go out of view, use - SlimeVR positional data. Smoothly blend when swapping between sources. - Hysteresis avoids rapid swapping when external pose validity flickers.*/ + // For controllers: prefer external (VD/Steam Link) when in view; use + // SlimeVR when they disconnect or go out of view. Hysteresis avoids rapid + // source switching. if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); bool external_valid = external.has_value() && external->poseIsValid; @@ -135,55 +134,15 @@ void SlimeVRDriver::TrackerDevice::Update() { pose_source_frames_external_invalid_++; pose_source_frames_external_valid_ = 0; } - // Only switch to external after N consecutive valid frames; only switch - // away after N consecutive invalid frames. bool want_external = (using_external_pose_ && pose_source_frames_external_invalid_ < kPoseSourceFramesToSwitch) || (pose_source_frames_external_valid_ >= kPoseSourceFramesToSwitch); vr::DriverPose_t slimevr_pose = last_pose_atomic_.load(); - vr::DriverPose_t target_pose = + vr::DriverPose_t pose_to_use = want_external && external_valid ? *external : slimevr_pose; - auto now = std::chrono::steady_clock::now(); - vr::DriverPose_t pose_to_use; - - if (pose_blending_) { - float elapsed_ms = - static_cast( - std::chrono::duration_cast( - now - pose_blend_start_) - .count()) / - 1000.f; - float t = elapsed_ms / static_cast(pose_blend_duration_.count()); - if (t >= 1.0f) { - pose_blending_ = false; - // Use the blend target we committed to at blend start, not current - // want_external (which may have flickered during the blend) - using_external_pose_ = blending_to_external_; - pose_to_use = pose_blend_to_; - } else { - // Smooth step for easier-in-out - float s = t * t * (3.0f - 2.0f * t); - pose_to_use = BlendPoses(pose_blend_from_, pose_blend_to_, s); - } - } else { - if (want_external != using_external_pose_) { - pose_blending_ = true; - blending_to_external_ = want_external; - pose_blend_start_ = now; - pose_blend_from_ = last_output_pose_; - pose_blend_to_ = target_pose; - float s = 0.0f; - pose_to_use = BlendPoses(pose_blend_from_, pose_blend_to_, s); - } else { - pose_to_use = target_pose; - // Only sync when not blending; don't overwrite every frame - using_external_pose_ = want_external; - } - } - - last_output_pose_ = pose_to_use; + using_external_pose_ = want_external; GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( device_index_, pose_to_use, sizeof(vr::DriverPose_t)); } @@ -760,62 +719,6 @@ void SlimeVRDriver::TrackerDevice::DebugRequest(const char *pchRequest, } } -vr::DriverPose_t -SlimeVRDriver::TrackerDevice::BlendPoses(const vr::DriverPose_t &from, - const vr::DriverPose_t &to, float t) { - vr::DriverPose_t out = - to; // copy world-from-driver, validity, etc. from target - - out.vecPosition[0] = - from.vecPosition[0] + t * (to.vecPosition[0] - from.vecPosition[0]); - out.vecPosition[1] = - from.vecPosition[1] + t * (to.vecPosition[1] - from.vecPosition[1]); - out.vecPosition[2] = - from.vecPosition[2] + t * (to.vecPosition[2] - from.vecPosition[2]); - - double dot = - from.qRotation.w * to.qRotation.w + from.qRotation.x * to.qRotation.x + - from.qRotation.y * to.qRotation.y + from.qRotation.z * to.qRotation.z; - double to_w = to.qRotation.w, to_x = to.qRotation.x, to_y = to.qRotation.y, - to_z = to.qRotation.z; - if (dot < 0.0) { - to_w = -to_w; - to_x = -to_x; - to_y = -to_y; - to_z = -to_z; - dot = -dot; - } - dot = (dot > 1.0) ? 1.0 : (dot < -1.0 ? -1.0 : dot); - double theta = std::acos(dot); - double sin_theta = std::sin(theta); - if (sin_theta > 1e-6) { - double a = std::sin((1.0 - static_cast(t)) * theta) / sin_theta; - double b = std::sin(static_cast(t) * theta) / sin_theta; - out.qRotation.w = static_cast(a * from.qRotation.w + b * to_w); - out.qRotation.x = static_cast(a * from.qRotation.x + b * to_x); - out.qRotation.y = static_cast(a * from.qRotation.y + b * to_y); - out.qRotation.z = static_cast(a * from.qRotation.z + b * to_z); - } - - out.vecVelocity[0] = - from.vecVelocity[0] + t * (to.vecVelocity[0] - from.vecVelocity[0]); - out.vecVelocity[1] = - from.vecVelocity[1] + t * (to.vecVelocity[1] - from.vecVelocity[1]); - out.vecVelocity[2] = - from.vecVelocity[2] + t * (to.vecVelocity[2] - from.vecVelocity[2]); - out.vecAngularVelocity[0] = - from.vecAngularVelocity[0] + - t * (to.vecAngularVelocity[0] - from.vecAngularVelocity[0]); - out.vecAngularVelocity[1] = - from.vecAngularVelocity[1] + - t * (to.vecAngularVelocity[1] - from.vecAngularVelocity[1]); - out.vecAngularVelocity[2] = - from.vecAngularVelocity[2] + - t * (to.vecAngularVelocity[2] - from.vecAngularVelocity[2]); - - return out; -} - vr::DriverPose_t SlimeVRDriver::TrackerDevice::GetPose() { return last_pose_atomic_; } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index da7e1af..43e6361 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -112,21 +112,9 @@ class TrackerDevice : public IVRDevice { bool is_left_hand_; bool is_right_hand_; - // Smooth transition when swapping between external (VD/Steam Link) and - // SlimeVR pose + // Prefer external (VD/Steam Link) when in view, SlimeVR when not. + // Hysteresis: require consecutive frames before switching source. bool using_external_pose_ = false; - bool pose_blending_ = false; - bool blending_to_external_ = - false; // target of current blend; used at blend end - std::chrono::steady_clock::time_point pose_blend_start_; - vr::DriverPose_t pose_blend_from_; - vr::DriverPose_t pose_blend_to_; - vr::DriverPose_t last_output_pose_ = IVRDevice::MakeDefaultPose(); - static constexpr std::chrono::milliseconds pose_blend_duration_{200}; - static vr::DriverPose_t BlendPoses(const vr::DriverPose_t &from, - const vr::DriverPose_t &to, float t); - // Hysteresis: require consecutive frames before switching source to avoid - // rapid flicker when external pose validity is unstable int pose_source_frames_external_valid_ = 0; int pose_source_frames_external_invalid_ = 0; static constexpr int kPoseSourceFramesToSwitch = 3; From c127780ab0c0f26122d8974030ea960648f83699 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 04:39:50 -0600 Subject: [PATCH 095/117] Make algorithm for swapping between SlimeVR pose and virtual desktop provided pose less aggressive. --- src/TrackerDevice.hpp | 5 +++-- src/VRDriver.cpp | 24 +++++++++++++++++++++--- src/VRDriver.hpp | 3 +++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 43e6361..492e35b 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -113,11 +113,12 @@ class TrackerDevice : public IVRDevice { bool is_right_hand_; // Prefer external (VD/Steam Link) when in view, SlimeVR when not. - // Hysteresis: require consecutive frames before switching source. + // Hysteresis: require many consecutive frames before switching source to + // avoid flicker from timing or single-frame dropouts. bool using_external_pose_ = false; int pose_source_frames_external_valid_ = 0; int pose_source_frames_external_invalid_ = 0; - static constexpr int kPoseSourceFramesToSwitch = 3; + static constexpr int kPoseSourceFramesToSwitch = 12; vr::VRInputComponentHandle_t skeletal_component_handle_; diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 5269ad2..8576efc 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -6,7 +6,6 @@ #include #include - vr::EVRInitError SlimeVRDriver::VRDriver::Init(vr::IVRDriverContext *pDriverContext) { // Perform driver context initialisation @@ -722,8 +721,8 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } } - external_left_pose_ = std::nullopt; - external_right_pose_ = std::nullopt; + bool found_left = false; + bool found_right = false; auto *props = GetProperties(); for (uint32_t i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { @@ -751,8 +750,27 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); if (role == vr::TrackedControllerRole_LeftHand) { external_left_pose_ = driver_pose; + external_left_frames_missing_ = 0; + found_left = true; } else if (role == vr::TrackedControllerRole_RightHand) { external_right_pose_ = driver_pose; + external_right_frames_missing_ = 0; + found_right = true; + } + } + + // Grace period: keep returning last valid pose for N frames when we don't + // see one (smooths timing/ordering dropouts). + if (!found_left) { + external_left_frames_missing_++; + if (external_left_frames_missing_ > kExternalPoseGraceFrames) { + external_left_pose_ = std::nullopt; + } + } + if (!found_right) { + external_right_frames_missing_++; + if (external_right_frames_missing_ > kExternalPoseGraceFrames) { + external_right_pose_ = std::nullopt; } } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 2aa01fd..195f9fd 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -83,6 +83,9 @@ namespace SlimeVRDriver { std::optional external_left_pose_; std::optional external_right_pose_; + int external_left_frames_missing_ = 0; + int external_right_frames_missing_ = 0; + static constexpr int kExternalPoseGraceFrames = 15; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); From f55c1770335484c592fa15959a9eed1244f8572e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 05:34:38 -0600 Subject: [PATCH 096/117] Make sure we arent accidentally grabbing ourselves in pose detection. --- src/VRDriver.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 8576efc..2470478 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -2,6 +2,7 @@ #include "TrackerRole.hpp" #include "VRPaths_openvr.hpp" #include +#include #include #include #include @@ -747,6 +748,16 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { if (err != vr::ETrackedPropertyError::TrackedProp_Success) continue; + // Never use our own driver's controllers as "external" (avoids mis-identify + // and flicker if index-based exclusion ever misses). + char manufacturer[256] = {}; + props->GetStringProperty(container, vr::Prop_ManufacturerName_String, + manufacturer, sizeof(manufacturer), &err); + if (err == vr::ETrackedPropertyError::TrackedProp_Success && + std::strcmp(manufacturer, "SlimeVR") == 0) { + continue; + } + vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); if (role == vr::TrackedControllerRole_LeftHand) { external_left_pose_ = driver_pose; From eaba28bd8da1a0fa6921746d014cd5b52504de89 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 06:30:49 -0600 Subject: [PATCH 097/117] Remove logic for attempting to prevent flicker, as it doesnt appear to provide any net benefit. --- src/TrackerDevice.cpp | 19 ++----------------- src/TrackerDevice.hpp | 8 -------- src/VRDriver.cpp | 26 +++----------------------- src/VRDriver.hpp | 3 --- 4 files changed, 5 insertions(+), 51 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index fc89fe9..8645576 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -122,27 +122,12 @@ void SlimeVRDriver::TrackerDevice::Update() { } // For controllers: prefer external (VD/Steam Link) when in view; use - // SlimeVR when they disconnect or go out of view. Hysteresis avoids rapid - // source switching. + // SlimeVR when they disconnect or go out of view. if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); bool external_valid = external.has_value() && external->poseIsValid; - if (external_valid) { - pose_source_frames_external_valid_++; - pose_source_frames_external_invalid_ = 0; - } else { - pose_source_frames_external_invalid_++; - pose_source_frames_external_valid_ = 0; - } - bool want_external = - (using_external_pose_ && - pose_source_frames_external_invalid_ < kPoseSourceFramesToSwitch) || - (pose_source_frames_external_valid_ >= kPoseSourceFramesToSwitch); vr::DriverPose_t slimevr_pose = last_pose_atomic_.load(); - vr::DriverPose_t pose_to_use = - want_external && external_valid ? *external : slimevr_pose; - - using_external_pose_ = want_external; + vr::DriverPose_t pose_to_use = external_valid ? *external : slimevr_pose; GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( device_index_, pose_to_use, sizeof(vr::DriverPose_t)); } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 492e35b..ff09545 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -112,14 +112,6 @@ class TrackerDevice : public IVRDevice { bool is_left_hand_; bool is_right_hand_; - // Prefer external (VD/Steam Link) when in view, SlimeVR when not. - // Hysteresis: require many consecutive frames before switching source to - // avoid flicker from timing or single-frame dropouts. - bool using_external_pose_ = false; - int pose_source_frames_external_valid_ = 0; - int pose_source_frames_external_invalid_ = 0; - static constexpr int kPoseSourceFramesToSwitch = 12; - vr::VRInputComponentHandle_t skeletal_component_handle_; float trigger_value_ = 0; diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 2470478..214c45d 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -722,8 +722,8 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } } - bool found_left = false; - bool found_right = false; + external_left_pose_ = std::nullopt; + external_right_pose_ = std::nullopt; auto *props = GetProperties(); for (uint32_t i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { @@ -748,8 +748,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { if (err != vr::ETrackedPropertyError::TrackedProp_Success) continue; - // Never use our own driver's controllers as "external" (avoids mis-identify - // and flicker if index-based exclusion ever misses). + // Never use our own driver's controllers as "external". char manufacturer[256] = {}; props->GetStringProperty(container, vr::Prop_ManufacturerName_String, manufacturer, sizeof(manufacturer), &err); @@ -761,27 +760,8 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); if (role == vr::TrackedControllerRole_LeftHand) { external_left_pose_ = driver_pose; - external_left_frames_missing_ = 0; - found_left = true; } else if (role == vr::TrackedControllerRole_RightHand) { external_right_pose_ = driver_pose; - external_right_frames_missing_ = 0; - found_right = true; - } - } - - // Grace period: keep returning last valid pose for N frames when we don't - // see one (smooths timing/ordering dropouts). - if (!found_left) { - external_left_frames_missing_++; - if (external_left_frames_missing_ > kExternalPoseGraceFrames) { - external_left_pose_ = std::nullopt; - } - } - if (!found_right) { - external_right_frames_missing_++; - if (external_right_frames_missing_ > kExternalPoseGraceFrames) { - external_right_pose_ = std::nullopt; } } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 195f9fd..2aa01fd 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -83,9 +83,6 @@ namespace SlimeVRDriver { std::optional external_left_pose_; std::optional external_right_pose_; - int external_left_frames_missing_ = 0; - int external_right_frames_missing_ = 0; - static constexpr int kExternalPoseGraceFrames = 15; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); From 161709120987f9129c0e1955e8342fabc20c198c Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 06:42:39 -0600 Subject: [PATCH 098/117] Use hemisphere in front of the user to calculate whether we should swap to SlimeVR skeleton vs Virtual Desktop tracking. --- src/VRDriver.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 214c45d..1d55000 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -714,6 +714,23 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::VRServerDriverHost()->GetRawTrackedDevicePoses( 0.0f, raw_poses, vr::k_unMaxTrackedDeviceCount); + // HMD pose for hemisphere check: use external only when hand is in front + // (within the half-space in front of the user); behind = use SlimeVR. + float hmd_pos[3] = {0.f, 0.f, 0.f}; + float hmd_forward[3] = {0.f, 0.f, -1.f}; // default forward -Z + const vr::TrackedDevicePose_t &hmd_pose = + raw_poses[vr::k_unTrackedDeviceIndex_Hmd]; + if (hmd_pose.bPoseIsValid && hmd_pose.bDeviceIsConnected) { + const vr::HmdMatrix34_t &m = hmd_pose.mDeviceToAbsoluteTracking; + hmd_pos[0] = m.m[0][3]; + hmd_pos[1] = m.m[1][3]; + hmd_pos[2] = m.m[2][3]; + // OpenVR: -Z is forward in tracking space + hmd_forward[0] = -m.m[0][2]; + hmd_forward[1] = -m.m[1][2]; + hmd_forward[2] = -m.m[2][2]; + } + std::unordered_set our_indices; for (const auto &device : devices_) { vr::TrackedDeviceIndex_t idx = device->GetDeviceIndex(); @@ -758,6 +775,18 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); + // Only use external pose when hand is in the front hemisphere (in front + // of the user); once it goes behind, we fall back to SlimeVR. + float to_hand[3] = { + static_cast(driver_pose.vecPosition[0]) - hmd_pos[0], + static_cast(driver_pose.vecPosition[1]) - hmd_pos[1], + static_cast(driver_pose.vecPosition[2]) - hmd_pos[2], + }; + float dot = to_hand[0] * hmd_forward[0] + to_hand[1] * hmd_forward[1] + + to_hand[2] * hmd_forward[2]; + if (dot < 0.f) + continue; // behind user: don't use external, SlimeVR will be used + if (role == vr::TrackedControllerRole_LeftHand) { external_left_pose_ = driver_pose; } else if (role == vr::TrackedControllerRole_RightHand) { From 531f5e5ffccdb90e96494dc585dc49000f79b25e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 17:58:47 -0600 Subject: [PATCH 099/117] Adjust pose swapping calculations --- src/TrackerDevice.cpp | 4 ++-- src/VRDriver.cpp | 37 ++++++++++++++++++++++++++++--------- src/VRDriver.hpp | 3 +++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 8645576..46826d8 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -125,9 +125,9 @@ void SlimeVRDriver::TrackerDevice::Update() { // SlimeVR when they disconnect or go out of view. if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); - bool external_valid = external.has_value() && external->poseIsValid; vr::DriverPose_t slimevr_pose = last_pose_atomic_.load(); - vr::DriverPose_t pose_to_use = external_valid ? *external : slimevr_pose; + vr::DriverPose_t pose_to_use = + external.has_value() ? *external : slimevr_pose; GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( device_index_, pose_to_use, sizeof(vr::DriverPose_t)); } diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 1d55000..4e783ae 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -716,6 +716,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { // HMD pose for hemisphere check: use external only when hand is in front // (within the half-space in front of the user); behind = use SlimeVR. + // OpenVR: right-handed, +Y up, +X right, -Z forward. float hmd_pos[3] = {0.f, 0.f, 0.f}; float hmd_forward[3] = {0.f, 0.f, -1.f}; // default forward -Z const vr::TrackedDevicePose_t &hmd_pose = @@ -725,11 +726,21 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { hmd_pos[0] = m.m[0][3]; hmd_pos[1] = m.m[1][3]; hmd_pos[2] = m.m[2][3]; - // OpenVR: -Z is forward in tracking space + // HMD -Z axis in world = direction user is facing hmd_forward[0] = -m.m[0][2]; hmd_forward[1] = -m.m[1][2]; hmd_forward[2] = -m.m[2][2]; } + // Use external only when hand is clearly in front (in headset sensor range). + // Swap to SlimeVR sooner: when hand is at the edge or behind we have no + // headset data anyway, so we must switch before that. SlimeVR covers the + // range in front that's out of sensor view and everything behind. + // Leniency: dead zone between the two thresholds to avoid flicker. + const float kDotUseExternal = + 0.45f; // dot > this: clearly in view, use external + const float kDotUseSlimeVR = + 0.35f; // dot < this: edge or behind, use SlimeVR (swap sooner) + // 0.35 <= dot <= 0.45: keep previous (last_used_external_*) std::unordered_set our_indices; for (const auto &device : devices_) { @@ -747,8 +758,9 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { if (our_indices.count(i)) continue; const vr::TrackedDevicePose_t &p = raw_poses[i]; - if (!p.bDeviceIsConnected || !p.bPoseIsValid) - continue; + // Don't require bPoseIsValid/bDeviceIsConnected here; use the device when + // it matches role/hemisphere so we don't flicker on single-frame validity + // drops. vr::PropertyContainerHandle_t container = props->TrackedDeviceToPropertyContainer(i); @@ -775,8 +787,6 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); - // Only use external pose when hand is in the front hemisphere (in front - // of the user); once it goes behind, we fall back to SlimeVR. float to_hand[3] = { static_cast(driver_pose.vecPosition[0]) - hmd_pos[0], static_cast(driver_pose.vecPosition[1]) - hmd_pos[1], @@ -784,13 +794,22 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { }; float dot = to_hand[0] * hmd_forward[0] + to_hand[1] * hmd_forward[1] + to_hand[2] * hmd_forward[2]; - if (dot < 0.f) - continue; // behind user: don't use external, SlimeVR will be used + bool use_external; if (role == vr::TrackedControllerRole_LeftHand) { - external_left_pose_ = driver_pose; + use_external = (dot > kDotUseExternal) ? true + : (dot < kDotUseSlimeVR) ? false + : last_used_external_left_; + last_used_external_left_ = use_external; + if (use_external) + external_left_pose_ = driver_pose; } else if (role == vr::TrackedControllerRole_RightHand) { - external_right_pose_ = driver_pose; + use_external = (dot > kDotUseExternal) ? true + : (dot < kDotUseSlimeVR) ? false + : last_used_external_right_; + last_used_external_right_ = use_external; + if (use_external) + external_right_pose_ = driver_pose; } } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 2aa01fd..ef62287 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -83,6 +83,9 @@ namespace SlimeVRDriver { std::optional external_left_pose_; std::optional external_right_pose_; + bool last_used_external_left_ = + true; // leniency at edge: keep choice in dead zone + bool last_used_external_right_ = true; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); From 781d42b5450e6ad43de6392f7a1d620ddc53a502 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 19:15:29 -0600 Subject: [PATCH 100/117] Temporarily remove pose swapping. --- src/VRDriver.cpp | 54 ++---------------------------------------------- src/VRDriver.hpp | 3 --- 2 files changed, 2 insertions(+), 55 deletions(-) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 4e783ae..31fcd26 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -714,34 +714,6 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::VRServerDriverHost()->GetRawTrackedDevicePoses( 0.0f, raw_poses, vr::k_unMaxTrackedDeviceCount); - // HMD pose for hemisphere check: use external only when hand is in front - // (within the half-space in front of the user); behind = use SlimeVR. - // OpenVR: right-handed, +Y up, +X right, -Z forward. - float hmd_pos[3] = {0.f, 0.f, 0.f}; - float hmd_forward[3] = {0.f, 0.f, -1.f}; // default forward -Z - const vr::TrackedDevicePose_t &hmd_pose = - raw_poses[vr::k_unTrackedDeviceIndex_Hmd]; - if (hmd_pose.bPoseIsValid && hmd_pose.bDeviceIsConnected) { - const vr::HmdMatrix34_t &m = hmd_pose.mDeviceToAbsoluteTracking; - hmd_pos[0] = m.m[0][3]; - hmd_pos[1] = m.m[1][3]; - hmd_pos[2] = m.m[2][3]; - // HMD -Z axis in world = direction user is facing - hmd_forward[0] = -m.m[0][2]; - hmd_forward[1] = -m.m[1][2]; - hmd_forward[2] = -m.m[2][2]; - } - // Use external only when hand is clearly in front (in headset sensor range). - // Swap to SlimeVR sooner: when hand is at the edge or behind we have no - // headset data anyway, so we must switch before that. SlimeVR covers the - // range in front that's out of sensor view and everything behind. - // Leniency: dead zone between the two thresholds to avoid flicker. - const float kDotUseExternal = - 0.45f; // dot > this: clearly in view, use external - const float kDotUseSlimeVR = - 0.35f; // dot < this: edge or behind, use SlimeVR (swap sooner) - // 0.35 <= dot <= 0.45: keep previous (last_used_external_*) - std::unordered_set our_indices; for (const auto &device : devices_) { vr::TrackedDeviceIndex_t idx = device->GetDeviceIndex(); @@ -758,9 +730,6 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { if (our_indices.count(i)) continue; const vr::TrackedDevicePose_t &p = raw_poses[i]; - // Don't require bPoseIsValid/bDeviceIsConnected here; use the device when - // it matches role/hemisphere so we don't flicker on single-frame validity - // drops. vr::PropertyContainerHandle_t container = props->TrackedDeviceToPropertyContainer(i); @@ -787,29 +756,10 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); - float to_hand[3] = { - static_cast(driver_pose.vecPosition[0]) - hmd_pos[0], - static_cast(driver_pose.vecPosition[1]) - hmd_pos[1], - static_cast(driver_pose.vecPosition[2]) - hmd_pos[2], - }; - float dot = to_hand[0] * hmd_forward[0] + to_hand[1] * hmd_forward[1] + - to_hand[2] * hmd_forward[2]; - - bool use_external; if (role == vr::TrackedControllerRole_LeftHand) { - use_external = (dot > kDotUseExternal) ? true - : (dot < kDotUseSlimeVR) ? false - : last_used_external_left_; - last_used_external_left_ = use_external; - if (use_external) - external_left_pose_ = driver_pose; + external_left_pose_ = driver_pose; } else if (role == vr::TrackedControllerRole_RightHand) { - use_external = (dot > kDotUseExternal) ? true - : (dot < kDotUseSlimeVR) ? false - : last_used_external_right_; - last_used_external_right_ = use_external; - if (use_external) - external_right_pose_ = driver_pose; + external_right_pose_ = driver_pose; } } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index ef62287..2aa01fd 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -83,9 +83,6 @@ namespace SlimeVRDriver { std::optional external_left_pose_; std::optional external_right_pose_; - bool last_used_external_left_ = - true; // leniency at edge: keep choice in dead zone - bool last_used_external_right_ = true; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); From 7de9f981ad8d66b7fa04236a1ca110df28b4dee5 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 20:42:26 -0600 Subject: [PATCH 101/117] Ensure only one spot is setting position. --- src/TrackerDevice.cpp | 22 +++++++++------------- src/VRDriver.cpp | 3 --- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 46826d8..2b95b44 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -121,16 +121,16 @@ void SlimeVRDriver::TrackerDevice::Update() { system_click_value, 0); } - // For controllers: prefer external (VD/Steam Link) when in view; use - // SlimeVR when they disconnect or go out of view. + // Single place the pose is finally set: controllers use external (VD/Steam + // Link) when available else SlimeVR; trackers use last SlimeVR pose. + vr::DriverPose_t pose_to_use = last_pose_atomic_.load(); if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); - vr::DriverPose_t slimevr_pose = last_pose_atomic_.load(); - vr::DriverPose_t pose_to_use = - external.has_value() ? *external : slimevr_pose; - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( - device_index_, pose_to_use, sizeof(vr::DriverPose_t)); + if (external.has_value()) + pose_to_use = *external; } + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( + device_index_, pose_to_use, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::PositionMessage( @@ -215,11 +215,8 @@ void SlimeVRDriver::TrackerDevice::PositionMessage( triple_tap, 0); } - // Notify SteamVR that pose was updated + // Store for Update(); pose is submitted only there. last_pose_atomic_ = (last_pose_ = pose); - - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( - device_index_, pose, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::ControllerInputMessage( messages::ControllerInput &controllerInput) { @@ -298,9 +295,8 @@ void SlimeVRDriver::TrackerDevice::StatusMessage( // TODO: send position/rotation of 0 instead of last pose? + // Store for Update(); pose is submitted only there. last_pose_atomic_ = (last_pose_ = pose); - GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( - device_index_, pose, sizeof(vr::DriverPose_t)); } DeviceType SlimeVRDriver::TrackerDevice::GetDeviceType() { diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 31fcd26..12a6457 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -722,9 +722,6 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } } - external_left_pose_ = std::nullopt; - external_right_pose_ = std::nullopt; - auto *props = GetProperties(); for (uint32_t i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { if (our_indices.count(i)) From 0ff61708e6294938504ec2c3341e9594b6f427fd Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 21:25:01 -0600 Subject: [PATCH 102/117] Re-introduce hand swapping. --- src/VRDriver.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/VRDriver.hpp | 7 +++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 12a6457..582ae33 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -2,6 +2,7 @@ #include "TrackerRole.hpp" #include "VRPaths_openvr.hpp" #include +#include #include #include #include @@ -709,6 +710,22 @@ vr::DriverPose_t SlimeVRDriver::VRDriver::DriverPoseFromTrackedDevicePose( return pose; } +bool SlimeVRDriver::VRDriver::ExternalPoseEquals(const vr::DriverPose_t &a, + const vr::DriverPose_t &b) { + const float eps = 1e-5f; + for (int i = 0; i < 3; i++) { + if (std::fabs(a.vecPosition[i] - b.vecPosition[i]) > eps) + return false; + } + if (std::fabs(a.qRotation.w - b.qRotation.w) > eps || + std::fabs(a.qRotation.x - b.qRotation.x) > eps || + std::fabs(a.qRotation.y - b.qRotation.y) > eps || + std::fabs(a.qRotation.z - b.qRotation.z) > eps) { + return false; + } + return true; +} + void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::TrackedDevicePose_t raw_poses[vr::k_unMaxTrackedDeviceCount]; vr::VRServerDriverHost()->GetRawTrackedDevicePoses( @@ -754,9 +771,31 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); if (role == vr::TrackedControllerRole_LeftHand) { - external_left_pose_ = driver_pose; + if (last_external_left_pose_.has_value() && + ExternalPoseEquals(driver_pose, *last_external_left_pose_)) { + stale_external_left_frames_++; + if (stale_external_left_frames_ >= kStaleExternalPoseFrames) + external_left_pose_ = std::nullopt; // swap to SlimeVR + else + external_left_pose_ = driver_pose; + } else { + stale_external_left_frames_ = 0; + external_left_pose_ = driver_pose; + } + last_external_left_pose_ = driver_pose; } else if (role == vr::TrackedControllerRole_RightHand) { - external_right_pose_ = driver_pose; + if (last_external_right_pose_.has_value() && + ExternalPoseEquals(driver_pose, *last_external_right_pose_)) { + stale_external_right_frames_++; + if (stale_external_right_frames_ >= kStaleExternalPoseFrames) + external_right_pose_ = std::nullopt; // swap to SlimeVR + else + external_right_pose_ = driver_pose; + } else { + stale_external_right_frames_ = 0; + external_right_pose_ = driver_pose; + } + last_external_right_pose_ = driver_pose; } } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 2aa01fd..acdc42f 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -83,8 +83,15 @@ namespace SlimeVRDriver { std::optional external_left_pose_; std::optional external_right_pose_; + std::optional last_external_left_pose_; + std::optional last_external_right_pose_; + int stale_external_left_frames_ = 0; + int stale_external_right_frames_ = 0; + static constexpr int kStaleExternalPoseFrames = 30; // ~0.5s at 60 Hz void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); + static bool ExternalPoseEquals(const vr::DriverPose_t &a, + const vr::DriverPose_t &b); }; }; // namespace SlimeVRDriver From f5baaec6ba24068d96d4e92a4874ba6ff301e03e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 22:50:22 -0600 Subject: [PATCH 103/117] Attempt to make transition smoother. --- src/TrackerDevice.cpp | 37 ++++++++++++++++++++++++++++++++----- src/TrackerDevice.hpp | 5 +++++ src/VRDriver.hpp | 2 +- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 2b95b44..dcd9eb2 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -20,6 +20,28 @@ SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, int device_id, std::string SlimeVRDriver::TrackerDevice::GetSerial() { return serial_; } +vr::DriverPose_t +SlimeVRDriver::TrackerDevice::LerpPose(const vr::DriverPose_t &from, + const vr::DriverPose_t &to, float t) { + vr::DriverPose_t out = to; + for (int i = 0; i < 3; i++) + out.vecPosition[i] = + from.vecPosition[i] + (to.vecPosition[i] - from.vecPosition[i]) * t; + // Nlerp rotation + float w = (1.f - t) * from.qRotation.w + t * to.qRotation.w; + float x = (1.f - t) * from.qRotation.x + t * to.qRotation.x; + float y = (1.f - t) * from.qRotation.y + t * to.qRotation.y; + float z = (1.f - t) * from.qRotation.z + t * to.qRotation.z; + float len = std::sqrt(w * w + x * x + y * y + z * z); + if (len > 1e-6f) { + out.qRotation.w = w / len; + out.qRotation.x = x / len; + out.qRotation.y = y / len; + out.qRotation.z = z / len; + } + return out; +} + void SlimeVRDriver::TrackerDevice::Update() { if (device_index_ == vr::k_unTrackedDeviceIndexInvalid) return; @@ -121,16 +143,21 @@ void SlimeVRDriver::TrackerDevice::Update() { system_click_value, 0); } - // Single place the pose is finally set: controllers use external (VD/Steam - // Link) when available else SlimeVR; trackers use last SlimeVR pose. - vr::DriverPose_t pose_to_use = last_pose_atomic_.load(); + // Target pose: controllers use external (VD/Steam Link) when available else + // SlimeVR; trackers use last SlimeVR pose. + vr::DriverPose_t target = last_pose_atomic_.load(); if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); if (external.has_value()) - pose_to_use = *external; + target = *external; } + // Lerp from current toward target to reduce jitter and smooth transitions. + if (!smoothed_pose_.has_value()) + smoothed_pose_ = target; + else + smoothed_pose_ = LerpPose(*smoothed_pose_, target, kPoseLerpSpeed); GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( - device_index_, pose_to_use, sizeof(vr::DriverPose_t)); + device_index_, *smoothed_pose_, sizeof(vr::DriverPose_t)); } void SlimeVRDriver::TrackerDevice::PositionMessage( diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index ff09545..5e5c7ac 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -68,6 +69,10 @@ class TrackerDevice : public IVRDevice { vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); + std::optional smoothed_pose_; + static constexpr float kPoseLerpSpeed = 0.8f; + static vr::DriverPose_t LerpPose(const vr::DriverPose_t &from, + const vr::DriverPose_t &to, float t); bool did_vibrate_ = false; float vibrate_anim_state_ = 0.f; diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index acdc42f..d604341 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -87,7 +87,7 @@ namespace SlimeVRDriver { std::optional last_external_right_pose_; int stale_external_left_frames_ = 0; int stale_external_right_frames_ = 0; - static constexpr int kStaleExternalPoseFrames = 30; // ~0.5s at 60 Hz + static constexpr int kStaleExternalPoseFrames = 1; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); From a8656f13bd935cb35ed8ec3970f27d0139abc9d0 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 20 Feb 2026 23:04:42 -0600 Subject: [PATCH 104/117] Dont write fingers if there is no data. --- src/TrackerDevice.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index dcd9eb2..7797451 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -206,22 +206,20 @@ void SlimeVRDriver::TrackerDevice::PositionMessage( bool double_tap = false; bool triple_tap = false; - if (fingertracking_enabled_) { - // Set finger rotations + // Only push SlimeVR finger data when SlimeVR is actually reporting it. + // When it isn't, we skip so we don't overwrite finger data from Virtual + // Desktop/Steam Link + if (fingertracking_enabled_ && position.finger_bone_rotations_size() > 0) { vr::VRBoneTransform_t finger_skeleton_[31]{}; for (int i = 0; i < position.finger_bone_rotations_size(); i++) { - // Get data from protobuf auto fingerData = position.finger_bone_rotations(i); int fingerBoneName = static_cast(fingerData.name()); - - // Map from our 15 bones to OpenVR's 31 bones int boneIndex = protobuf_fingers_to_openvr[fingerBoneName]; finger_skeleton_[boneIndex].orientation = { fingerData.w(), fingerData.x(), fingerData.y(), fingerData.z()}; } - - // Update the finger skeleton for this hand. With and without controller - // have the same pose. + // Update the finger skeleton once with the full hand. With and without + // controller have the same pose. vr::VRDriverInput()->UpdateSkeletonComponent( skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, finger_skeleton_, 31); From cefcbe71e49d060c1ed162e5ccb746dd438afd9a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 21 Feb 2026 00:36:20 -0600 Subject: [PATCH 105/117] Makes swapping to SlimeVR hand tracking more responsive --- src/TrackerDevice.cpp | 14 +++-------- src/VRDriver.cpp | 57 ++++++++++++++++++++++++++++--------------- src/VRDriver.hpp | 6 +++++ 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 7797451..ce3e2a5 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -49,9 +49,7 @@ void SlimeVRDriver::TrackerDevice::Update() { // Check if this device was asked to be identified auto events = GetDriver()->GetOpenVREvents(); for (auto event : events) { - // Note here, event.trackedDeviceIndex does not necessarily equal - // device_index_, not sure why, but the component handle will match so we - // can just use that instead + // Note here, event.trackedDeviceIndex does not necessarily equal device_index_, not sure why, but the component handle will match so we can just use that instead // if (event.trackedDeviceIndex == device_index_) { if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { if (event.data.hapticVibration.componentHandle == haptic_component_) { @@ -143,8 +141,7 @@ void SlimeVRDriver::TrackerDevice::Update() { system_click_value, 0); } - // Target pose: controllers use external (VD/Steam Link) when available else - // SlimeVR; trackers use last SlimeVR pose. + // Target pose: controllers use external (VD/Steam Link) when available else SlimeVR; trackers use last SlimeVR pose. vr::DriverPose_t target = last_pose_atomic_.load(); if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); @@ -206,9 +203,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage( bool double_tap = false; bool triple_tap = false; - // Only push SlimeVR finger data when SlimeVR is actually reporting it. - // When it isn't, we skip so we don't overwrite finger data from Virtual - // Desktop/Steam Link + // Only push SlimeVR finger data when SlimeVR is actually reporting it; when it isn't, we skip so we don't overwrite finger data from Virtual Desktop/Steam Link. if (fingertracking_enabled_ && position.finger_bone_rotations_size() > 0) { vr::VRBoneTransform_t finger_skeleton_[31]{}; for (int i = 0; i < position.finger_bone_rotations_size(); i++) { @@ -246,8 +241,7 @@ void SlimeVRDriver::TrackerDevice::PositionMessage( void SlimeVRDriver::TrackerDevice::ControllerInputMessage( messages::ControllerInput &controllerInput) { if (was_activated_ && is_controller_) { - // Get inputs from protobuf, store them for Update which is called during - // RunFrame + // Get inputs from protobuf, store them for Update which is called during RunFrame trigger_value_ = controllerInput.trigger(); trigger_value_click = controllerInput.trigger() > 0.5f; grip_value = controllerInput.grip(); diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 582ae33..8fabcce 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -258,8 +258,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { pos.v[1] += trans.translation.v[1]; pos.v[2] += trans.translation.v[2]; - // rotate by quaternion w = cos(-trans.yaw / 2), x = 0, y = sin(-trans.yaw - // / 2), z = 0 + // rotate by quaternion w = cos(-trans.yaw/2), x = 0, y = sin(-trans.yaw/2), z = 0 auto tmp_w = cos(-trans.yaw / 2); auto tmp_y = sin(-trans.yaw / 2); auto new_w = tmp_w * q.w - tmp_y * q.y; @@ -272,9 +271,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { q.y = new_y; q.z = new_z; - // rotate point on the xz plane by -trans.yaw radians - // this is equivilant to the quaternion multiplication, after applying the - // double angle formula. + // rotate point on the xz plane by -trans.yaw radians; equivalent to the quaternion multiplication after applying the double angle formula. float tmp_sin = sin(-trans.yaw); float tmp_cos = cos(-trans.yaw); auto pos_x = pos.v[0] * tmp_cos + pos.v[2] * tmp_sin; @@ -344,9 +341,7 @@ void SlimeVRDriver::VRDriver::RunFrame() { now - last_frame_time_); last_frame_time_ = now; - // Update external controller poses (e.g. Virtual Desktop / Steam Link on - // Quest) so we can prefer their hand position when in view and fall back to - // SlimeVR when not. + // Update external controller poses (e.g. Virtual Desktop / Steam Link on Quest) so we can prefer their hand position when in view and fall back to SlimeVR when not. { std::lock_guard lock(devices_mutex_); UpdateExternalControllerPoses(); @@ -528,9 +523,7 @@ vr::IVRServerDriverHost *SlimeVRDriver::VRDriver::GetDriverHost() { } //----------------------------------------------------------------------------- -// Purpose: Calculates quaternion (qw,qx,qy,qz) representing the rotation -// from: -// https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp +// Purpose: Calculates quaternion (qw,qx,qy,qz) representing the rotation. From: https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp //----------------------------------------------------------------------------- vr::HmdQuaternion_t @@ -569,8 +562,7 @@ SlimeVRDriver::UniverseTranslation::parse(simdjson::ondemand::object &obj) { int iii = 0; for (auto component : obj["translation"]) { if (iii > 2) { - break; // TODO: 4 components in a translation vector? should this be an - // error? + break; // TODO: 4 components in a translation vector? should this be an error? } res.translation.v[iii] = static_cast(component.get_double()); iii += 1; @@ -712,17 +704,31 @@ vr::DriverPose_t SlimeVRDriver::VRDriver::DriverPoseFromTrackedDevicePose( bool SlimeVRDriver::VRDriver::ExternalPoseEquals(const vr::DriverPose_t &a, const vr::DriverPose_t &b) { - const float eps = 1e-5f; + // Position only; rotation ignored so small rotation jitter doesn't block frozen detection. + const float pos_eps = 0.005f; // ~5 mm for (int i = 0; i < 3; i++) { - if (std::fabs(a.vecPosition[i] - b.vecPosition[i]) > eps) + if (std::fabs(a.vecPosition[i] - b.vecPosition[i]) > pos_eps) return false; } - if (std::fabs(a.qRotation.w - b.qRotation.w) > eps || - std::fabs(a.qRotation.x - b.qRotation.x) > eps || - std::fabs(a.qRotation.y - b.qRotation.y) > eps || - std::fabs(a.qRotation.z - b.qRotation.z) > eps) { + return true; +} + +bool SlimeVRDriver::VRDriver::ExternalHandInFrontAndInRadius( + const float hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) { + if (!hmd_pose.bPoseIsValid) return false; - } + const auto &m = hmd_pose.mDeviceToAbsoluteTracking.m; + float hx = m[0][3], hy = m[1][3], hz = m[2][3]; + float dx = hand_pos[0] - hx; + float dy = hand_pos[1] - hy; + float dz = hand_pos[2] - hz; + float dist_sq = dx * dx + dy * dy + dz * dz; + if (dist_sq > kExternalHandMaxRadius * kExternalHandMaxRadius) + return false; + // Forward = -Z in OpenVR (third column of rotation) + float fx = -m[0][2], fy = -m[1][2], fz = -m[2][2]; + if (dx * fx + dy * fy + dz * fz < 0.f) + return false; // behind HMD return true; } @@ -739,6 +745,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } } + const vr::TrackedDevicePose_t &hmd_pose = raw_poses[0]; auto *props = GetProperties(); for (uint32_t i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { if (our_indices.count(i)) @@ -770,6 +777,16 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); + // Only use external hand when it's in front of HMD and within 160 cm + // radius; else SlimeVR. + if (!ExternalHandInFrontAndInRadius(driver_pose.vecPosition, hmd_pose)) { + if (role == vr::TrackedControllerRole_LeftHand) + external_left_pose_ = std::nullopt; + else if (role == vr::TrackedControllerRole_RightHand) + external_right_pose_ = std::nullopt; + // Don't update last_external_* so next frame we don't compare against a pose we rejected + continue; + } if (role == vr::TrackedControllerRole_LeftHand) { if (last_external_left_pose_.has_value() && ExternalPoseEquals(driver_pose, *last_external_left_pose_)) { diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index d604341..7aa14fb 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -88,10 +88,16 @@ namespace SlimeVRDriver { int stale_external_left_frames_ = 0; int stale_external_right_frames_ = 0; static constexpr int kStaleExternalPoseFrames = 1; + /// Max distance (meters) from HMD for using external hand; beyond this or behind HMD → SlimeVR. 1.6f = 160 cm. + static constexpr float kExternalHandMaxRadius = 1.6f; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); static bool ExternalPoseEquals(const vr::DriverPose_t &a, const vr::DriverPose_t &b); + /// True if hand position is in front of HMD and within kExternalHandMaxRadius. + static bool + ExternalHandInFrontAndInRadius(const float hand_pos[3], + const vr::TrackedDevicePose_t &hmd_pose); }; }; // namespace SlimeVRDriver From 58879bde75529a8436170fc02ef98af0ae342857 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 21 Feb 2026 01:23:02 -0600 Subject: [PATCH 106/117] Fix type issues. --- src/TrackerDevice.cpp | 14 +++++++------- src/VRDriver.cpp | 16 +++++++--------- src/VRDriver.hpp | 2 +- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index ce3e2a5..064fad0 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -27,13 +27,13 @@ SlimeVRDriver::TrackerDevice::LerpPose(const vr::DriverPose_t &from, for (int i = 0; i < 3; i++) out.vecPosition[i] = from.vecPosition[i] + (to.vecPosition[i] - from.vecPosition[i]) * t; - // Nlerp rotation - float w = (1.f - t) * from.qRotation.w + t * to.qRotation.w; - float x = (1.f - t) * from.qRotation.x + t * to.qRotation.x; - float y = (1.f - t) * from.qRotation.y + t * to.qRotation.y; - float z = (1.f - t) * from.qRotation.z + t * to.qRotation.z; - float len = std::sqrt(w * w + x * x + y * y + z * z); - if (len > 1e-6f) { + // Nlerp rotation (pose quaternions are double) + double w = (1.0 - static_cast(t)) * from.qRotation.w + static_cast(t) * to.qRotation.w; + double x = (1.0 - static_cast(t)) * from.qRotation.x + static_cast(t) * to.qRotation.x; + double y = (1.0 - static_cast(t)) * from.qRotation.y + static_cast(t) * to.qRotation.y; + double z = (1.0 - static_cast(t)) * from.qRotation.z + static_cast(t) * to.qRotation.z; + double len = std::sqrt(w * w + x * x + y * y + z * z); + if (len > 1e-6) { out.qRotation.w = w / len; out.qRotation.x = x / len; out.qRotation.y = y / len; diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 8fabcce..b7e9d49 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -714,20 +714,18 @@ bool SlimeVRDriver::VRDriver::ExternalPoseEquals(const vr::DriverPose_t &a, } bool SlimeVRDriver::VRDriver::ExternalHandInFrontAndInRadius( - const float hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) { + const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) { if (!hmd_pose.bPoseIsValid) return false; const auto &m = hmd_pose.mDeviceToAbsoluteTracking.m; - float hx = m[0][3], hy = m[1][3], hz = m[2][3]; - float dx = hand_pos[0] - hx; - float dy = hand_pos[1] - hy; - float dz = hand_pos[2] - hz; - float dist_sq = dx * dx + dy * dy + dz * dz; - if (dist_sq > kExternalHandMaxRadius * kExternalHandMaxRadius) + double hx = m[0][3], hy = m[1][3], hz = m[2][3]; + double dx = hand_pos[0] - hx, dy = hand_pos[1] - hy, dz = hand_pos[2] - hz; + double dist_sq = dx * dx + dy * dy + dz * dz; + if (dist_sq > static_cast(kExternalHandMaxRadius) * kExternalHandMaxRadius) return false; // Forward = -Z in OpenVR (third column of rotation) - float fx = -m[0][2], fy = -m[1][2], fz = -m[2][2]; - if (dx * fx + dy * fy + dz * fz < 0.f) + double fx = -m[0][2], fy = -m[1][2], fz = -m[2][2]; + if (dx * fx + dy * fy + dz * fz < 0.0) return false; // behind HMD return true; } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 7aa14fb..fb8215e 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -97,7 +97,7 @@ namespace SlimeVRDriver { const vr::DriverPose_t &b); /// True if hand position is in front of HMD and within kExternalHandMaxRadius. static bool - ExternalHandInFrontAndInRadius(const float hand_pos[3], + ExternalHandInFrontAndInRadius(const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose); }; }; // namespace SlimeVRDriver From b75238b56ae64731caf49f68e0e5743f0609b8e0 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 21 Feb 2026 02:39:25 -0600 Subject: [PATCH 107/117] Increase allowed hand distance. --- src/VRDriver.cpp | 3 +-- src/VRDriver.hpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index b7e9d49..4775ed7 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -775,8 +775,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); - // Only use external hand when it's in front of HMD and within 160 cm - // radius; else SlimeVR. + // Only use external hand when it's in front of HMD and within 170 cm radius; else SlimeVR. if (!ExternalHandInFrontAndInRadius(driver_pose.vecPosition, hmd_pose)) { if (role == vr::TrackedControllerRole_LeftHand) external_left_pose_ = std::nullopt; diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index fb8215e..7c2e5fd 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -88,8 +88,8 @@ namespace SlimeVRDriver { int stale_external_left_frames_ = 0; int stale_external_right_frames_ = 0; static constexpr int kStaleExternalPoseFrames = 1; - /// Max distance (meters) from HMD for using external hand; beyond this or behind HMD → SlimeVR. 1.6f = 160 cm. - static constexpr float kExternalHandMaxRadius = 1.6f; + /// Max distance (meters) from HMD for using external hand; beyond this or behind HMD → SlimeVR. 1.7f = 170 cm. + static constexpr float kExternalHandMaxRadius = 1.7f; void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); From c71afcdcf8bc649be1c497ac9a6289e4203b0146 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 21 Feb 2026 02:47:17 -0600 Subject: [PATCH 108/117] Add config file resource for hand tracking/slimevr skeleton ratios. --- .../resources/slimevr_driver_config.json | 6 +++ src/IVRDriver.hpp | 3 ++ src/TrackerDevice.cpp | 2 +- src/TrackerDevice.hpp | 1 - src/VRDriver.cpp | 42 +++++++++++++++---- src/VRDriver.hpp | 20 +++++---- 6 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 driver/slimevr/resources/slimevr_driver_config.json diff --git a/driver/slimevr/resources/slimevr_driver_config.json b/driver/slimevr/resources/slimevr_driver_config.json new file mode 100644 index 0000000..4f08333 --- /dev/null +++ b/driver/slimevr/resources/slimevr_driver_config.json @@ -0,0 +1,6 @@ +{ + "external_hand_max_radius_m": 1.7, + "stale_external_pose_frames": 1, + "pose_lerp_speed": 0.8, + "frozen_pose_position_epsilon_m": 0.005 +} diff --git a/src/IVRDriver.hpp b/src/IVRDriver.hpp index 31c396f..7f23b9d 100644 --- a/src/IVRDriver.hpp +++ b/src/IVRDriver.hpp @@ -97,6 +97,9 @@ class IVRDriver : protected vr::IServerTrackedDeviceProvider { virtual std::optional GetExternalPoseForHand(bool left_hand) = 0; + /** Pose lerp speed (0–1) for smoothing VD/SlimeVR transitions. From driver config file. */ + virtual float GetPoseLerpSpeed() = 0; + virtual inline const char *const *GetInterfaceVersions() override { return vr::k_InterfaceVersions; }; diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 064fad0..59cb1a6 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -152,7 +152,7 @@ void SlimeVRDriver::TrackerDevice::Update() { if (!smoothed_pose_.has_value()) smoothed_pose_ = target; else - smoothed_pose_ = LerpPose(*smoothed_pose_, target, kPoseLerpSpeed); + smoothed_pose_ = LerpPose(*smoothed_pose_, target, GetDriver()->GetPoseLerpSpeed()); GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( device_index_, *smoothed_pose_, sizeof(vr::DriverPose_t)); } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index 5e5c7ac..c9eeae6 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -70,7 +70,6 @@ class TrackerDevice : public IVRDevice { std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); std::optional smoothed_pose_; - static constexpr float kPoseLerpSpeed = 0.8f; static vr::DriverPose_t LerpPose(const vr::DriverPose_t &from, const vr::DriverPose_t &to, float t); diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 4775ed7..da73659 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -32,6 +32,8 @@ SlimeVRDriver::VRDriver::Init(vr::IVRDriverContext *pDriverContext) { logger_->Log("SlimeVR Driver Loaded Successfully"); + LoadDriverConfig(); + bridge_ = std::make_shared( std::static_pointer_cast(std::make_shared("Bridge")), std::bind(&SlimeVRDriver::VRDriver::OnBridgeMessage, this, @@ -478,6 +480,32 @@ bool SlimeVRDriver::VRDriver::AddDevice(std::shared_ptr device) { return true; } +void SlimeVRDriver::VRDriver::LoadDriverConfig() { + vr::IVRResources *res = vr::VRResources(); + if (!res) return; + char path[1024]; + uint32_t len = res->GetResourceFullPath( + "slimevr_driver_config.json", "", path, sizeof(path)); + if (len == 0 || len >= sizeof(path)) return; + path[len] = '\0'; + try { + auto json = simdjson::padded_string::load(path); + simdjson::ondemand::document doc = json_parser_.iterate(json); + auto obj = doc.get_object(); + if (auto v = obj["external_hand_max_radius_m"]; !v.error()) config_external_hand_max_radius_m_ = static_cast(v.get_double()); + if (auto v = obj["stale_external_pose_frames"]; !v.error()) config_stale_external_pose_frames_ = static_cast(v.get_int64()); + if (auto v = obj["pose_lerp_speed"]; !v.error()) config_pose_lerp_speed_ = static_cast(v.get_double()); + if (auto v = obj["frozen_pose_position_epsilon_m"]; !v.error()) config_frozen_pose_position_epsilon_m_ = static_cast(v.get_double()); + logger_->Log("Loaded driver config from {}", path); + } catch (const simdjson::simdjson_error &) { + // Use defaults; config file missing or invalid + } +} + +float SlimeVRDriver::VRDriver::GetPoseLerpSpeed() { + return config_pose_lerp_speed_; +} + SlimeVRDriver::SettingsValue SlimeVRDriver::VRDriver::GetSettingsValue(std::string key) { vr::EVRSettingsError err = vr::EVRSettingsError::VRSettingsError_None; @@ -703,9 +731,8 @@ vr::DriverPose_t SlimeVRDriver::VRDriver::DriverPoseFromTrackedDevicePose( } bool SlimeVRDriver::VRDriver::ExternalPoseEquals(const vr::DriverPose_t &a, - const vr::DriverPose_t &b) { - // Position only; rotation ignored so small rotation jitter doesn't block frozen detection. - const float pos_eps = 0.005f; // ~5 mm + const vr::DriverPose_t &b) const { + float pos_eps = config_frozen_pose_position_epsilon_m_; for (int i = 0; i < 3; i++) { if (std::fabs(a.vecPosition[i] - b.vecPosition[i]) > pos_eps) return false; @@ -714,14 +741,15 @@ bool SlimeVRDriver::VRDriver::ExternalPoseEquals(const vr::DriverPose_t &a, } bool SlimeVRDriver::VRDriver::ExternalHandInFrontAndInRadius( - const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) { + const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) const { if (!hmd_pose.bPoseIsValid) return false; + double r = static_cast(config_external_hand_max_radius_m_); const auto &m = hmd_pose.mDeviceToAbsoluteTracking.m; double hx = m[0][3], hy = m[1][3], hz = m[2][3]; double dx = hand_pos[0] - hx, dy = hand_pos[1] - hy, dz = hand_pos[2] - hz; double dist_sq = dx * dx + dy * dy + dz * dz; - if (dist_sq > static_cast(kExternalHandMaxRadius) * kExternalHandMaxRadius) + if (dist_sq > r * r) return false; // Forward = -Z in OpenVR (third column of rotation) double fx = -m[0][2], fy = -m[1][2], fz = -m[2][2]; @@ -788,7 +816,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { if (last_external_left_pose_.has_value() && ExternalPoseEquals(driver_pose, *last_external_left_pose_)) { stale_external_left_frames_++; - if (stale_external_left_frames_ >= kStaleExternalPoseFrames) + if (stale_external_left_frames_ >= config_stale_external_pose_frames_) external_left_pose_ = std::nullopt; // swap to SlimeVR else external_left_pose_ = driver_pose; @@ -801,7 +829,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { if (last_external_right_pose_.has_value() && ExternalPoseEquals(driver_pose, *last_external_right_pose_)) { stale_external_right_frames_++; - if (stale_external_right_frames_ >= kStaleExternalPoseFrames) + if (stale_external_right_frames_ >= config_stale_external_pose_frames_) external_right_pose_ = std::nullopt; // swap to SlimeVR else external_right_pose_ = driver_pose; diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 7c2e5fd..1f36d5d 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -87,17 +87,19 @@ namespace SlimeVRDriver { std::optional last_external_right_pose_; int stale_external_left_frames_ = 0; int stale_external_right_frames_ = 0; - static constexpr int kStaleExternalPoseFrames = 1; - /// Max distance (meters) from HMD for using external hand; beyond this or behind HMD → SlimeVR. 1.7f = 170 cm. - static constexpr float kExternalHandMaxRadius = 1.7f; + // Values from slimevr_driver_config.json (fallbacks if file missing) + float config_external_hand_max_radius_m_ = 1.7f; + int config_stale_external_pose_frames_ = 1; + float config_pose_lerp_speed_ = 0.8f; + float config_frozen_pose_position_epsilon_m_ = 0.005f; + void LoadDriverConfig(); void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); - static bool ExternalPoseEquals(const vr::DriverPose_t &a, - const vr::DriverPose_t &b); - /// True if hand position is in front of HMD and within kExternalHandMaxRadius. - static bool - ExternalHandInFrontAndInRadius(const double hand_pos[3], - const vr::TrackedDevicePose_t &hmd_pose); + bool ExternalPoseEquals(const vr::DriverPose_t &a, + const vr::DriverPose_t &b) const; + bool ExternalHandInFrontAndInRadius(const double hand_pos[3], + const vr::TrackedDevicePose_t &hmd_pose) const; + virtual float GetPoseLerpSpeed() override; }; }; // namespace SlimeVRDriver From a20fd4186af2da1575915014cfae371d5c47969e Mon Sep 17 00:00:00 2001 From: Sebastina Date: Fri, 27 Feb 2026 00:43:48 -0600 Subject: [PATCH 109/117] Add slower lerp when tracking system swaps. --- src/IVRDriver.hpp | 4 +++- src/TrackerDevice.cpp | 12 +++++++++--- src/TrackerDevice.hpp | 1 + src/VRDriver.cpp | 5 +++++ src/VRDriver.hpp | 2 ++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/IVRDriver.hpp b/src/IVRDriver.hpp index 7f23b9d..d47067d 100644 --- a/src/IVRDriver.hpp +++ b/src/IVRDriver.hpp @@ -97,8 +97,10 @@ class IVRDriver : protected vr::IServerTrackedDeviceProvider { virtual std::optional GetExternalPoseForHand(bool left_hand) = 0; - /** Pose lerp speed (0–1) for smoothing VD/SlimeVR transitions. From driver config file. */ + /** Pose lerp speed (0–1) for smoothing. From driver config file. */ virtual float GetPoseLerpSpeed() = 0; + /** Slower lerp speed used when swapping VD ↔ SlimeVR to smooth the transition. */ + virtual float GetPoseLerpSpeedOnSwap() = 0; virtual inline const char *const *GetInterfaceVersions() override { return vr::k_InterfaceVersions; diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 59cb1a6..282ba0f 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -143,16 +143,22 @@ void SlimeVRDriver::TrackerDevice::Update() { // Target pose: controllers use external (VD/Steam Link) when available else SlimeVR; trackers use last SlimeVR pose. vr::DriverPose_t target = last_pose_atomic_.load(); + bool have_external = false; if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); - if (external.has_value()) + have_external = external.has_value(); + if (have_external) target = *external; } - // Lerp from current toward target to reduce jitter and smooth transitions. + // Use slower lerp when swapping VD ↔ SlimeVR to smooth the transition. + float lerp_t = GetDriver()->GetPoseLerpSpeed(); + if (is_controller_ && (have_external != last_frame_had_external_)) + lerp_t = GetDriver()->GetPoseLerpSpeedOnSwap(); + last_frame_had_external_ = have_external; if (!smoothed_pose_.has_value()) smoothed_pose_ = target; else - smoothed_pose_ = LerpPose(*smoothed_pose_, target, GetDriver()->GetPoseLerpSpeed()); + smoothed_pose_ = LerpPose(*smoothed_pose_, target, lerp_t); GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated( device_index_, *smoothed_pose_, sizeof(vr::DriverPose_t)); } diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp index c9eeae6..7b4d2ab 100644 --- a/src/TrackerDevice.hpp +++ b/src/TrackerDevice.hpp @@ -70,6 +70,7 @@ class TrackerDevice : public IVRDevice { std::atomic last_pose_atomic_ = IVRDevice::MakeDefaultPose(); std::optional smoothed_pose_; + bool last_frame_had_external_ = false; static vr::DriverPose_t LerpPose(const vr::DriverPose_t &from, const vr::DriverPose_t &to, float t); diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index da73659..1d9788a 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -495,6 +495,7 @@ void SlimeVRDriver::VRDriver::LoadDriverConfig() { if (auto v = obj["external_hand_max_radius_m"]; !v.error()) config_external_hand_max_radius_m_ = static_cast(v.get_double()); if (auto v = obj["stale_external_pose_frames"]; !v.error()) config_stale_external_pose_frames_ = static_cast(v.get_int64()); if (auto v = obj["pose_lerp_speed"]; !v.error()) config_pose_lerp_speed_ = static_cast(v.get_double()); + if (auto v = obj["pose_lerp_speed_on_swap"]; !v.error()) config_pose_lerp_speed_on_swap_ = static_cast(v.get_double()); if (auto v = obj["frozen_pose_position_epsilon_m"]; !v.error()) config_frozen_pose_position_epsilon_m_ = static_cast(v.get_double()); logger_->Log("Loaded driver config from {}", path); } catch (const simdjson::simdjson_error &) { @@ -506,6 +507,10 @@ float SlimeVRDriver::VRDriver::GetPoseLerpSpeed() { return config_pose_lerp_speed_; } +float SlimeVRDriver::VRDriver::GetPoseLerpSpeedOnSwap() { + return config_pose_lerp_speed_on_swap_; +} + SlimeVRDriver::SettingsValue SlimeVRDriver::VRDriver::GetSettingsValue(std::string key) { vr::EVRSettingsError err = vr::EVRSettingsError::VRSettingsError_None; diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 1f36d5d..0175c67 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -91,6 +91,7 @@ namespace SlimeVRDriver { float config_external_hand_max_radius_m_ = 1.7f; int config_stale_external_pose_frames_ = 1; float config_pose_lerp_speed_ = 0.8f; + float config_pose_lerp_speed_on_swap_ = 0.25f; float config_frozen_pose_position_epsilon_m_ = 0.005f; void LoadDriverConfig(); void UpdateExternalControllerPoses(); @@ -101,5 +102,6 @@ namespace SlimeVRDriver { bool ExternalHandInFrontAndInRadius(const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) const; virtual float GetPoseLerpSpeed() override; + virtual float GetPoseLerpSpeedOnSwap() override; }; }; // namespace SlimeVRDriver From 9d6e1581e6220f6075052882c07fdedf96b6124a Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 18 Apr 2026 20:24:47 -0500 Subject: [PATCH 110/117] Update vcpkg --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index 66c0373..2fa7118 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 66c0373dc7fca549e5803087b9487edfe3aca0a1 +Subproject commit 2fa7118fb2ce0c27ab73e08ab1991f4cb67af880 From ba40e5b38869bc262a3c83fa1a16d3e2ed04ec45 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 18 Apr 2026 20:48:38 -0500 Subject: [PATCH 111/117] Re-number indexes --- src/bridge/ProtobufMessages.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bridge/ProtobufMessages.proto b/src/bridge/ProtobufMessages.proto index 1ab2802..ad67b78 100644 --- a/src/bridge/ProtobufMessages.proto +++ b/src/bridge/ProtobufMessages.proto @@ -119,9 +119,9 @@ message Position { repeated FingerBoneRotation finger_bone_rotations = 10; repeated Input input = 11; - optional float vx = 10; - optional float vy = 11; - optional float vz = 12; + optional float vx = 12; + optional float vy = 13; + optional float vz = 14; } message ControllerInput { From e88e43a7643997efc1baef0762956011e486a7ed Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 18 Apr 2026 21:01:52 -0500 Subject: [PATCH 112/117] Fix code post rebase --- src/VRDriver.cpp | 193 ++++++------------------------------ src/bridge/BridgeClient.cpp | 4 +- 2 files changed, 35 insertions(+), 162 deletions(-) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 1d9788a..e1df3cc 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -64,7 +64,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { continue; } - messages::ProtobufMessage* message = google::protobuf::Arena::CreateMessage(&arena_); + messages::ProtobufMessage* message = google::protobuf::Arena::Create(&arena_); vr::TrackedDevicePose_t hmd_pose; vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0.0f, &hmd_pose, 1); @@ -92,7 +92,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { logger_->Log("HMD props: serial='{}', model='{}', manufacturer='{}'", serial, name, manufacturer); // Send add message for HMD - messages::TrackerAdded* tracker_added = google::protobuf::Arena::CreateMessage(&arena_); + messages::TrackerAdded* tracker_added = google::protobuf::Arena::Create(&arena_); message->set_allocated_tracker_added(tracker_added); tracker_added->set_tracker_id(0); tracker_added->set_tracker_role(TrackerRole::HMD); @@ -101,7 +101,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { tracker_added->set_manufacturer(manufacturer.empty() ? "OpenVR" : manufacturer); bridge_->SendBridgeMessage(*message); - messages::TrackerStatus* tracker_status = google::protobuf::Arena::CreateMessage(&arena_); + messages::TrackerStatus* tracker_status = google::protobuf::Arena::Create(&arena_); message->set_allocated_tracker_status(tracker_status); tracker_status->set_tracker_id(0); tracker_status->set_status(messages::TrackerStatus_Status::TrackerStatus_Status_OK); @@ -161,7 +161,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { pos.v[2] = pos_z; } - messages::Position* hmd_position = google::protobuf::Arena::CreateMessage(&arena_); + messages::Position* hmd_position = google::protobuf::Arena::Create(&arena_); message->set_allocated_position(hmd_position); hmd_position->set_tracker_id(0); hmd_position->set_data_source(messages::Position_DataSource_FULL); @@ -178,7 +178,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { if (std::chrono::duration_cast(now - battery_sent_at_).count() > 100) { vr::ETrackedPropertyError err; if (vr::VRProperties()->GetBoolProperty(hmd_prop_container, vr::Prop_DeviceProvidesBatteryStatus_Bool, &err)) { - messages::Battery* hmdBattery = google::protobuf::Arena::CreateMessage(&arena_); + messages::Battery* hmdBattery = google::protobuf::Arena::Create(&arena_); message->set_allocated_battery(hmdBattery); hmdBattery->set_tracker_id(0); hmdBattery->set_battery_level(vr::VRProperties()->GetFloatProperty(hmd_prop_container, vr::Prop_DeviceBatteryPercentage_Float, &err) * 100); @@ -192,140 +192,7 @@ void SlimeVRDriver::VRDriver::RunPoseRequestThread() { std::this_thread::sleep_for(std::chrono::milliseconds(2)); } - - messages::ProtobufMessage *message = - google::protobuf::Arena::CreateMessage( - &arena_); - - if (!sent_hmd_add_message_) { - // Send add message for HMD - messages::TrackerAdded *tracker_added = - google::protobuf::Arena::CreateMessage( - &arena_); - message->set_allocated_tracker_added(tracker_added); - tracker_added->set_tracker_id(0); - tracker_added->set_tracker_role(TrackerRole::HMD); - tracker_added->set_tracker_serial("HMD"); - tracker_added->set_tracker_name("HMD"); - bridge_->SendBridgeMessage(*message); - - messages::TrackerStatus *tracker_status = - google::protobuf::Arena::CreateMessage( - &arena_); - message->set_allocated_tracker_status(tracker_status); - tracker_status->set_tracker_id(0); - tracker_status->set_status( - messages::TrackerStatus_Status::TrackerStatus_Status_OK); - bridge_->SendBridgeMessage(*message); - - sent_hmd_add_message_ = true; - logger_->Log("Sent HMD hello message"); - } - - vr::PropertyContainerHandle_t hmd_prop_container = - vr::VRProperties()->TrackedDeviceToPropertyContainer( - vr::k_unTrackedDeviceIndex_Hmd); - - vr::ETrackedPropertyError universe_error; - uint64_t universe = vr::VRProperties()->GetUint64Property( - hmd_prop_container, vr::Prop_CurrentUniverseId_Uint64, &universe_error); - if (universe_error == vr::ETrackedPropertyError::TrackedProp_Success) { - if (!current_universe_.has_value() || - current_universe_.value().first != universe) { - auto result = SearchUniverses(universe); - if (result.has_value()) { - current_universe_.emplace(universe, result.value()); - logger_->Log("Found current universe"); - } else { - logger_->Log("Failed to find current universe!"); - } - } - } else if (universe_error != last_universe_error_) { - logger_->Log( - "Failed to find current universe: Prop_CurrentUniverseId_Uint64 " - "error = {}", - vr::VRPropertiesRaw()->GetPropErrorNameFromEnum(universe_error)); - } - last_universe_error_ = universe_error; - - vr::TrackedDevicePose_t hmd_pose; - vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0.0f, &hmd_pose, 1); - - vr::HmdQuaternion_t q = GetRotation(hmd_pose.mDeviceToAbsoluteTracking); - vr::HmdVector3_t pos = GetPosition(hmd_pose.mDeviceToAbsoluteTracking); - - if (current_universe_.has_value()) { - auto trans = current_universe_.value().second; - pos.v[0] += trans.translation.v[0]; - pos.v[1] += trans.translation.v[1]; - pos.v[2] += trans.translation.v[2]; - - // rotate by quaternion w = cos(-trans.yaw/2), x = 0, y = sin(-trans.yaw/2), z = 0 - auto tmp_w = cos(-trans.yaw / 2); - auto tmp_y = sin(-trans.yaw / 2); - auto new_w = tmp_w * q.w - tmp_y * q.y; - auto new_x = tmp_w * q.x + tmp_y * q.z; - auto new_y = tmp_w * q.y + tmp_y * q.w; - auto new_z = tmp_w * q.z - tmp_y * q.x; - - q.w = new_w; - q.x = new_x; - q.y = new_y; - q.z = new_z; - - // rotate point on the xz plane by -trans.yaw radians; equivalent to the quaternion multiplication after applying the double angle formula. - float tmp_sin = sin(-trans.yaw); - float tmp_cos = cos(-trans.yaw); - auto pos_x = pos.v[0] * tmp_cos + pos.v[2] * tmp_sin; - auto pos_z = pos.v[0] * -tmp_sin + pos.v[2] * tmp_cos; - - pos.v[0] = pos_x; - pos.v[2] = pos_z; - } - - messages::Position *hmd_position = - google::protobuf::Arena::CreateMessage(&arena_); - message->set_allocated_position(hmd_position); - hmd_position->set_tracker_id(0); - hmd_position->set_data_source(messages::Position_DataSource_FULL); - hmd_position->set_x(pos.v[0]); - hmd_position->set_y(pos.v[1]); - hmd_position->set_z(pos.v[2]); - hmd_position->set_qx((float)q.x); - hmd_position->set_qy((float)q.y); - hmd_position->set_qz((float)q.z); - hmd_position->set_qw((float)q.w); - bridge_->SendBridgeMessage(*message); - - auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(now - - battery_sent_at_) - .count() > 100) { - vr::ETrackedPropertyError err; - if (vr::VRProperties()->GetBoolProperty( - hmd_prop_container, vr::Prop_DeviceProvidesBatteryStatus_Bool, - &err)) { - messages::Battery *hmdBattery = - google::protobuf::Arena::CreateMessage(&arena_); - message->set_allocated_battery(hmdBattery); - hmdBattery->set_tracker_id(0); - hmdBattery->set_battery_level( - vr::VRProperties()->GetFloatProperty( - hmd_prop_container, vr::Prop_DeviceBatteryPercentage_Float, - &err) * - 100); - hmdBattery->set_is_charging(vr::VRProperties()->GetBoolProperty( - hmd_prop_container, vr::Prop_DeviceIsCharging_Bool, &err)); - bridge_->SendBridgeMessage(*message); - } - battery_sent_at_ = now; - } - - arena_.Reset(); - - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - } - logger_->Log("Pose request thread exited"); + logger_->Log("Pose request thread exited"); } void SlimeVRDriver::VRDriver::RunFrame() { @@ -605,32 +472,39 @@ SlimeVRDriver::UniverseTranslation::parse(simdjson::ondemand::object &obj) { return res; } -std::optional SlimeVRDriver::VRDriver::SearchUniverse(const simdjson::padded_string &json, uint64_t target) { - simdjson::ondemand::document doc = json_parser_.iterate(json); - - for (simdjson::ondemand::object uni: doc["universes"]) { - // TODO: universeID comes after the translation, would it be faster to unconditionally parse the translation? - auto elem = uni["universeID"]; - uint64_t parsed_universe; - - auto is_integer = elem.is_integer(); - if (!is_integer.error() && is_integer.value_unsafe()) { - parsed_universe = elem.get_uint64(); +std::optional SlimeVRDriver::VRDriver::SearchUniverse(std::string path_or_json, uint64_t target) { + try { + simdjson::padded_string json; + if (path_or_json.find('{') != std::string::npos) { + json = simdjson::padded_string(path_or_json); } else { - parsed_universe = elem.get_uint64_in_string(); + json = simdjson::padded_string::load(path_or_json); } + simdjson::ondemand::document doc = json_parser_.iterate(json); + + for (simdjson::ondemand::object uni : doc["universes"]) { + // TODO: universeID comes after the translation, would it be faster to unconditionally parse the translation? + auto elem = uni["universeID"]; + uint64_t parsed_universe; + + auto is_integer = elem.is_integer(); + if (!is_integer.error() && is_integer.value_unsafe()) { + parsed_universe = elem.get_uint64(); + } else { + parsed_universe = elem.get_uint64_in_string(); + } - if (parsed_universe == target) { - auto standing_uni = uni["standing"].get_object(); - return SlimeVRDriver::UniverseTranslation::parse(standing_uni.value()); + if (parsed_universe == target) { + auto standing_uni = uni["standing"].get_object(); + return SlimeVRDriver::UniverseTranslation::parse(standing_uni.value()); + } } + } catch (simdjson::simdjson_error &e) { + logger_->Log("Error getting universes from {}: {}", path_or_json, e.what()); + return std::nullopt; } - } catch (simdjson::simdjson_error &e) { - logger_->Log("Error getting universes from {}: {}", path, e.what()); - return std::nullopt; - } - return std::nullopt; + return std::nullopt; } std::optional SlimeVRDriver::VRDriver::SearchUniverses(uint64_t target) { @@ -647,7 +521,6 @@ std::optional SlimeVRDriver::VRDriver::Searc logger_->Log("Error loading driver-provided chaperone JSON: {}", e.what()); } } - } auto driver_chap_path = vr::VRProperties()->GetStringProperty(hmd_prop_container, vr::Prop_DriverProvidedChaperonePath_String); if (driver_chap_path != "") { diff --git a/src/bridge/BridgeClient.cpp b/src/bridge/BridgeClient.cpp index 716bcda..fa141a7 100644 --- a/src/bridge/BridgeClient.cpp +++ b/src/bridge/BridgeClient.cpp @@ -82,8 +82,8 @@ void BridgeClient::CloseConnectionHandles() { } void BridgeClient::SendVersion() { - messages::ProtobufMessage* message = google::protobuf::Arena::CreateMessage(&arena_); - messages::Version* version = google::protobuf::Arena::CreateMessage(&arena_); + messages::ProtobufMessage* message = google::protobuf::Arena::Create(&arena_); + messages::Version* version = google::protobuf::Arena::Create(&arena_); message->set_allocated_version(version); version->set_protocol_version(PROTOCOL_VERSION); SendBridgeMessage(*message); From 51236fa8386322e877c4c8cacf85f4dd0c339ea3 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sat, 18 Apr 2026 21:22:45 -0500 Subject: [PATCH 113/117] Compile error fix attempt for universe --- src/VRDriver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index e1df3cc..b8b3121 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -525,7 +525,7 @@ std::optional SlimeVRDriver::VRDriver::Searc auto driver_chap_path = vr::VRProperties()->GetStringProperty(hmd_prop_container, vr::Prop_DriverProvidedChaperonePath_String); if (driver_chap_path != "") { try { - auto driver_res = SearchUniverse(simdjson::padded_string::load(driver_chap_path).take_value(), target); + auto driver_res = SearchUniverse(driver_chap_path, target); if (driver_res.has_value()) { return driver_res.value(); } @@ -537,7 +537,7 @@ std::optional SlimeVRDriver::VRDriver::Searc if (default_chap_path_.has_value() && std::filesystem::exists(default_chap_path_.value())) { try { - return SearchUniverse(simdjson::padded_string::load(default_chap_path_.value()).take_value(), target); + return SearchUniverse(default_chap_path_.value(), target); } catch (simdjson::simdjson_error &e) { logger_->Log("Error loading chaperone from default path {}: {}", default_chap_path_.value(), e.what()); From a89f96a60d7d1597e96a8c423b7b7c80575810c3 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Sun, 19 Apr 2026 01:13:09 -0500 Subject: [PATCH 114/117] Attempt to merge button states from virtual desktop. Add configurable controller priority. --- src/IVRDriver.hpp | 8 +++ src/TrackerDevice.cpp | 134 ++++++++++++++++++------------------------ src/VRDriver.cpp | 33 +++++++++++ src/VRDriver.hpp | 11 ++++ 4 files changed, 108 insertions(+), 78 deletions(-) diff --git a/src/IVRDriver.hpp b/src/IVRDriver.hpp index d47067d..90a7295 100644 --- a/src/IVRDriver.hpp +++ b/src/IVRDriver.hpp @@ -102,6 +102,14 @@ class IVRDriver : protected vr::IServerTrackedDeviceProvider { /** Slower lerp speed used when swapping VD ↔ SlimeVR to smooth the transition. */ virtual float GetPoseLerpSpeedOnSwap() = 0; + /** Priority for the controller hand role. */ + virtual int GetControllerPriority() = 0; + /** Whether to avoid overwriting input when no data is received. */ + virtual bool GetInputPassthrough() = 0; + + /** Gets external controller buttons for the hand. */ + virtual uint64_t GetExternalButtonsForHand(bool left_hand) = 0; + virtual inline const char *const *GetInterfaceVersions() override { return vr::k_InterfaceVersions; }; diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 282ba0f..48e4042 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -68,86 +68,68 @@ void SlimeVRDriver::TrackerDevice::Update() { } } + bool have_external = false; + if (is_controller_) { + auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); + have_external = external.has_value(); + } + if (was_activated_ && is_controller_) { - // Get inputs from protobuf - LogInput(("Check handle for trigger before update. Value is " + - std::to_string(trigger_value_)) - .c_str(), - this->trigger_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, - trigger_value_, 0); - - LogInput(("Check handle for trigger touch before update. Value is " + - std::to_string(trigger_value_click)) - .c_str(), - this->trigger_component_touch_); - vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, - trigger_value_click, 0); - - LogInput(("Check handle for grip before update. Value is " + - std::to_string(grip_value)) - .c_str(), - this->grip_value_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, - grip_value, 0); - - LogInput(("Check handle for stick x before update. Value is " + - std::to_string(thumbstick_x_value)) - .c_str(), - this->stick_x_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, - thumbstick_x_value, 0); - - LogInput(("Check handle for stick y before update. Value is " + - std::to_string(thumbstick_y_value)) - .c_str(), - this->stick_y_component_); - vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, - thumbstick_y_value, 0); - - LogInput(("Check handle for button a before update. Value is " + - std::to_string(button_1_value)) - .c_str(), - this->button_a_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, - button_1_value, 0); + bool send_input = true; + if (GetDriver()->GetInputPassthrough() && have_external) { + send_input = false; + } - LogInput(("Check handle for button b before update. Value is " + - std::to_string(button_2_value)) - .c_str(), - this->button_b_component_); - vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, - button_2_value, 0); + // Merge external button state (from VD/Steam Link) with SlimeVR's own state. + // For digital buttons we OR them together so either source can trigger. + uint64_t ext_buttons = GetDriver()->GetExternalButtonsForHand(is_left_hand_); + bool ext_a = (ext_buttons & (1ULL << vr::k_EButton_A)) != 0; + bool ext_b = (ext_buttons & (1ULL << vr::k_EButton_ApplicationMenu)) != 0; + bool ext_stick = (ext_buttons & (1ULL << vr::k_EButton_IndexController_JoystickClick)) != 0; + bool ext_system = (ext_buttons & (1ULL << vr::k_EButton_System)) != 0; + bool ext_grip = (ext_buttons & (1ULL << vr::k_EButton_Grip)) != 0; + bool ext_trigger= (ext_buttons & (1ULL << vr::k_EButton_SteamVR_Trigger)) != 0; + + // Merged digital states: local SlimeVR OR external VD + bool merged_a = button_1_value || ext_a; + bool merged_b = button_2_value || ext_b; + bool merged_stick = stick_click_value || ext_stick; + bool merged_system = system_click_value|| ext_system; + bool merged_trigger_click = trigger_value_click || ext_trigger; + + if (send_input) { + // Analog inputs from SlimeVR (no external analog source available) + vr::VRDriverInput()->UpdateScalarComponent(this->trigger_component_, + trigger_value_, 0); + vr::VRDriverInput()->UpdateScalarComponent(this->grip_value_component_, + grip_value, 0); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_x_component_, + thumbstick_x_value, 0); + vr::VRDriverInput()->UpdateScalarComponent(this->stick_y_component_, + thumbstick_y_value, 0); + } - LogInput(("Check handle for stick click before update. Value is " + - std::to_string(stick_click_value)) - .c_str(), - this->stick_click_component_); + // Digital buttons are always merged regardless of send_input, + // so VD button presses always come through. + vr::VRDriverInput()->UpdateBooleanComponent(this->button_a_component_, + merged_a, 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->button_b_component_, + merged_b, 0); vr::VRDriverInput()->UpdateBooleanComponent(this->stick_click_component_, - stick_click_value, 0); - - LogInput(("Check handle for system before update. Value is " + - std::to_string(system_click_value)) - .c_str(), - this->system_component); + merged_stick, 0); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component, - system_click_value, 0); - - LogInput(("Check handle for system touch before update. Value is " + - std::to_string(system_click_value)) - .c_str(), - this->system_component_touch); + merged_system, 0); vr::VRDriverInput()->UpdateBooleanComponent(this->system_component_touch, - system_click_value, 0); + merged_system, 0); + vr::VRDriverInput()->UpdateBooleanComponent(this->trigger_component_touch_, + merged_trigger_click, 0); } // Target pose: controllers use external (VD/Steam Link) when available else SlimeVR; trackers use last SlimeVR pose. vr::DriverPose_t target = last_pose_atomic_.load(); - bool have_external = false; if (is_controller_) { auto external = GetDriver()->GetExternalPoseForHand(is_left_hand_); - have_external = external.has_value(); - if (have_external) + if (external.has_value()) target = *external; } // Use slower lerp when swapping VD ↔ SlimeVR to smooth the transition. @@ -406,7 +388,7 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { "slimevr_virtual_controller"); vr::VRProperties()->SetInt32Property( containerHandle_, vr::Prop_ControllerHandSelectionPriority_Int32, - 2147483647); // Prioritizes our controller over whatever else. + GetDriver()->GetControllerPriority()); } else { vr::VRProperties()->SetInt32Property(containerHandle_, vr::Prop_DeviceClass_Int32, @@ -643,15 +625,11 @@ vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) { NULL, // Fist 31, &skeletal_component_handle_); - // Update the skeleton so steamvr knows we have an active skeletal input - // device - vr::VRBoneTransform_t finger_skeleton_[31]{}; - vr::VRDriverInput()->UpdateSkeletonComponent( - skeletal_component_handle_, vr::VRSkeletalMotionRange_WithController, - finger_skeleton_, 31); - vr::VRDriverInput()->UpdateSkeletonComponent( - skeletal_component_handle_, vr::VRSkeletalMotionRange_WithoutController, - finger_skeleton_, 31); + // NOTE: We intentionally do NOT send a zeroed-out skeleton here. + // Sending an empty skeleton on activation would override finger data + // from other sources (e.g. Virtual Desktop hand tracking). + // The first real skeleton update will come via PositionMessage when + // SlimeVR Server actually has finger tracking data to send. } was_activated_ = true; return vr::EVRInitError::VRInitError_None; diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index b8b3121..c1130c3 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -201,6 +201,20 @@ void SlimeVRDriver::VRDriver::RunFrame() { std::vector events; while (vr::VRServerDriverHost()->PollNextEvent(&event, sizeof(event))) { events.push_back(event); + + if (event.eventType == vr::EVREventType::VREvent_ButtonPress || + event.eventType == vr::EVREventType::VREvent_ButtonUnpress) { + uint64_t mask = 1ULL << event.data.controller.button; + if (event.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { + if (event.trackedDeviceIndex == external_left_index_) { + if (event.eventType == vr::EVREventType::VREvent_ButtonPress) external_left_buttons_ |= mask; + else external_left_buttons_ &= ~mask; + } else if (event.trackedDeviceIndex == external_right_index_) { + if (event.eventType == vr::EVREventType::VREvent_ButtonPress) external_right_buttons_ |= mask; + else external_right_buttons_ &= ~mask; + } + } + } } openvr_events_ = std::move(events); @@ -364,6 +378,8 @@ void SlimeVRDriver::VRDriver::LoadDriverConfig() { if (auto v = obj["pose_lerp_speed"]; !v.error()) config_pose_lerp_speed_ = static_cast(v.get_double()); if (auto v = obj["pose_lerp_speed_on_swap"]; !v.error()) config_pose_lerp_speed_on_swap_ = static_cast(v.get_double()); if (auto v = obj["frozen_pose_position_epsilon_m"]; !v.error()) config_frozen_pose_position_epsilon_m_ = static_cast(v.get_double()); + if (auto v = obj["controller_priority"]; !v.error()) config_controller_priority_ = static_cast(v.get_int64()); + if (auto v = obj["input_passthrough"]; !v.error()) config_input_passthrough_ = v.get_bool().value_or(false); logger_->Log("Loaded driver config from {}", path); } catch (const simdjson::simdjson_error &) { // Use defaults; config file missing or invalid @@ -378,6 +394,14 @@ float SlimeVRDriver::VRDriver::GetPoseLerpSpeedOnSwap() { return config_pose_lerp_speed_on_swap_; } +int SlimeVRDriver::VRDriver::GetControllerPriority() { + return config_controller_priority_; +} + +bool SlimeVRDriver::VRDriver::GetInputPassthrough() { + return config_input_passthrough_; +} + SlimeVRDriver::SettingsValue SlimeVRDriver::VRDriver::GetSettingsValue(std::string key) { vr::EVRSettingsError err = vr::EVRSettingsError::VRSettingsError_None; @@ -637,6 +661,9 @@ bool SlimeVRDriver::VRDriver::ExternalHandInFrontAndInRadius( } void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { + external_left_index_ = vr::k_unTrackedDeviceIndexInvalid; + external_right_index_ = vr::k_unTrackedDeviceIndexInvalid; + vr::TrackedDevicePose_t raw_poses[vr::k_unMaxTrackedDeviceCount]; vr::VRServerDriverHost()->GetRawTrackedDevicePoses( 0.0f, raw_poses, vr::k_unMaxTrackedDeviceCount); @@ -691,6 +718,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { continue; } if (role == vr::TrackedControllerRole_LeftHand) { + external_left_index_ = i; if (last_external_left_pose_.has_value() && ExternalPoseEquals(driver_pose, *last_external_left_pose_)) { stale_external_left_frames_++; @@ -704,6 +732,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } last_external_left_pose_ = driver_pose; } else if (role == vr::TrackedControllerRole_RightHand) { + external_right_index_ = i; if (last_external_right_pose_.has_value() && ExternalPoseEquals(driver_pose, *last_external_right_pose_)) { stale_external_right_frames_++; @@ -719,3 +748,7 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { } } } + +uint64_t SlimeVRDriver::VRDriver::GetExternalButtonsForHand(bool left_hand) { + return left_hand ? external_left_buttons_ : external_right_buttons_; +} diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 0175c67..4a733ce 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -93,6 +93,8 @@ namespace SlimeVRDriver { float config_pose_lerp_speed_ = 0.8f; float config_pose_lerp_speed_on_swap_ = 0.25f; float config_frozen_pose_position_epsilon_m_ = 0.005f; + int config_controller_priority_ = 2147483647; + bool config_input_passthrough_ = false; void LoadDriverConfig(); void UpdateExternalControllerPoses(); static vr::DriverPose_t @@ -103,5 +105,14 @@ namespace SlimeVRDriver { const vr::TrackedDevicePose_t &hmd_pose) const; virtual float GetPoseLerpSpeed() override; virtual float GetPoseLerpSpeedOnSwap() override; + virtual int GetControllerPriority() override; + virtual bool GetInputPassthrough() override; + virtual uint64_t GetExternalButtonsForHand(bool left_hand) override; + + // External controller event spying + vr::TrackedDeviceIndex_t external_left_index_ = vr::k_unTrackedDeviceIndexInvalid; + vr::TrackedDeviceIndex_t external_right_index_ = vr::k_unTrackedDeviceIndexInvalid; + uint64_t external_left_buttons_ = 0; + uint64_t external_right_buttons_ = 0; }; }; // namespace SlimeVRDriver From 190c3b4559c6ca8d8878cfb8fe25d2e959d2843c Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 21 Apr 2026 00:12:36 -0500 Subject: [PATCH 115/117] Fix some incompatible API functions. --- src/TrackerDevice.cpp | 2 +- src/VRDriver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp index 48e4042..5ccfd73 100644 --- a/src/TrackerDevice.cpp +++ b/src/TrackerDevice.cpp @@ -85,7 +85,7 @@ void SlimeVRDriver::TrackerDevice::Update() { uint64_t ext_buttons = GetDriver()->GetExternalButtonsForHand(is_left_hand_); bool ext_a = (ext_buttons & (1ULL << vr::k_EButton_A)) != 0; bool ext_b = (ext_buttons & (1ULL << vr::k_EButton_ApplicationMenu)) != 0; - bool ext_stick = (ext_buttons & (1ULL << vr::k_EButton_IndexController_JoystickClick)) != 0; + bool ext_stick = (ext_buttons & (1ULL << vr::k_EButton_SteamVR_Touchpad)) != 0; bool ext_system = (ext_buttons & (1ULL << vr::k_EButton_System)) != 0; bool ext_grip = (ext_buttons & (1ULL << vr::k_EButton_Grip)) != 0; bool ext_trigger= (ext_buttons & (1ULL << vr::k_EButton_SteamVR_Trigger)) != 0; diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index c1130c3..fe91eea 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -379,7 +379,7 @@ void SlimeVRDriver::VRDriver::LoadDriverConfig() { if (auto v = obj["pose_lerp_speed_on_swap"]; !v.error()) config_pose_lerp_speed_on_swap_ = static_cast(v.get_double()); if (auto v = obj["frozen_pose_position_epsilon_m"]; !v.error()) config_frozen_pose_position_epsilon_m_ = static_cast(v.get_double()); if (auto v = obj["controller_priority"]; !v.error()) config_controller_priority_ = static_cast(v.get_int64()); - if (auto v = obj["input_passthrough"]; !v.error()) config_input_passthrough_ = v.get_bool().value_or(false); + if (auto v = obj["input_passthrough"]; !v.error()) config_input_passthrough_ = v.get_bool().get(false); logger_->Log("Loaded driver config from {}", path); } catch (const simdjson::simdjson_error &) { // Use defaults; config file missing or invalid From 37c10db4525fc0231e475f3336fc995a345149f9 Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 21 Apr 2026 00:23:28 -0500 Subject: [PATCH 116/117] Attempt to fix API mismatch --- src/VRDriver.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index fe91eea..37a4a5b 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -379,7 +379,12 @@ void SlimeVRDriver::VRDriver::LoadDriverConfig() { if (auto v = obj["pose_lerp_speed_on_swap"]; !v.error()) config_pose_lerp_speed_on_swap_ = static_cast(v.get_double()); if (auto v = obj["frozen_pose_position_epsilon_m"]; !v.error()) config_frozen_pose_position_epsilon_m_ = static_cast(v.get_double()); if (auto v = obj["controller_priority"]; !v.error()) config_controller_priority_ = static_cast(v.get_int64()); - if (auto v = obj["input_passthrough"]; !v.error()) config_input_passthrough_ = v.get_bool().get(false); + if (auto v = obj["input_passthrough"]; !v.error()) { + bool val = false; + if (v.get_bool().get(val) == simdjson::SUCCESS) { + config_input_passthrough_ = val; + } + } logger_->Log("Loaded driver config from {}", path); } catch (const simdjson::simdjson_error &) { // Use defaults; config file missing or invalid From 009179a291368acbf11badc132d5b2f3cc6201fd Mon Sep 17 00:00:00 2001 From: Sebastina Date: Tue, 21 Apr 2026 05:18:06 -0500 Subject: [PATCH 117/117] Remove stalwe pose detrction as it false positives too much. --- .../resources/slimevr_driver_config.json | 4 +- src/VRDriver.cpp | 44 +++---------------- src/VRDriver.hpp | 8 ---- 3 files changed, 6 insertions(+), 50 deletions(-) diff --git a/driver/slimevr/resources/slimevr_driver_config.json b/driver/slimevr/resources/slimevr_driver_config.json index 4f08333..ef2a804 100644 --- a/driver/slimevr/resources/slimevr_driver_config.json +++ b/driver/slimevr/resources/slimevr_driver_config.json @@ -1,6 +1,4 @@ { "external_hand_max_radius_m": 1.7, - "stale_external_pose_frames": 1, - "pose_lerp_speed": 0.8, - "frozen_pose_position_epsilon_m": 0.005 + "pose_lerp_speed": 0.8 } diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp index 37a4a5b..e98f5e9 100644 --- a/src/VRDriver.cpp +++ b/src/VRDriver.cpp @@ -374,10 +374,8 @@ void SlimeVRDriver::VRDriver::LoadDriverConfig() { simdjson::ondemand::document doc = json_parser_.iterate(json); auto obj = doc.get_object(); if (auto v = obj["external_hand_max_radius_m"]; !v.error()) config_external_hand_max_radius_m_ = static_cast(v.get_double()); - if (auto v = obj["stale_external_pose_frames"]; !v.error()) config_stale_external_pose_frames_ = static_cast(v.get_int64()); if (auto v = obj["pose_lerp_speed"]; !v.error()) config_pose_lerp_speed_ = static_cast(v.get_double()); if (auto v = obj["pose_lerp_speed_on_swap"]; !v.error()) config_pose_lerp_speed_on_swap_ = static_cast(v.get_double()); - if (auto v = obj["frozen_pose_position_epsilon_m"]; !v.error()) config_frozen_pose_position_epsilon_m_ = static_cast(v.get_double()); if (auto v = obj["controller_priority"]; !v.error()) config_controller_priority_ = static_cast(v.get_int64()); if (auto v = obj["input_passthrough"]; !v.error()) { bool val = false; @@ -637,16 +635,6 @@ vr::DriverPose_t SlimeVRDriver::VRDriver::DriverPoseFromTrackedDevicePose( return pose; } -bool SlimeVRDriver::VRDriver::ExternalPoseEquals(const vr::DriverPose_t &a, - const vr::DriverPose_t &b) const { - float pos_eps = config_frozen_pose_position_epsilon_m_; - for (int i = 0; i < 3; i++) { - if (std::fabs(a.vecPosition[i] - b.vecPosition[i]) > pos_eps) - return false; - } - return true; -} - bool SlimeVRDriver::VRDriver::ExternalHandInFrontAndInRadius( const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) const { if (!hmd_pose.bPoseIsValid) @@ -715,41 +703,19 @@ void SlimeVRDriver::VRDriver::UpdateExternalControllerPoses() { vr::DriverPose_t driver_pose = DriverPoseFromTrackedDevicePose(p); // Only use external hand when it's in front of HMD and within 170 cm radius; else SlimeVR. if (!ExternalHandInFrontAndInRadius(driver_pose.vecPosition, hmd_pose)) { - if (role == vr::TrackedControllerRole_LeftHand) + if (role == vr::TrackedControllerRole_LeftHand) { external_left_pose_ = std::nullopt; - else if (role == vr::TrackedControllerRole_RightHand) + } else if (role == vr::TrackedControllerRole_RightHand) { external_right_pose_ = std::nullopt; - // Don't update last_external_* so next frame we don't compare against a pose we rejected + } continue; } if (role == vr::TrackedControllerRole_LeftHand) { external_left_index_ = i; - if (last_external_left_pose_.has_value() && - ExternalPoseEquals(driver_pose, *last_external_left_pose_)) { - stale_external_left_frames_++; - if (stale_external_left_frames_ >= config_stale_external_pose_frames_) - external_left_pose_ = std::nullopt; // swap to SlimeVR - else - external_left_pose_ = driver_pose; - } else { - stale_external_left_frames_ = 0; - external_left_pose_ = driver_pose; - } - last_external_left_pose_ = driver_pose; + external_left_pose_ = driver_pose; } else if (role == vr::TrackedControllerRole_RightHand) { external_right_index_ = i; - if (last_external_right_pose_.has_value() && - ExternalPoseEquals(driver_pose, *last_external_right_pose_)) { - stale_external_right_frames_++; - if (stale_external_right_frames_ >= config_stale_external_pose_frames_) - external_right_pose_ = std::nullopt; // swap to SlimeVR - else - external_right_pose_ = driver_pose; - } else { - stale_external_right_frames_ = 0; - external_right_pose_ = driver_pose; - } - last_external_right_pose_ = driver_pose; + external_right_pose_ = driver_pose; } } } diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp index 4a733ce..eb184fa 100644 --- a/src/VRDriver.hpp +++ b/src/VRDriver.hpp @@ -83,24 +83,16 @@ namespace SlimeVRDriver { std::optional external_left_pose_; std::optional external_right_pose_; - std::optional last_external_left_pose_; - std::optional last_external_right_pose_; - int stale_external_left_frames_ = 0; - int stale_external_right_frames_ = 0; // Values from slimevr_driver_config.json (fallbacks if file missing) float config_external_hand_max_radius_m_ = 1.7f; - int config_stale_external_pose_frames_ = 1; float config_pose_lerp_speed_ = 0.8f; float config_pose_lerp_speed_on_swap_ = 0.25f; - float config_frozen_pose_position_epsilon_m_ = 0.005f; int config_controller_priority_ = 2147483647; bool config_input_passthrough_ = false; void LoadDriverConfig(); void UpdateExternalControllerPoses(); static vr::DriverPose_t DriverPoseFromTrackedDevicePose(const vr::TrackedDevicePose_t &raw); - bool ExternalPoseEquals(const vr::DriverPose_t &a, - const vr::DriverPose_t &b) const; bool ExternalHandInFrontAndInRadius(const double hand_pos[3], const vr::TrackedDevicePose_t &hmd_pose) const; virtual float GetPoseLerpSpeed() override;