From dc2197ab08f0bb2fb39a3c4b7db5800035d5c34f Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Mon, 30 Oct 2023 06:22:01 +0200 Subject: [PATCH] ui: better objects table. better font. keybinds window. update fps every 100ms instead of every frame --- .gitignore | 1 + TODO.md | 2 + assets/fonts/Exo-Regular.ttf | Bin 0 -> 91580 bytes assets/fonts/OFL.txt | 93 +++++++++++++++ src/main.rs | 11 +- src/ui/ui.rs | 225 ++++++++++++++++++++++++----------- 6 files changed, 259 insertions(+), 73 deletions(-) create mode 100644 assets/fonts/Exo-Regular.ttf create mode 100644 assets/fonts/OFL.txt diff --git a/.gitignore b/.gitignore index 23650b5..4c2e105 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ 3rdparty/*/target models/**/* +3dobs.log diff --git a/TODO.md b/TODO.md index e7f6256..0f1e673 100644 --- a/TODO.md +++ b/TODO.md @@ -32,5 +32,7 @@ - [x] Toggle for dis/allowing the one instance option - [x] Show how much memory each loaded model is using - [ ] Move the invisible point lights to be around selected object from all sides so it's nicely illuminated + - [x] Shortcuts/keybinds window under the 'Help' menu item + - [ ] Texture support - [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later) diff --git a/assets/fonts/Exo-Regular.ttf b/assets/fonts/Exo-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b5721ce23e6138649e3c3593c92358f95ebcdcea GIT binary patch literal 91580 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
pbW4*PcvGdfF{J+#xs^~< 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�>Iyo*Ogu`C$Vk|fnQj7I--5zgBN!{$)@n_A1sm3;vdr}VPKjt zKHmmvu-lyFrg$xDAO2C3t0LU|N!x6&Ys6C7?7W#7Sas|N{y~4W+tjMfcXT$n(;d$1 zx`5Hgs@RnnAAV+*o}J7r?$x#)Wun|3P*EA|!mFv{JYTnl>+AfcD=zbdqVby?8`?%j zYn+A(Z~ZK*%G1S4Y!+KxV+qqm^P~oc-Q|tp`HzA2fYoQxYHOPv&aQs4TdaoNFSnAF z?}<5Z8?qdQl}FV#th`?-`q9us-!G304Y8U_g4n*_abfpEp`uIBuN!oK4gIh?M8<2i zb=M2)E-V<>>VBj?RNv%JSY5ha1M9%U#)Zwcp6>ppb+O1EFPp)W%cAuNjYGa$X;LRP?QSU@k%p5o?Gv7CY zOIY<;8(z9qX$!O2%AZU=r2Mbk>QhSSk{uaZVZuAn+Ta6PaAPXXBe#HwWlM;RASO+n zJ2L62MW^Qzs!WM2OBAdC@SU#VDr;F|oh@PC=V)!HuQk?}^`y5<4cUAXrOvv5ZOq(I z+SoYhwAq;{mYk?@o3&-p`c5CxVi8}bB~qxhxGSgH;_V&%_J-nmbHBCz-xc*GahJ{E zh?|{|YY6jqo174G?cr)n&%|KxsF~NKuW8X?zBd|ujYy^$<1If9ZRoU(H(cJ;XM|uQ z{dRkYb1ppKl2Q{hw&I3{SW|7quJhV-G6XBZWH!fwz>s?0nt1Zbiqyo!xbkW_ zw$7irxcjk4(G^#?&}$4;I?tdNY{-G!B_JZi)iz}8=Z#TIFwkN1>V^$$Uio`LVc0P+ zI1-vm#!hZdSGpV8T)uh>o9=m}%rq363#2VDArz@7>=%$tDYvn-W6O-?b{y4&TxAa0 z2TX2rL#yMN==MJ6bmO%Hqcv^G+SriO)#cgTI_hJ($pw3nwLW2OEHdo7Fqx-q+qE+< zPp)qDO$HLx!SleS3j(?mTrS%X7dx@~;QGgv=cKS>@pbkR*^m)X`75avF-%-DL{C_& z5#j2{klvHM92p+njQrjj^OG3F zrE#Cl;fdF}AmudX@=4r|#-+T%o*c`de55*P?>D(kxSsIY==OfcwDsD-(duNX24$b_ zZujQaF)#B^E!c}28xoC;#btXhOzGsdox8L;F6LmoI&>~cFte^{e~#>T53IF(tkunK zeck!`3u`ZNDUTjnQm%Thm7b?bAjbcM^m~j=?-bw_Lp`2^S&tvushRojgqiso&!~LE zVhL$K@v&>qt3K;Mw6RAjbk5d2Q+RoyPiHOo?*ae%8!s)qfL4-e>AUO#VHpQt8Cghq zae~zh%cFQIan0TA)4YmDeNvAci{li7p}D+J&&JC_216(xW|P{cG|#cectsAi zrOh#Ke4(w}(B-HqV=3#Pq0%u}LDf^8__>mvHU1jQ!*O?A zON=%nlwHZ*y{OY|Dl981%-0nfnhJGg#&vRyzR;bYKU`6me_oY|&aQXC>^`MA4OG`q zjJ`9Ma$D|)XzmB;IxvNM92f5wzn>Ss43p*MlLh*fzsRm{0S8}^{)q7muqQH}R?fwC zeBoqq)L;l03Uxn~suy2++%RP@Oy%cO&+SVI%{}}{Y4n_}bW|otP>3kG)V@$SS%jWM z3R}1MH_7~X8ShGGo+fm^S^6C+H}OpV{)8hD+py8 zT&(f(&+D<)evi*3&;@7|#=0k?i$~Ka$(*ZEs0ciTZCrYQ-HB_0ikA#nyCmzkA?rVg zd0j1Q{r^$clIgu?wtzCR%beB{r?_2bTr;htsSF=Q7c}Z)h+6ob z-zZ{89lyL0#+O;!M?Ga`XH~hY);>Mt44WfvcS|(+tGaNfgB6(?4bqrYTvvOjbuQ9V z*Oh#-DUhcNJC((;6rZc?SPNu$u%{O>HBxLbI%P++O2@#6Tg#CKGbN)fZg<2Sb`DS3 zYu$z_r@72C`m4^=7?Yi0U0$H+$z)euPh_t3P;Ff?1~xRBls6rn;W~`96&dI@@C6Oy zzL&qrfq zfk|xV_;wY4!f_Lw4pWoEUm0s^>I!w*N(!x=(O>mglWytOfmDwz)@@y!uj@&(vl^#kMBDmL^A|qb+Nb6{H+~*sQ{%Y>a_wU{NjW2IlcgJ^5J^8y|m=qd6j(Oe( zjdp4*TrV=8UHtRbrE$oMzISK72iNr3!#~g9^maf#4WGUI4az2b&gY+fs1U(PGHz%6 zCF`S=Mw#>)>AOsWwI;q{ujPEhe#^fB4gKiTfZT*mZs8TADh`Z+gA3l1vCiC{F2;I$ zi5nO(`UWjq`RT-wp9ewB6&w@2N4ACyf(H8Bg1CY{51~JOZq5A+O2p@crblXJe}jcW zngHH2twHt#{aOGIaCj$&cM=?wqCHEozme_QLlJ|S8Gidr(^tOU^XxBuU;YZUKu?$_ z-r|4VZ?r5)XDw)j*|qQD_E3X1r+;GKzli~ zRVvZEBfSDB1Po~30Zs$jUM9Uto;|Z;MfTB*C5&h0`98rhkdL=m6zTJ|r3Lx;MgFjZ9y zj5P%u4>-CQQG$px-UCmOl!ND#ESgF|803=BoWN32Ic6r!EprP#NBQSY{yCv}8!=m- zPLCRI&C*`<*vj0nC-TL~{hN_1C z6jDxBtJl@k)nDVP!SMOUiaWgBuJ&N(^)`oA>#Bb^)|_;;6<+_Lj*_F!KQkeHF;JJkwR)lp< zRJ0OROCMy;r6<@K+#6eQJGn&lbW$^(nUE>s+d~hWzE{YHu#e3c&#C&(WmT8uf7IGy zJ=+p#ygL8ts;l+aG)5ZF!q3Hr4jecT`WpThIuQB_{znoY!OYd7Rso&}Gbh93YjIhc zY<2|Y5tNi!3kM4tU3Qbv>arFM76-#NR?&Id&ar4=Nql0*DXH`++eYJtlHL=pQfKe) z0@p$(E@KPI@Bu)FGoyOKVyv<`fu#_ydNtX?!J^AbTF17Z+L=6Q+eDk8Ffz8|^sc$9 zPUtJ4C^?My@&Y{9!DBw;EfT^5Bcyb2#ZZII74sB!%tw+#wz;h-UuRo1;T`mzECNia ztg@2fWgfACf%I9bOEAj zDEB!I0zB1$FJv9eS>oN9KAuv7PMQ(~n1wPv{Cvb(Pn4 z_1fANk`_~Bwjx`P4hy3%?rqoGu>rYG>7dFUcg&|#jS@T?f&vdgPFF!9_ULs9n&2=qZCoO<*v{H2+e|36u^k-Gq?`NO!Ljl~Hq@*3H6EG=Tqt^zYwYC%b(6T>DtniLMj>VvSqz zUt_$nF@BTlMAyTCG0`fBKRVI!=V+;X9xaDOOY!f|(3El^9ZPpgcvA=d(ra)(9%Uuu znAu1#oBlt(PuN`SC=P|{-`87(rDvUK`{8T1vu(5Q@BPBKCu4Bw57G$sW);{=a~*~Z zRS{-h1EoiN=)zhr&kC;+5T@I;&AV|Vuwb3)Hk73Xe$W1(ycHa4aCZCgMyReiX8q8~ z`)Burs)}D;Jeyt#^|{{OKjW=XnxZnxNZ0%r`@O>f`A{`N8%~e)m`TKj*hwa$c>dU< z{$74hJ*oAI6_vnixQ?G1@5alO{?t7_)y}NSa}V|I>Tb05?Cx(IAGH{|CVjE-kjWSv zO^ofZSVkro(|NRcp1fQ0T)I4uR@rKqy#Byx*Us4Ti}KrLU0!w1+1s|C+g+WX*P$=c zVT~D=TBRPjQRARp3GO%G3%Qcg4E(%<-x{LRQFCQGN%+*#6;@=FjM@uP$Y^$_1^J zt{vQDiMyCm;EumPgx&)}ciQ1HGCVO4h1xuM-XpUIe>3;P^FAq+OV26aMDflw$~A0E z`4$@;9mQ;PfeEYhXPS-ucxMh&g#Y9mLm_mgD=Fy5X6!oU(#xI3bo*X}=9}R%?izwd(jf)aPL%RBLC=krM-Ycb;j|r| zn9G=lpZ!zAr%rfAV!N)G`yP8#>0*!KrKNjoJ5QM0d)ZjyYoQlHqxd12!%9jCxdMI^ zk;{I(5EHvlL=izeSYImZ^(1P)Tpiy%J-s_oEsqpR#ns;SrlxkUxmYUva?|y1KXCl< z54?T-E76m7tqTO!?K&y?%9QlOMJP(1#sb`ZsEsK=o}wJ}JUGt)wG1d7tlWbd2p8Nz z9)4UNn{j)RdHRydZ!cjZwaRC?l!wK*=wn2jaJ@&-E|*#f5efr+2V~?FoWUT{rmA=_?E5X>$XQL zP)nk`&Q1>m?hXcjX&LS98LC%)%RfQ3#Jd%|8?Y9uu^Lx#4MrXa~ z8BAC$@v)$5)bWk0ul{JEjQiLAc=>o&eL8Zv^juZT#(~a-M2*ap=g$rX9|!~<2nJuA znr!d0K=)F7MxVpXa(VBR;m|M!>7O|1&d6V1$slYOe%RU98q?i157sn0s_=%SCw^+{ zOWJGvE#@1QQ>6mslk61MfCn48i|t*}t{(GnMR3DV&rHaar~Hm#!k>iLmsb{UKfuZY zYpX9x!-^`W6@~S*tuMQtXf-jt&R=-p{0_HRN|)*bI_2#lNH7n)tcL_?wF?<)_UGQu z3BJrEKT=SSCu%%zbR@Ocw>v#NK5**E4_;hy%|3SgM;7EwSXoc8uPL)q1g`~tR`IRW zQgMe_8^$4DOZj8SB#-~_!DQnkdrV2==kN5w;OUo{HWa!r6uK1iNyp~`;_Jq$sqM} zS~PpG5cv$1^(ss3kxSSkO83LP2tJu+iuK`Hp-Tw|(L2Q+VSV(Cq&ZJ>53-H5Dh8H7 zjT$qv?=+ad{T&NGU(s zPE4{#CMEykuP^BB{TGlioIl(vF`Omi1nxl0<%;DQcdg*0431Xk=tu$6u z)s*#?)Kyg}FXeuMd=gSE?jgE_@(DeNBH^v|9wI=r3D(RGN{t<1aH+ zu=Fqw<5k1lYYC%Dfe$~3R*U~YHW3_?W)^=_C;60Zwl;g0BVn^899{M{+ho(iz`(#l zlln^&g^HEOesrbau4?e^ggZP$ri_;0%FgzoMWsLsf=6hpmEBh{%cki%5( zVi&O;>ZeFlp4e7A3ux(X4fuPK3>9n>P3KjEcrs*DHs{E*)p59G1R*_&MZuaEG+0a|Rbr zb1phj*OrCj8+pPgQj+r7PG1J(J4lK$D#PcAT3&7R-uDNi-J z=WjcC-N#SqitL5=LO3E{i?c;hZyqJPPxf9G)kQeW=5uB6ChlFb8TM}qdrf)~?5h-;|2pv3{B`F?e!O7P6chiuNhg;^ zh2$YdTrgcl7`ZQzi}1mkN$T#u|L<=Te)&t`SDT2I)QKOFZQ@tNo5()wpMlC+wh`YM zR$NrIaX=GevEtN$#Q9+5koW`C3D6QSpv;y+zLz+>t`LKM_Fgu=t~F$u!tY-t1Ww zMKG^9xiT-hx~Aj5Z#6ait%hjqc8>F7t^)M$>)_q=;kI$A<5J~6n?>U6yd6d6P&$KA z3qJ|XsYTicy_|G;$qBX?0U67OE-&_&0hzK*#5-bm4C`KQ}$eZ9NajJcz$J&tzH z`B2L57^sa;6;m7hIZLR|5-*yO-I?bC=9EYL@Fw@V^6lH~L4Mf8+207ed%0pinRk0>sjQa+Tka9&ks7gJkPiZvCNwfgGY^cyuye zqbB~|z!zd2_Tt<$^YFP5<{-(XLRY%|daz4Ik%6c6w`_}|tXM6tITgFXr>jSVJnH#~ zLaM}3tz6pilod#OeLgZsE*Ud_xX?8>kam5soNs*zw`|mN{GnvG)9rSfy@P38XJ32E zFYO_t;J>ceZL#qtliI)h_-?`1*erNeF>43MLX)wb_zpOTERL&{>*5|1dB}RBc3r(+G_MiQr}s znM`pT2xwZuZPj`v+ghXrt_nEFJf7F{sF|-8=+5U>7d@?p?B=oeixWv}oxL!bf6&(# z@b!jtrEaUeGu`4%S<;<}HLmc~fp^oB@mBvp=6&CAo<}s|ityz24HW{(WI(`G#vcwBd_``gP#E^6{r*hG?@vQUSXVizI3dSDO+k-Y z*IkX#U>1CWFsD`cjOHP&>+bGG{QIQ%s8~-1#gmf6bCTSS9w22*vCt^9Nh7pK_Hotm zJ4EGiUHNcZOmFdz{>We5*QYHCZL;ycB4ps$?N9onJb-pU@R}a2g@i zs;>*Z{(#pKIND@un&=I7^WM%euP(&*@9r5;+tg&MZ)h~+Nq9WseT|l8VjCIa`(okM zqkWE{Os5T*KP$JltF_7n5W}rH<*QnkWT>e{N%gq;j^kC>kKd^pd{S@{04kPiUegPv_ra*;5TZe)OcC=ljl>FHIr1*0lm&M4SJqHCW-~yo1bZL zv>H})#A3PTr)v!jdP8*iGHm56s-0+f6psv2~{m(LkFs9BTO4ecRKSXf$#=4&IYZ z!H}&l?$5W@H`OLnJ1ADOjFwD690jXJOfNlzve)Kz#nx_Sv^U;3GlM5M^0MMu+*&eU;pTrfr& zETOc;6)~66U-I*JTEiAc#7Ia!o==I-=|(mr$6W0(t9vjno(l~Ho|i1ERoDwVb~Ec8+e4i2KC_VX>(hv%Xs=y=RkCJ z)ao$6VazCa)J{3a^Z_>NGnZCC=2|Lg?|_d1fI+{*>GL@qe!e3TYj2N5I>;Hj*K2op zy^e6P7!D(85Pfx4o**wV)1U^h+CK0nBI%|JTnN%eloj^~Lo7aV&ebuQ%ueg|skAxN z6E--8IOZwoXr^H)5QJpgl8@v8`qv zn;WMua>eI%cbK%AaE+!lIz4>xW?!v3R@ZFqyb>@JDo-Pim;R-qwaiw*vZke}(=zw` ziz{}#8OF~;e?i#}=E`9*_xj)eXHkY!#<(6{1!7`o94+UH0jj+CNM?Z%mps4=L24+^kkJ4KAui`7dCM)6k zcVKkPh`cr*0RkHXUE@z{9@@xb;&^Iw?aeD6e0b%p)1xHcON#Tg55ddf+(0C4g8F%8 z|D2l#MLIhsSFfI&^smTqWy5|hVtg1sZ;RJt>aStkY>9Cz`^y9N8aCGw3=AmUOc(2B z_TzY#EXU+kKq-4qWKR!d+(n+AOGbybr2B%)cXr=HE*0+}mm-Ivm7f}2xh-#cQg}?r z;YINc+Y2)I2y59s6$8_i-=DHIGU`578=5FF z=DD-v^zvgr*mEkfVIq+j-xxWy=Lg4N!K#5*RzcnX?Jn^0tV(Jz0=+JTrt-pe&eh=M zaT#4xskb`QZ*}Y@CUNe`6)VV9{r%#XNWDos!=%MY@+eYZ9>wo33xaSDUbKJeruqO8 zHO+{q;czbW$mbswMkxQK5*SVgs^mbEk;Ij>WYEEm{Zr47kL_HUw_813ZdW`~nkYu3 zx3x*}?#zshzwo2+Vsw|=VsVeUEk>^FoZ>bLN}Q)i{Qk5q`oijGFbkvl+07d^h&S%tORnC0rFeY* z{{ONDOeVp!wf`U6(qXGDoG~{EzNUWi3@CLPW9kg)Xe6oo;M1CDC|U9&O2c&i&rAG< z`hcW3hFNe1x-+t%V@k#gN5`hrb1Xi*<6K1npWw%ZNG!K`=qX1bY%z!1Io9ZK7|77_ zE3P<%p}&{UmEvy_XOpito#^o|Lo6%mgthoUcd)@?Z^{);fOX30q%^YgZaQ!PuN%?Q zRGgG$`|qKG$7_ekgW}o~D8gpdIlS80i9Jn;Q4wME=$D1(bfgLk#d>NO+pS7$-< zXb^j_comJDWkF>b^1#S|xMOmX?7i$V@z`aJS8ooC^UFish4Pw}J$Z7I-|xQ#Xd_<` zXQZpA?nliTD25mS7z37QofZ5gdF$x;Pi@NRU#jskPA(H~r!QVE_HtDCqCj>nDELAn zA}=9lVA03vFeORpQpyKJbSNnF78f}lqpFzjajU1TE;y_nT2|-Gh237YDq-Vw>gFw) zO+uru6g36vw^+OLAE#!JRi%06fn)VrqWsk+lB9|T(j zcdRl+?%9?WUaf99EB|7SwJb-n3Nl3GUzLjZFU)Nf9m_y)tP}5{6<1I}dbVO?Drl(} z*V0%s@fEaOC$%WWr=^wyXn9g_}ZabrvF%b`!V#e z0ncC0i0D%-;?zgWHnbd-T53h0A1ynWeU&{DBsC&LWwab-#w#0HfeN+anAFnC9H~6W z{0=QGBK+WZW+!t3csPxgMlnqx5$|TED^^wm&WpTdiMFZWCcD3IOo%6)CcQoJl zX__$(wcRYZ<5>J~LB+d8WuEZ8!_3x-5w!teeJE3#7H|bkRy@2c8uHo=j%H?SvSe}A zuq^^^fELe@Usv8@=FXUhO7NtmA43uhyhMqdi>u&TwX8cwTcZ_{)WA4iCwX1 zUjMLoUTpY+iDA^!Yzq70evDct-mQ2TEn8S)rG-6#mU?joy_ZJ(6IBl=5lI4ptA!r(;pdr!9w>PeA!UMGK|;N6^yC z`YI2yx1ohnK7D38+lg=OMoXg@ms;**NyW$lckIt9=M)bEA9{?niltdg3n=;ZDkZbV zDkbk^4VAs@14~mf*Uou!d?4lHOY8%dj#ME+!i7w!jY7%RV!SWcs@PYt^X>x|Xkxxo zd50aR)Y>DNFeQ5U?F20Wmvb}Uk*~Q`;JZHowHhjS1Nu7g-g963AJHV`XySj$Yxw_y zCSkRy<~qQz0Vx%yVGva|_cQNE7K4N#zA$$}G7nilKJd$NV-BgsM}-CJs7P7p76Uahk6ZQxR2U}hE1fg7XN7u`dM1V&CJz6FaCzJ+s% z8Bu%-*)|B)ruiHYnJGAslD{=DGBS{iMw84)tfMo=yZwF_o;Tz9N2<@$qzwouNU8$w zG$*3b#K7<{Gs3%tfQyfHb|Ch9!T3sPe0xQUJU3&lN{JmhXGM>8@(0EGc^gQHuJ`0`|)615xHtXTOl{ zfD4;iR^KWzXloav?r3`&$q8NGBHH0jA0L{HaE9 zjbKsLH@1{#pT}$dR0H--kS_S`>3V>-W^vuXi5L!OqDbSOf|HMI9m#^$q_sQgS-6AQRrx%7xl9e^Pl*iM)JuH>D)E*a z?@tMyb{=H6b$jnY=8Ygn+`&LxZ;1OpV_}2IE)X5x))tEx4faK|;#_UOZ;6qP-peD*FE*7*FW*3)SK|5WFL)liXwN_nkXz*}BW_A anyhow::Result<(), Box> { utils::import_models_from_paths(&args_paths, &mut state); } + let mut time_since_last_frame_acc = 0.0; + // main loop while !window.should_close() { let current_frame = glfw.get_time() as f32; @@ -166,6 +168,13 @@ fn main() -> anyhow::Result<(), Box> { state.camera.update_speed(delta_time); + time_since_last_frame_acc += delta_time; + + if time_since_last_frame_acc >= 0.1 { + state.fps = 1.0 / delta_time; + time_since_last_frame_acc = 0.0; + } + // camera matrices let view_mat = glm::ext::look_at(state.camera.position, state.camera.position + state.camera.front, state.camera.up); let projection_mat = glm::ext::perspective(glm::radians(state.camera.fov), state.viewport_size[0] / state.viewport_size[1], 0.01, 200.0); @@ -270,7 +279,7 @@ fn main() -> anyhow::Result<(), Box> { gl::Clear(gl::COLOR_BUFFER_BIT); gl::Disable(gl::DEPTH_TEST); gl::Disable(gl::BLEND); - ui::draw_ui(&mut imgui, &renderer, &glfw_platform, &mut window, &mut state, delta_time, &mut last_cursor, scene_texture); + ui::draw_ui(&mut imgui, &renderer, &glfw_platform, &mut window, &mut state, &mut last_cursor, scene_texture); glfw.poll_events(); window.swap_buffers(); diff --git a/src/ui/ui.rs b/src/ui/ui.rs index df6d318..fbbe06b 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -16,6 +16,7 @@ pub struct State { pub show_console: bool, pub show_help_menu_about: bool, pub show_settings: bool, + pub show_keybinds: bool, pub is_cursor_captured: bool, pub can_capture_cursor: bool, pub draw_grid: bool, @@ -29,6 +30,7 @@ pub struct State { pub viewport_size: [f32; 2], pub logger: logger::WritableLog, pub settings: Settings, + pub fps: f32, } impl Default for State { @@ -38,6 +40,7 @@ impl Default for State { show_console: false, show_help_menu_about: false, show_settings: false, + show_keybinds: false, first_frame_drawn: false, is_cursor_captured: false, can_capture_cursor: false, @@ -51,6 +54,7 @@ impl Default for State { viewport_size: [0.0, 0.0], logger: logger::WritableLog::default(), settings: Settings::default(), + fps: 0.0, } } } @@ -76,12 +80,20 @@ pub fn init_imgui(window: &mut glfw::Window) -> (imgui::Context, imgui_glfw_supp glfw_platform.attach_window( imgui.io_mut(), &window, - imgui_glfw_support::HiDpiMode::Rounded + imgui_glfw_support::HiDpiMode::Default ); imgui .fonts() - .add_font(&[imgui::FontSource::DefaultFontData { config: None }]); + .add_font(&[imgui::FontSource::TtfData { + data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"), + size_pixels: 20.0, + config: Some(imgui::FontConfig { + oversample_h: 3, + pixel_snap_h: true, + ..imgui::FontConfig::default() + }) + }]); imgui.io_mut().font_global_scale = (1.0 / glfw_platform.hidpi_factor()) as f32; @@ -107,7 +119,7 @@ pub fn import_model(state: &mut State) { utils::import_models_from_paths(&models, state); } -pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw::Window, delta_time: f32) { +pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw::Window) { ui.main_menu_bar(|| { ui.menu("File", || { if ui.menu_item_config("Import Model(s)").shortcut("Ctrl+O").build() { @@ -124,20 +136,48 @@ pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw:: if ui.menu_item_config("Show Grid").selected(state.draw_grid).build() { state.draw_grid = !state.draw_grid; } + if ui.menu_item_config("Draw Bounding Box").selected(state.draw_aabb).build() { + state.draw_aabb = !state.draw_aabb; + } }); ui.menu("Help", || { + if ui.menu_item_config("Keybinds").selected(state.show_keybinds).build() { + state.show_keybinds = !state.show_keybinds; + } if ui.menu_item_config("About").selected(state.show_help_menu_about).build() { state.show_help_menu_about = !state.show_help_menu_about; } }); let mem = state.objects.iter().fold(0 as usize, |acc, m| acc + m.mem_usage) as f32; - let mem_fps = format!("Mem: {:.1}MB | FPS: {:.1}", mem / (1024.0 * 1024.0), 1.0 / delta_time); + let mem_fps = format!("Mem: {:.1}MB | FPS: {:.1}", mem / (1024.0 * 1024.0), state.fps); let avail_size = [*ui.content_region_avail().get(0).unwrap() - ui.calc_text_size(&mem_fps)[0], 0.0]; ui.dummy(avail_size); ui.text(&mem_fps); }); } +fn draw_about_window(ui: &imgui::Ui, state: &mut State) { + if !state.show_help_menu_about { + return; + } + let display_size = ui.io().display_size; + + ui.window("About") + .resizable(false) + .movable(false) + .opened(&mut state.show_help_menu_about) + .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) + .position_pivot([0.5, 0.5]) + .build(|| { + ui.text("3dobs - 3D Object Browser"); + ui.text(format!("Version: {}-{}", env!("CARGO_PKG_VERSION"), env!("GIT_HASH"))); + ui.text(format!("{}", env!("CARGO_PKG_DESCRIPTION"))); + ui.spacing(); + ui.spacing(); + ui.text(format!("Made by: {}", env!("CARGO_PKG_AUTHORS"))); + }); +} + pub fn draw_settings_window(ui: &imgui::Ui, state: &mut State) { if !state.show_settings { return; @@ -145,9 +185,9 @@ pub fn draw_settings_window(ui: &imgui::Ui, state: &mut State) { let display_size = ui.io().display_size; ui.window("Settings") - .size([500.0, 100.0], imgui::Condition::FirstUseEver) .opened(&mut state.show_settings) - .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::FirstUseEver) + .movable(false) + .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) .position_pivot([0.5, 0.5]) .build(|| { if ui.checkbox("Only allow one program instance (Reboot required when enabling)", &mut state.settings.one_instance) { @@ -156,6 +196,58 @@ pub fn draw_settings_window(ui: &imgui::Ui, state: &mut State) { }); } +fn draw_keybinds_window(ui: &imgui::Ui, state: &mut State) { + if !state.show_keybinds { + return; + } + let display_size = ui.io().display_size; + + ui.window("Keybinds") + .opened(&mut state.show_keybinds) + .resizable(false) + .movable(false) + .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) + .position_pivot([0.5, 0.5]) + .build(|| { + if let Some(..) = ui.begin_table_with_sizing("Keybinds Table", 2, imgui::TableFlags::SIZING_STRETCH_SAME, [0.0, 0.0], 0.0) { + ui.table_next_column(); + ui.text_colored([0.7, 0.7, 0.6, 1.0], "Key"); + ui.table_next_column(); + ui.text_colored([0.7, 0.7, 0.6, 1.0], "Action"); + + ui.table_next_column(); + ui.text("Ctrl + O | Drag & Drop"); + ui.table_next_column(); + ui.text("Import Model(s)"); + + ui.table_next_column(); + ui.text("Ctrl + Q"); + ui.table_next_column(); + ui.text("Quit"); + + ui.table_next_column(); + ui.text("Left Mouse Button"); + ui.table_next_column(); + ui.text("Rotate object"); + + ui.table_next_column(); + ui.text("Scroll"); + ui.table_next_column(); + ui.text("Zoom camera"); + + ui.table_next_column(); + ui.text("Left Shift"); + ui.table_next_column(); + ui.text("Pan camera"); + + ui.table_next_column(); + ui.text("Left Ctrl"); + ui.table_next_column(); + ui.text("Increase camera movement speed"); + } + }); +} + fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) { imgui::Drag::new("###XPos") .range(f32::NEG_INFINITY, f32::INFINITY) @@ -172,16 +264,6 @@ fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) { .speed(0.1) .display_format("Z: %.3f") .build(ui, &mut mesh.position.z); - imgui::Drag::new("Scale") - .range(f32::NEG_INFINITY, f32::INFINITY) - .speed(0.1) - .display_format("%.7f") - .build_array(ui, mesh.scale.as_array_mut()); - imgui::Drag::new("Rotation") - .range(f32::NEG_INFINITY, f32::INFINITY) - .speed(1.0) - .display_format("%.2f") - .build_array(ui, mesh.rotation.as_array_mut()); } fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) { @@ -198,27 +280,25 @@ fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) { } fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool { - let object = &mut state.objects[idx]; - if let Some(..) = ui.begin_table_with_sizing("Objects Table", 2, imgui::TableFlags::SIZING_STRETCH_PROP, [0.0, 0.0], 0.0) { - ui.table_next_row(); - ui.table_next_column(); - ui.tree_node_config(format!("{} ({:.1}MB)###{}", object.name.as_str(), object.mem_usage as f32 / (1024.0 * 1024.0), idx)) - .build(|| { - for (j, mesh) in &mut object.meshes.iter_mut().enumerate() { - draw_mesh_hierarchy(ui, mesh, j); - } - }); + ui.table_next_column(); + if ui.checkbox(format!("###{}", state.objects[idx].id), &mut (Some(state.objects[idx].id) == state.active_model)) { + state.objects[idx].reset_rotation(); + state.active_model = Some(state.objects[idx].id); + state.camera.focus_on_selected_model(state.active_model, &state.objects); + } - ui.table_next_column(); - if ui.small_button(format!("X###{}-{}", object.name.as_str(), idx)) { - info!("Removing object {}", object.name); - return true; - } - if ui.checkbox("Selected", &mut (Some(state.objects[idx].id) == state.active_model)) { - state.objects[idx].reset_rotation(); - state.active_model = Some(state.objects[idx].id); - state.camera.focus_on_selected_model(state.active_model, &state.objects); - } + ui.table_next_column(); + ui.tree_node_config(format!("{} ({:.1}MB)###{}", state.objects[idx].name.as_str(), state.objects[idx].mem_usage as f32 / (1024.0 * 1024.0), idx)) + .build(|| { + for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() { + draw_mesh_hierarchy(ui, mesh, j); + } + }); + + ui.table_next_column(); + if ui.small_button(format!("X###{}-{}", state.objects[idx].name.as_str(), idx)) { + info!("Removing object {}", state.objects[idx].name); + return true; } return false; @@ -230,19 +310,40 @@ fn draw_objects_window(ui: &imgui::Ui, state: &mut State) { .build(|| { let mut i = 0; - while i < state.objects.len() { - if draw_object_hierarchy(ui, state, i) { - let selected_obj_id = state.objects[i].id; - state.objects.remove(i); - if state.active_model == Some(selected_obj_id) { - let model = state.objects.last_mut().map(|m| m.reset_rotation()); - state.active_model = model.and_then(|o| Some(o.id)); - state.camera.focus_on_selected_model(state.active_model, &state.objects); - } - continue; - } + if let Some(..) = ui.begin_table_with_sizing("Objects Table", 3, imgui::TableFlags::SIZING_FIXED_FIT, [0.0, 0.0], 0.0) { + ui.table_setup_column_with(imgui::TableColumnSetup { + name: "", + flags: imgui::TableColumnFlags::empty(), + init_width_or_weight: 30.0, + user_id: imgui::Id::default(), + }); + ui.table_setup_column_with(imgui::TableColumnSetup { + name: "", + flags: imgui::TableColumnFlags::WIDTH_STRETCH, + init_width_or_weight: 0.0, + user_id: imgui::Id::default(), + }); + ui.table_setup_column_with(imgui::TableColumnSetup { + name: "", + flags: imgui::TableColumnFlags::empty(), + init_width_or_weight: 20.0, + user_id: imgui::Id::default(), + }); - i = i + 1; + while i < state.objects.len() { + if draw_object_hierarchy(ui, state, i) { + let selected_obj_id = state.objects[i].id; + state.objects.remove(i); + if state.active_model == Some(selected_obj_id) { + let model = state.objects.last_mut().map(|m| m.reset_rotation()); + state.active_model = model.and_then(|o| Some(o.id)); + state.camera.focus_on_selected_model(state.active_model, &state.objects); + } + continue; + } + + i = i + 1; + } } }); } @@ -252,7 +353,7 @@ fn draw_console(ui: &imgui::Ui, state: &mut State) { .size([500.0, 200.0], imgui::Condition::FirstUseEver) .build(|| { ui.child_window("###ConsoleHistory") - .size([0.0, -27.0]) + .size([0.0, -35.0]) .build(|| { for line in state.logger.arc.read().unwrap().history.iter() { let style = ui.push_style_color(imgui::StyleColor::Text, line.level); @@ -384,8 +485,6 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) { ui.same_line(); ui.checkbox("Wireframe", &mut state.wireframe); ui.same_line(); - ui.checkbox("Bounding box", &mut state.draw_aabb); - ui.same_line(); ui.checkbox("FOV zoom", &mut state.fov_zoom); ui.same_line(); ui.set_next_item_width(150.0); @@ -418,39 +517,21 @@ pub fn draw_ui( glfw_platform: &imgui_glfw_support::GlfwPlatform, window: &mut glfw::Window, state: &mut State, - delta_time: f32, last_cursor: &mut Option, scene_fb_texture: u32, ) { glfw_platform.prepare_frame(imgui.io_mut(), window).expect("Failed to prepare imgui frame"); - let display_size = imgui.io().display_size; let ui = imgui.new_frame(); create_initial_docking(ui, state); - draw_main_menu_bar(ui, state, window, delta_time); - - if state.show_help_menu_about { - ui.window("About") - .size([300.0, 150.0], imgui::Condition::FirstUseEver) - .resizable(false) - .movable(false) - .opened(&mut state.show_help_menu_about) - .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) - .position_pivot([0.5, 0.5]) - .build(|| { - ui.text("3dobs - 3D Object Browser"); - ui.text(format!("Version: {}-{}", env!("CARGO_PKG_VERSION"), env!("GIT_HASH"))); - ui.text(format!("{}", env!("CARGO_PKG_DESCRIPTION"))); - ui.spacing(); - ui.spacing(); - ui.text(format!("Made by: {}", env!("CARGO_PKG_AUTHORS"))); - }); - } + draw_main_menu_bar(ui, state, window); draw_viewport(ui, state, scene_fb_texture); draw_objects_window(ui, state); draw_console(ui, state); + draw_about_window(ui, state); + draw_keybinds_window(ui, state); draw_settings_window(ui, state); ui.end_frame_early();