From 2b41aa71ea4291d3e18f1e627a9b2c5461d2955d Mon Sep 17 00:00:00 2001 From: Guillaume Blanc Date: Tue, 16 Apr 2024 23:47:55 +0200 Subject: [PATCH] Expose motion looping correction setting. --- .../ozz/animation/offline/motion_extractor.h | 16 ++- media/bin/pab_atlas_motion_track.ozz | Bin 6649 -> 6569 bytes media/bin/pab_atlas_no_motion.ozz | Bin 77578 -> 77578 bytes media/bin/pab_jog_motion_track.ozz | Bin 744 -> 744 bytes media/bin/pab_jog_no_motion.ozz | Bin 7503 -> 7476 bytes media/bin/pab_run_motion_track.ozz | Bin 773 -> 773 bytes media/bin/pab_run_no_motion.ozz | Bin 6938 -> 6938 bytes media/bin/pab_walk_motion_track.ozz | Bin 862 -> 862 bytes media/bin/pab_walk_no_motion.ozz | Bin 10583 -> 10601 bytes .../sample_motion_extraction.cc | 102 +++++++++-------- samples/motion_playback/CMakeLists.txt | 8 +- src/animation/offline/fbx/CMakeLists.txt | 4 +- src/animation/offline/motion_extractor.cc | 105 +++++++++++++----- src/animation/offline/tools/import2ozz.cc | 2 +- .../offline/tools/import2ozz_config.cc | 2 + .../offline/tools/import2ozz_track.cc | 1 + src/animation/offline/tools/reference.json | 2 + 17 files changed, 160 insertions(+), 82 deletions(-) diff --git a/include/ozz/animation/offline/motion_extractor.h b/include/ozz/animation/offline/motion_extractor.h index 263917dcb..9dea7bd92 100644 --- a/include/ozz/animation/offline/motion_extractor.h +++ b/include/ozz/animation/offline/motion_extractor.h @@ -73,14 +73,22 @@ class OZZ_ANIMOFFLINE_DLL MotionExtractor { bool x, y, z; // Extract X, Y, Z components Reference reference; // Extracting reference bool bake; // Bake extracted data to output animation + bool loop; // Distributes begin - end difference to make animation + // loopable. }; - Settings position_settings = {true, false, true, // X and Z projection + Settings position_settings = {true, + false, + true, // X and Z projection Reference::kSkeleton, // Reference - true}; // Bake extracted position - Settings rotation_settings = {false, true, false, // Y / Yaw only + true, // Bake extracted position + false}; // Don't loop position + Settings rotation_settings = {false, + true, + false, // Y / Yaw only Reference::kSkeleton, // Reference - true}; // Bake extracted rotation + true, // Bake extracted rotation + true}; // Loop rosition }; } // namespace offline } // namespace animation diff --git a/media/bin/pab_atlas_motion_track.ozz b/media/bin/pab_atlas_motion_track.ozz index 72b6b025451cec88d13183582a1b6a21df6544c7..34ad3adb885cbdc03ffc984cca20629af3daca60 100644 GIT binary patch delta 2742 zcmYk;3rtg27zgmnqcG(m>JX{rwP+Mse4%2X4<`yi7TKKJ6mdhRI0-P_j0*_diyDP( zqOLwsiinCtHxK~<3$pM~Pzp*10zLq(h=3?6pc7f|_di)iN^XANcY4luzI$%lH=bFZ zIZf6kr_^_>eXNoa6BTXR6opk&hWaBLcXg1>1@(x{To?Da+sf+Cnw2TVca_=x?MnXQ zBjxC!$I64sF6D-EJ?f>le_1tFc&gS~%~zeB;iZ<2na5w0tX8EIho~y_8&qEPn^d76 zN2|6K#i<+v<5e6d82>r$f|93e?Cey9==87Q&ivoWvTtDB-F@W7V0dQG0di9S9DL*` zd5k|izU&xJfk_GkV)R%bb_#q=nn`)CQ7nezBJ9h_b8=yaEjqIP6rAJQKu*krL-THt zpIwEwxz~_KSzvz6B|et|_gRIW2T|dzNDOEzNThu04!F@Ij{HduEHzz99{(D)Jb6ei z7Md^uTS#~xBLzJytlBy8isEtFC?Tj69uMb3$UEd=AIC?dC;xIh2L@HW{$9%j{cPW8d)XNr( zGQUwQ4j6oof*z;~sb#Uu=UeWR53}(O5#i(z=1RK*;(S~hHpI!ycTxe04ir$_K0w}_ zAe!g6pw00L@eR5K7~q?nMm9Xbp}AJ4CHJ3zT@8A2_&T_x(15%jESuX)E?5I6OFGHh zGvM9NZj-l-7;RjiTl7Ib1)?%gz|{0rWK%sHVLO4`jC;n#>B?va18j$$!2;O>9pX}! zycRnkM6r(4FGwN}F$aX~pbqyN$BXL=b=!-ng>u$nA^$nKp1EjkCHW?sz(hNR?8DsD ztno3<^WT|xaKoYFDx?%OCc`b3f34e9q(vly`ps^I0Zj?jgJ`_azl@7q>w0?AMd? zULb!)cAm_eupNGG#!+A;v$snZ-BUaET(@kBk{frx+3%+bIF0`g3emNt>{Rc^FOS6d7TGNwc32)l{!R<;GxsLTa0|Jsvz*DTEy(YAI7&g!|BZG8Lb8Xb z#Rcq?&XqhNdp|~g))fOe{SoYSwTJwko$^B`m9)TvUC3`8iXqQ>hz;y0E+yCOfiqoO z72@;HRey?r$Ej`_;5HdP96UhYD}~$YhR89l@K1q*`Lp^;m|r|l+@HBVz{f}*X?WRG4r z))SsO3~x9pqx|{lcz<;-=6j}v9r*@JOxT9NvLj~X7Up^TVZRFGWrH}yj{@QKk$7tK z4)C=$M|%HrQ#j8Q1ytF>Gj&qR?>Y;wxE_w@a(6cZor_Irz#<7+mh~3DkWy7}QQZmZ Yn4K>?W#e)3|9>N@)2;2*J&rd20!vxQ8UO$Q delta 2824 zcmX}udsIzX90%~-C<%F#lE)N;?uj9kZ zygar(^lJM4iC3qPO4G0w7h3l1)5qxB%*}YPhr2QJ>;RKZOIIbJe2Ov2!pFEfeVXx7 z{%oUruD{XJBha{F`Euj3wAIFT->)$Wp*H*p^{AI3KgFuvdkyll$eaFUZzOzS2EE4g zE8Jxo9i9;mJC3HU5A1}8j#3zCHUBpRVh7UpbE4p7db<6#-(imuxfvlA&A0$touO-D zZ^DiV^!Wa_rPaBKbpNPZ@WL$mpR6ocDODKAi9V0OCuv|uG;_v z`WMirYi!|HPv{|=%CQ00H}tZSSU6uhfTz2?Jby>kfOP6d&eyeZ z@@$|Y0zWa3@7`Jlgr_!P<(hqRg8F$uyPoONnROgL=D`hgY_+!FmMA?ow6B)=jCBS$ zSzG^UkQLmp1TM2vae11oFFt4!?jLl;2HYySfbE1Fxa)ShBHP~}H~5ZsLS5PwcS6`( zGJj@bGV*iJ<4&pR6R*P}l%CeQ1lBE}m7sEitS|KI8HnqC4+Cypp(oYimWro37RY&@ zfP9?x43lgI!&6I{ui9RR4X=1h&nb(9KeyZ#Ef-I<4zE4;&_mW&tF%DKjyq`a60ODE zL(0%%RqIf1F)%}pH?LuVo%5!{wX~?q83j*b%SBF+Evyw3Yh4~;{lly$y2p-J@Qmm3 zQi;YfF`lx(h({R*Str<{n*Px>fg5CpM2EEJI6%F2BAe-L*;4|8Z zTzU?~iF`D#bBII-Mra%OwrGf<;Q+!(aJ{xSPQm{M<5G*;8+ggbH-;6FU-bzCzPz+r zzL(-qZDTL_`6!VNSZW0q#L+HMP2m?u=#4j+@Ai_Ooo9ypu}}0hD*<0=!3{jVS&Mpq ze=Xf`|0|xpMPQaM2i!M%0xyZD^?~>?5rx(C#DFy9{oHuMl_4>3U+vc}F8djz^eCitj!Y0m8mhRxq|yuL(% zowd9!d;=U=%zV)NrYJb$B>mcQ7@XKcG0OU)-_`>NY$@dl%lF{dQ0zHI4=Kjar-
o3E0D-`Z1c=9>~iX-W?(E;$% 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 R5cyHT=1IplPa5qG|35`AI&1&{ diff --git a/media/bin/pab_jog_motion_track.ozz b/media/bin/pab_jog_motion_track.ozz index ba426a85e0311d9a49d4fe571de91912cf703a58..dc3cdabab5e89591e6f7723d8306df08757da7a8 100644 GIT binary patch delta 336 zcmaFC`hs;s4r9I8{61R-Ab6TkZx5n{ERTZundj@k{M*;RfcZZ(A^ff|UOSMwJw3Hx z`7=$DV19};gx@482j+*ps{za3S}6hMPgjP>yTo#X`4;oQ{Cb9EZ{C6}aCCuKz{a;5 z%olkF;cGE9f%%uKAr55fK4lGZz`=hIe*eA2dqDihpCJ5sZ}s+q_@+l7{C#N^V17b7 zgfIPSGMIlm1jKh?U|7>T2P|Oc08wz^;sh}NQ#90oU)f;3+GGfyKTQtI|8)++e-SZx H9g`IRDqB(5 delta 336 zcmaFC`hs;s4r9IO{61R-Ab6TkZx5n{Esuivndj?3{D#}tzkvBaG$H)1FkU;5x;;I$ zVEOY+l3;#{G=$$IDhK9=y{iGs-&rXE=Fe1y$UDb!gZXCj!2J4#WpCbsEpT*!Sir`& z8_XAZ2jOcmHG%n;tHBOzVCX(&4RXN2e-H=v-&?!~$ZdH13Bq6cR&OteZ+Zm6-iMML<%bAF$ zF)o|uqF4qVCOiDu=h)RJYvjkEhM(6j%06MO5ucC)PXkG(xoDhu*^<3ma< zTaziX?`JNXEaMLqip(z*%w`V$w!lBVkfg{G%S6v<`FJ@|R8_@8h6UBdCpjvrHQk*T zP1kgtj%Hct=pIr!-|3j{@)!RhJnfiZ3j~5Z>zJ5vmg3YXOTzyq$o5C%7dK|_QQ z9--0VyNWiYbodvlp&Gmp!9$3E@1F?6JgjU%1%eY%F5rd=gbtMms^Jm7Ri^@q5guY> zN9Be}zTRo1D76GZN%X`jsX`HgijiL-h9hqrR>^XFqYEl7D1b7k0SANxoI=x5LioQG zvow*IAUb-91$U?|sqt~>ju62-XmMe|ikNAFi-DTB5$X_XesnE=Q>GVr`UF>+j6hu} zPHU&xo%jWsb6p3|ua3VM&Ad^4%w~fz-mT5z_3j`W$t-d&91pdy5&lLyrFgeI`1*|R z)!XewYaf4KdN}h@Z(`EPD35qI^CRAE_Vhd{Vzlv}-lpL2;A#1F=Z5AFKSs8``jjoa zH#(LZ%^e%y%YC(W=eM(Rwv+GkwJFbk&62P4Prh6B`^pL1flhv@cme-h+-A>szDTW| rd|gRH=4eSBQ-)oO;3>cBZ{?vt8}A7;DQBcRxp(|YAeC7a9FczmX{!w5 delta 1432 zcmeHG-Afcv6hC)9*43Hanf-9rkJ8=NoprT#)xOoT4=Bij475Oqh=>AhiwJs&z4%ap zMa8M7kfNlpdPp{?NRXb2h{T5|q9CIu6{3g;%C>W7))I>Tg3fQwz2~0$J0JJJ+-V+b zKATWKF@TfjhAW3R_Dc!?Fg&2tW;t0m-2=X4b0b+0Si*queTyZ|z_3;?$Y1NNleo-Z zCKyP3VJ+RlbgC`XF|;Eofdm;#BF|b&CVo?T{KAdF7fD7rPgy|xOhT-w^hAGe6(ha^ z!xrNTzhvJ&N@YU&{=^Y-Fir=sN(*SAq+2F-Etj*+-ToE5jw zYE1PnCzE$BXV{=bs^D~bv;iQqNF;G&OWDqrp{v6FKR0`B*KXabD4#R8$7-agFgN_5 z*0HW!h3gp86-ALr6`2GFi&|I_zAXwZa~BIKv87N(#`0#*xsW&)RO7XUPo)QX&!#L)ipPO=bvw9QsKM(~EM8Qic;4AD= zY}5mIykv8Sk9>ba9O4jw8q$9u25Z6i1rOEoU{njloQx(^6Z|%W6{JcI+Y8mWT}x+$ zFKFM32(5s6dlwNRV>NY8{5cRYBFeDub#Ori!GdC_w4vLph^i!x<8#(hn1zV;qN;af zfA=q}8Zdt$1_Hm|Z`$=5X`SsfWzXB`%_wx}yf;E|5yEUeXBWA|OHHbz)4#{tJh?Hw zoXOwvGdShxU|(~qyz6I=D{;gqV_)&PaX~HF@$}PX%$U8ocV;V-Hnm{Hzsp5o`STWX z|6Z3*xsf=L!D)YMmG*H!8Sm?EfA{(Op4XG(#SdqG5bx~E;Wbh$-Ggfab$Bq)qb`O? z`65mP9^jSWI#;*0Og_|y?}I(qRoal_rA!$exa($?WdtBc<8(^#t VkA_>-2ktNVS9m|1%{7%t>Q7GZ5d#1K diff --git a/media/bin/pab_run_motion_track.ozz b/media/bin/pab_run_motion_track.ozz index 2437f49764bace5b8fee0a6d63255a9d0b797e2f..59f98e026be29330ca9de2c41fc4afb197dfdd74 100644 GIT binary patch delta 276 zcmZo=Yh~MTlCeI*{D>_B5VT&Zvj@?QRzJY}>oO32*I7P0koc_nTCn`Ctvq0U@FWPo z$^I2s{fj_|dV8OfVE(E@5dL%i1z>)cc|BM^n@j|lzheoQuf%X;Zr>h|1;XDT3U;5j z+6&^J+YR9dP0nJHPn>+d3@rM(10wof#2(CFwgbXHH}4_X6qV-?e%$=yyFsR${0HIR cNnB|I;{X2$;eX(*20P_B5VT&Zvj@=)RzJY}t1=LN*I7P0knpVfTCn`itvnz;L+~UB zztR2`SpBm=hFlbb&F@ArBcSk%eT58_8T2Mur*FWPuxI z6@zSU{~-vvp`CN3?N#jNhe2GT7~kmN!!&HsebGdSiNr)Bk9z)>y2T~?pdWUA_nv$H z=lScoy(2{NzK~KNSuo9>_W3#GYVxpa*6h z%$5lSkdUYlErrY@6M{#bA7;Jo9LQ6MzzAV~cdi1XO3!NYE?2FKWj3?Ud&Gb<4#Tyc z7f%K@UdI3tXJkUA90iW4_jH}f9=n;#3NFc1t~wQ<5U&ZDDYxEf!b}B}GD6~%gZ<*; z!6q(W=r2ZFY%V6$6QAmU=3+K`z;pFM5IgeOde)`W-r?=_qmAYCP<&#~{n`0FSKf1f zc7D&5cl^JdcLx(S<#%>2##Rj-`E1R*!<+^r`HkHDBIs|*w2TUVr{D-*QN>GY`@Huh ze^Yo-S0bDYo&mKax~FMAm!z8ZwZC;D`~|J$()Q0P%u>TE!`?}MxBDaCyKkNjAJpmu zL$F$zC7mB^_g?b%iZB3XMi%0$%?Tvq=^+iis%{({0Oaw$P9|J@UEQP6~1%7fhgz zp$;@q3pFtqzyL*vEX>G4WHL|-|M(^y=wK6`SjIXs!6?UBrdbB7K+sdXol5-neJbD( zX$Upm8*tKwyI=*WzD*oWXr-dZPYgQ7?FulW>s^W zodKD6ARBW{s2Qv=WXrSGHJi#{!kHS#Kz#=pcxEufp8uR}cutJLii{0v&?9g`)TLX{ zu15R9%TWU5V=FX6pOo@vbTzuxQwdAa>1pOa`0+A&X2(}(gWeHG;F-u#y^Saz`~9dT zN>3T=|Gfb<8r!*D1(u6p2jD;vF7KQkXmx0Dg?TIrWrkzTsj`b73b`l$V!kCA@1 zw9-~&tf$Q0+qw`Ku=iK^Uz@5sOXsZnlS}3g%zxX*V(*t^AD;OB*J>|Gh3Fb>vE`YW gyE^qR)N9e!VQbyeMml2a<|>E!YPUKf$%zW(-&&l_8~^|S delta 1750 zcmcgsdu)?c6hF6H-|hPD-W}~|XG!}N<{peLL&kI)b=gA~M@Wb|l8MP!SOUhxtT%%q zWDdm3vauOKi|E#ej)IA93W)`vZ4jt(; z6WY@T_l9+70aKZT0=)1j00Et=#*sSRy@pT;Tq{8f^*q9Tei1fWTgN-xs6vm6pna1o zgAyq9%?(YQ`UdSmLSMrhc)Ns4!FO|@+eX}k5<2N36yO98gUiA$>&}urXdyJ;iHG=a& z-t+%@-WRbbH~!Oey3%UER^!+jlPMuCK9Tz$1p8fDSX8U37o4$ASiEe>ChK+3Syl2R&Cp=gfPeRNWHq9Uk|` z!{KPXR4H5>TEN&rj4d@MqQA1Mvh1=hViEeIA8DS5+YH9)sc^`18NbvW(IhDp&P9hb z)$yNlhy5er^jp^vTTmI}>^V}$w#)5A!l>NMCgi1=QmItNzLVD#DOF0nN~O}^RI4=_ zHG*HFz?Ic1%FFdS9l!oP4C!ISVc-H6!(C0>nps3)OI%Up5nl=DLB4_jC}0aTuAm&U zpd4Wb15P`v;$RllQ(i4_c)$U=zu~|<4ulbk!6c#>wPLU`rrOr2#LT>k4WYzY2r8WS zq5z``z%0UuHFcmyUmlf@P~vo=r2wH7CD#n1#^VZDtGYFqg`7FJ9imM0w1a@W^eg-v zSj8dMaDWBj0P8(4f(D_!5B&Tbx|KD(62 z*R3bA<5gF`{b4`5pl^`9tnd6j+Rqe*TB!{GexD|q4Y|a8re$8XWic9AfIRS~YR2Ag znoY7-j5ZnPbfk5mcz%VpL}aI>#(fJsY>olesz$$H01q>ayhkm_+VhYlO414C3371 F>0h~y!K{aYU+!5e5dZCe2tV@4)7>C`GE{!6?Cm`ue*X)w zd_6;fuprn~53fNqSe{e`^LhZar9k-m_w|DLw~|17W(J1B$x6&N0Bb}~1^@s6 delta 293 zcmcb|c8_ht14hAyo%))48Gu0OM7=$bYMA_y(V$-O@EjYEAj221Aj6es0U&UpuxX8vVm_+H=pjJ?D3S z_nf=zbou4-&0c1b2!P0&zh~?#IY)X9);~S|0I1` z`y3w)M0}Np;wkWmheq~+z=^GUJ~-{8c~OXO@Zi)cFU14k4_vHYVx@ct2<-FVviC^Y zSN9Mrowl-X;>-tp~DdLgZO~Pz@c0Ks!0FvE&%4O8AYG8VULDgVoT_M z@9sZ0H}t=E_n(`a^WVBFVtrcNyOG_653_pr7q!RUu8W#v^ZiZH7#-VdYtR*Sx2;z8 z7IwEqvX9oDYVC+_g=<(JbrkMoXK_bs`PNu;r+I?k94*|g>1fD43Z;^6&`yCuVnV5@H?{3-+uQi^FPjS+H19K*Ty+>~_1uhQv-j*t6L(VLWvX z<$-h3oRqh7P1+bLR1Av2WH5o~DBuMgQ8f@$39y!dnV=MCh!!R?geYkh0IM8KC2J@Zo(Fy131l7W1jfea49~)mFVPF(_M=Eg zMgi@(5vg7T;vEDOD37!p&VifdPBfMM$UX@B4%U~Lg z;Ipa%>P^_9DtyF+@NlmReYwOfuo(Rv1UX=eNlm=~_$ ztp5tIx${YF9>qBO&IWXt4)3 zL%(4Hv*OZc6Bom3L#c7rwM*ij@blN-J@Dmgm}ybYPxLX_vRB}FL&4bAZ5M)9&Q58# zQ7Z0p-q}l6L@KT{($j0}&09o;%hG%DQ~PV+%}d=2Lb%l7e<^p|%@#P-SdmqE-7We$ zT&UV5+Ei6{A}(1`1~U<#XA0v z{)^&|H$t81Iph51Q$gcdU3luOxxL44-(*VPIC8HHoC(9%P0N{&dPb)-cHTC5DUG|% zTvmO$Sge=4TepI?#s|W1LdKI;spL1-6t0(CXJ^+PNog9W-gx|ioGD%6I2BL1Tm#!P z=CUGDZ%oBQrFphl{CG^k7pFeOUaqZFuxvb1(fcj4Y3Y?7XG%R>ZJzB~^-lHl?dNtj z7L`8N*UxmI>^A8ZrmMC_w@&;=xJ&orz_kdxV|LI@W1+>eh{bnZ_ZY0PxH|V*W>C~O z@Se4T>^A4Zr)|0H!M9$0BKI6@v>BZ>%9F8Zr2fY1qV|`LWcj;F*5c|PWBbRM^>Xl7~4!@*W~!O?>f T_+a;0*y&u(5VteSmBai6B`1gr delta 2736 zcmcIjYj6`)6h6DjZu-b$^Gmb!Z26fbmJOoEk%xY~2 zZK=wwMIES*icYcA!T6Yw`hX52GmJP$ha$)f`1sL^Z&0+P_1xVof}@W9@SN;9=iYnH zcfWh?%}D)l{SzUsN(GR7@`wB#HAmSvKzs3E%a>i|sHde=j76M@u-Fl8O4y-4s@n?U ziV{FMR!Z$}+je&w!_!I`Q7xJ;VMGS*C<9Q5hTwGH#LG*I7+}Q1Ar=@Yp2TAQ$;cJ9 zy50q06|my6AS(h~DcLE}15a&xX*kS^s<_x5WPpWbAqJNt(GzV;-AovPU`LPzwfI~R zF#a&I%ywsJHO&-{#Fu6&Yvn-C8pENA zfu1Bj@S*PhzAo}0A7=KDt^9oR?iR5--M7_Q6>;`eY&P}dpMd0Qrg{z-gORuUKJF;% zziE@cvcIRjco+UT-t1eGyf|`O-`8Z3riNk2E1GIXO-40kW{%@|j_1(fhoa$m63Hry z=VnvM$&suS(_wB771_$6HdmXAA~`9-SgY0P(EK4fyGuD8r1R%X0w;NE~OQ4P5kvSXb0LiAsV+Pd9ZkYKWj2HvG}A(Bg$ym39BxPi!Ad*=pG|>+RLK)MKNBEt zpq)hy>s;{~ETv%O=O9g(r@&KhghEsr$yQ!FKc*{Y{AB9#x)GbDG(+*UxjvYhnfFu7 zxKt|No?_{Cg;HPXLH?=>N=NA}3YbgBl#sHK5mGp&c$5S^nzABoN;j4&1t8z)9V)zF zl4Gci%Ma$yb_s&4g4?7bO3Z?vOwV?%-}K}%83R5zs($IwDOmIY$?IOE;zd;st9V?E zNGbG%DrJhFDK$R*(tR%$E@L{#vqFVVKo<1C)LO`K zoju*O}>o!$LuYQAL(!==*O z)7O($tA}GF$IsYEzqQ)oAJdJ$m~8%fWPIqceZOA5y=}!OB_EtAY)+-EUWV)5bUZdn zeD*4C$(P}(=gF=1T6gZ~dhTdq$&Xv!+4B(PU0L!4yH~qn^}S!cJN=IJWS_l!>ecP1 zYSoARziww6lZ~Sr*f}j1e`i&HlxTBqR8_3F=wMm)STi~O)xhF71@J{o(OXabOv)XN zWRt^9b~-8@*2J~*7ar=F2&k>%Ph%(4Z>}3WTL+&eNOs;W+S8q4Y{rp&XHIjq>&YW| zE4liEM>S2UGkGD#eRJnf%yA^p>a1HnT&=cgwzS^Mx+lgHzM+Y%k!Dhwf4$=;^;}IX z(Wd#4Z)hFlUmm||-M-^Gu68Nu$zP~#QeT(Wi`CgT@FOim0~dwrf@Fy(3$aC+o6?iGF|1FYz&3SGusJ$Inl^-rSVSrXNkV zoq9yod)Hu5WM|D<+M>_q&2+xS?ied7R{uPY?DjMgzqgz$@~%t`dj*#Jwo8l+kdwZH TWRHI(H{O+u?M_t|mTDoLabel("Components"); - rebuild |= - _im_gui->DoCheckBox("x", &motion_extractor_.position_settings.x); - rebuild |= - _im_gui->DoCheckBox("y", &motion_extractor_.position_settings.y); - rebuild |= - _im_gui->DoCheckBox("z", &motion_extractor_.position_settings.z); - - _im_gui->DoLabel("Reference"); - int ref = - static_cast(motion_extractor_.position_settings.reference); - rebuild |= _im_gui->DoRadioButton(0, "Identity", &ref); - rebuild |= _im_gui->DoRadioButton(1, "Skeleton", &ref); - rebuild |= _im_gui->DoRadioButton(2, "First frame", &ref); - motion_extractor_.position_settings.reference = - static_cast( - ref); + static bool position = true; + ozz::sample::ImGui::OpenClose ocp(_im_gui, "Position", &position); + { + ozz::sample::ImGui::OpenClose ocp(_im_gui, "Components", nullptr); + rebuild |= + _im_gui->DoCheckBox("x", &motion_extractor_.position_settings.x); + rebuild |= + _im_gui->DoCheckBox("y", &motion_extractor_.position_settings.y); + rebuild |= + _im_gui->DoCheckBox("z", &motion_extractor_.position_settings.z); + } + + { + ozz::sample::ImGui::OpenClose ocp(_im_gui, "Reference", nullptr); + int ref = + static_cast(motion_extractor_.position_settings.reference); + rebuild |= _im_gui->DoRadioButton(0, "Identity", &ref); + rebuild |= _im_gui->DoRadioButton(1, "Skeleton", &ref); + rebuild |= _im_gui->DoRadioButton(2, "First frame", &ref); + motion_extractor_.position_settings.reference = + static_cast( + ref); + } + rebuild |= _im_gui->DoCheckBox( + "Bake", &motion_extractor_.position_settings.bake); + rebuild |= _im_gui->DoCheckBox( + "Loop", &motion_extractor_.position_settings.loop); } { - ozz::sample::ImGui::OpenClose ocp(_im_gui, "Rotation", nullptr); - _im_gui->DoLabel("Components"); - rebuild |= _im_gui->DoCheckBox("x / pitch", - &motion_extractor_.rotation_settings.x); - rebuild |= _im_gui->DoCheckBox("y / yaw", - &motion_extractor_.rotation_settings.y); - rebuild |= _im_gui->DoCheckBox("z / roll", - &motion_extractor_.rotation_settings.z); - - _im_gui->DoLabel("Reference"); - int ref = - static_cast(motion_extractor_.rotation_settings.reference); - rebuild |= _im_gui->DoRadioButton(0, "Identity", &ref); - rebuild |= _im_gui->DoRadioButton(1, "Skeleton", &ref); - rebuild |= _im_gui->DoRadioButton(2, "First frame", &ref); - motion_extractor_.rotation_settings.reference = - static_cast( - ref); - } + static bool rotation = true; + ozz::sample::ImGui::OpenClose ocp(_im_gui, "Rotation", &rotation); + { + ozz::sample::ImGui::OpenClose ocp(_im_gui, "Components", nullptr); + rebuild |= _im_gui->DoCheckBox( + "x / pitch", &motion_extractor_.rotation_settings.x); + rebuild |= _im_gui->DoCheckBox( + "y / yaw", &motion_extractor_.rotation_settings.y); + rebuild |= _im_gui->DoCheckBox( + "z / roll", &motion_extractor_.rotation_settings.z); + } - rebuild |= _im_gui->DoCheckBox("Bake position", - &motion_extractor_.position_settings.bake); - rebuild |= _im_gui->DoCheckBox("Bake rotation", - &motion_extractor_.rotation_settings.bake); + { + ozz::sample::ImGui::OpenClose ocp(_im_gui, "Reference", nullptr); + int ref = + static_cast(motion_extractor_.rotation_settings.reference); + rebuild |= _im_gui->DoRadioButton(0, "Identity", &ref); + rebuild |= _im_gui->DoRadioButton(1, "Skeleton", &ref); + rebuild |= _im_gui->DoRadioButton(2, "First frame", &ref); + motion_extractor_.rotation_settings.reference = + static_cast( + ref); + } + rebuild |= _im_gui->DoCheckBox( + "Bake", &motion_extractor_.rotation_settings.bake); + rebuild |= _im_gui->DoCheckBox( + "Loop", &motion_extractor_.rotation_settings.loop); + } if (rebuild) { if (!ExtractMotion()) { diff --git a/samples/motion_playback/CMakeLists.txt b/samples/motion_playback/CMakeLists.txt index 225a7fc02..236776fc3 100644 --- a/samples/motion_playback/CMakeLists.txt +++ b/samples/motion_playback/CMakeLists.txt @@ -3,8 +3,8 @@ add_custom_command( DEPENDS $<$:BUILD_DATA> "${CMAKE_CURRENT_LIST_DIR}/README.md" "${ozz_media_directory}/bin/pab_skeleton.ozz" - "${ozz_media_directory}/bin/pab_atlas_no_motion.ozz" - "${ozz_media_directory}/bin/pab_atlas_motion_track.ozz" + "${ozz_media_directory}/bin/pab_jog_no_motion.ozz" + "${ozz_media_directory}/bin/pab_jog_motion_track.ozz" OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/README.md" "${CMAKE_CURRENT_BINARY_DIR}/media/skeleton.ozz" "${CMAKE_CURRENT_BINARY_DIR}/media/animation.ozz" @@ -12,8 +12,8 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E make_directory media COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/README.md" . COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_skeleton.ozz" "./media/skeleton.ozz" - COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_atlas_no_motion.ozz" "./media/animation.ozz" - COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_atlas_motion_track.ozz" "./media/motion.ozz" + COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_jog_no_motion.ozz" "./media/animation.ozz" + COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_jog_motion_track.ozz" "./media/motion.ozz" VERBATIM) add_executable(sample_motion_playback diff --git a/src/animation/offline/fbx/CMakeLists.txt b/src/animation/offline/fbx/CMakeLists.txt index e87dba170..629cb0140 100644 --- a/src/animation/offline/fbx/CMakeLists.txt +++ b/src/animation/offline/fbx/CMakeLists.txt @@ -48,8 +48,8 @@ set(build_configurations "/fbx/pab/hand.fbx\;{\"skeleton\":{\"filename\":\"pab_skeleton.ozz\",\"import\":{\"enable\":false}},\"animations\":[{\"clip\":\"curl\",\"filename\":\"pab_curl_additive.ozz\",\"additive\":true,\"additive_reference\":\"skeleton\"},{\"clip\":\"splay\",\"filename\":\"pab_splay_additive.ozz\",\"additive\":true,\"additive_reference\":\"skeleton\"}]}\;output:pab_curl_additive.ozz\;output:pab_splay_additive.ozz\;depend:pab_skeleton.ozz" # Library data with root motion - "/fbx/pab/atlas.fbx\;{\"skeleton\":{\"filename\":\"pab_skeleton.ozz\",\"import\":{\"enable\":false}},\"animations\":[{\"filename\":\"pab_atlas_no_motion.ozz\",\"tracks\":{\"motion\":{\"enable\":true,\"filename\":\"pab_atlas_motion_track.ozz\"}}}]}\;output:pab_atlas_no_motion.ozz\;output:pab_atlas_motion_track.ozz\;depend:pab_skeleton.ozz" - "/fbx/pab/locomotions.fbx\;{\"skeleton\":{\"filename\":\"pab_skeleton.ozz\",\"import\":{\"enable\":false}},\"animations\":[{\"filename\":\"pab_*_no_motion.ozz\",\"tracks\":{\"motion\":{\"enable\":true,\"filename\":\"pab_*_motion_track.ozz\"}}}]}\;output:pab_walk_no_motion.ozz\;output:pab_jog_no_motion.ozz\;output:pab_run_no_motion.ozz\;output:pab_walk_motion_track.ozz\;output:pab_jog_motion_track.ozz\;output:pab_run_motion_track.ozz\;depend:pab_skeleton.ozz" + "/fbx/pab/atlas.fbx\;{\"skeleton\":{\"filename\":\"pab_skeleton.ozz\",\"import\":{\"enable\":false}},\"animations\":[{\"filename\":\"pab_atlas_no_motion.ozz\",\"tracks\":{\"motion\":{\"enable\":true,\"rotation\":{\"loop\":true},\"filename\":\"pab_atlas_motion_track.ozz\"}}}]}\;output:pab_atlas_no_motion.ozz\;output:pab_atlas_motion_track.ozz\;depend:pab_skeleton.ozz" + "/fbx/pab/locomotions.fbx\;{\"skeleton\":{\"filename\":\"pab_skeleton.ozz\",\"import\":{\"enable\":false}},\"animations\":[{\"filename\":\"pab_*_no_motion.ozz\",\"tracks\":{\"motion\":{\"enable\":true,\"rotation\":{\"loop\":true},\"filename\":\"pab_*_motion_track.ozz\"}}}]}\;output:pab_walk_no_motion.ozz\;output:pab_jog_no_motion.ozz\;output:pab_run_no_motion.ozz\;output:pab_walk_motion_track.ozz\;output:pab_jog_motion_track.ozz\;output:pab_run_motion_track.ozz\;depend:pab_skeleton.ozz" # Robot user channels "/fbx/robot.fbx\;\;config_file:${PROJECT_SOURCE_DIR}/samples/user_channel/config.json\;output:robot_skeleton.ozz\;output:robot_animation.ozz\;output:robot_track_grasp.ozz" diff --git a/src/animation/offline/motion_extractor.cc b/src/animation/offline/motion_extractor.cc index f1eab4e33..525620e1b 100644 --- a/src/animation/offline/motion_extractor.cc +++ b/src/animation/offline/motion_extractor.cc @@ -116,37 +116,51 @@ bool MotionExtractor::operator()(const RawAnimation& _input, const auto& input_track = _input.tracks[root_joint]; auto& output_track = _output->tracks[root_joint]; - // Baking reference + // Compute extraction reference auto ref = BuildReference(position_settings.reference, rotation_settings.reference, GetJointLocalRestPose(_skeleton, root_joint), input_track); - // Copies root position - _motion_position->keyframes.clear(); - for (const auto& joint_key : input_track.translations) { - // Takes expected components only. - const math::Float3 mask{1.f * position_settings.x, - 1.f * position_settings.y, - 1.f * position_settings.z}; - const math::Float3 motion_p = (joint_key.value - ref.translation) * mask; - _motion_position->keyframes.push_back( - {ozz::animation::offline::RawTrackInterpolation::kLinear, - joint_key.time / _input.duration, motion_p}); - } - - // Copies root rotation - _motion_rotation->keyframes.clear(); - for (const auto& joint_key : input_track.rotations) { - // Decompose rotation to take expected components only. - const math::Float3 mask{1.f * rotation_settings.y, // Yaw - 1.f * rotation_settings.x, // Pitch - 1.f * rotation_settings.z}; // Roll - const auto euler = ToEuler(joint_key.value * Conjugate(ref.rotation)); - const auto motion_q = math::Quaternion::FromEuler(euler * mask); - _motion_rotation->keyframes.push_back( - {ozz::animation::offline::RawTrackInterpolation::kLinear, - joint_key.time / _input.duration, motion_q}); - } + // Extract root motion + // ----------------------------------------------------------------------------- + + // Copy function, used to copy aniamtion keyframes to motion keyframes. + auto extract = [duration = _input.duration](const auto& _keframes, + auto _extract, auto& output) { + output.clear(); + for (const auto& joint_key : _keframes) { + const auto& motion = _extract(joint_key.value); + output.push_back({ozz::animation::offline::RawTrackInterpolation::kLinear, + joint_key.time / duration, motion}); + } + }; + + // Copies root position, selecting only expecting components. + const math::Float3 position_mask{1.f * position_settings.x, + 1.f * position_settings.y, + 1.f * position_settings.z}; + extract( + input_track.translations, + [&mask = position_mask, &ref = ref.translation](const auto& _joint) { + return (_joint - ref) * mask; + }, + _motion_position->keyframes); + + // Copies root rotation, selecting only expecting decomposed rotation + // components. + const math::Float3 rotation_mask{1.f * rotation_settings.y, // Yaw + 1.f * rotation_settings.x, // Pitch + 1.f * rotation_settings.z}; // Roll + extract( + input_track.rotations, + [&mask = rotation_mask, &ref = ref.rotation](const auto& _joint) { + const auto euler = ToEuler(_joint * Conjugate(ref)); + return math::Quaternion::FromEuler(euler * mask); + }, + _motion_rotation->keyframes); + + // Bake + // ----------------------------------------------------------------------------- // Extract root motion rotation from the animation, aka bake it. if (rotation_settings.bake) { @@ -169,7 +183,44 @@ bool MotionExtractor::operator()(const RawAnimation& _input, } } + // Loopify + // ----------------------------------------------------------------------------- + // Distributes the difference between the first and last keyframes all along + // animation duration, so tha animation can loop. + auto loopify = [](auto& _keyframes, auto _diff, auto _lerp) { + if (_keyframes.size() < 2) { + return; + } + const auto delta = _diff(_keyframes.front().value, _keyframes.back().value); + for (size_t i = 0; i < _keyframes.size(); i++) { + const float alpha = i / (_keyframes.size() - 1.f); + auto& value = _keyframes[i].value; + value = _lerp(value, delta, alpha); + } + }; + + // Loopify translations + if (rotation_settings.loop) { + loopify( + _motion_rotation->keyframes, + [](auto _front, auto _back) { return _front * Conjugate(_back); }, + [](auto _value, auto _delta, float _alpha) { + return NLerp(math::Quaternion::identity(), _delta, _alpha) * _value; + }); + } + + // Loopify rotations + if (position_settings.loop) { + loopify( + _motion_position->keyframes, + [](auto _front, auto _back) { return _front - _back; }, + [](auto _value, auto _delta, float _alpha) { + return _delta * _alpha + _value; + }); + } + // Fixup animation translations. + // ----------------------------------------------------------------------------- // When root motion is applied, then root rotation is applied before joint // translation. Hence joint's translation should be corrected to support this // new composition order. diff --git a/src/animation/offline/tools/import2ozz.cc b/src/animation/offline/tools/import2ozz.cc index f11899663..0de72232b 100644 --- a/src/animation/offline/tools/import2ozz.cc +++ b/src/animation/offline/tools/import2ozz.cc @@ -161,7 +161,7 @@ int OzzImporter::operator()(int _argc, const char** _argv) { << std::endl; return EXIT_FAILURE; } - ozz::log::Log() << "Existing importer successfully." << std::endl; + ozz::log::Log() << "Exiting importer successfully." << std::endl; return EXIT_SUCCESS; } diff --git a/src/animation/offline/tools/import2ozz_config.cc b/src/animation/offline/tools/import2ozz_config.cc index b35030b34..c7f21dacf 100644 --- a/src/animation/offline/tools/import2ozz_config.cc +++ b/src/animation/offline/tools/import2ozz_config.cc @@ -341,6 +341,8 @@ bool SanitizeTrackMotionComponent(Json::Value& _root, } MakeDefault(_root, "bake", true, "Bake extracted motion into animation."); + MakeDefault(_root, "loop", false, + "Distributes begin - end difference to make animation loopable."); MakeDefault(_root, "raw", false, "Outputs raw track."); MakeDefault(_root, "optimize", true, "Activates keyframes optimization."); diff --git a/src/animation/offline/tools/import2ozz_track.cc b/src/animation/offline/tools/import2ozz_track.cc index 245b98953..d1ab7ea2b 100644 --- a/src/animation/offline/tools/import2ozz_track.cc +++ b/src/animation/offline/tools/import2ozz_track.cc @@ -351,6 +351,7 @@ ozz::animation::offline::MotionExtractor::Settings ProcessMotionTrackSettings( "Reference should have been checked during config validation"); settings.bake = _config["bake"].asBool(); + settings.loop = _config["loop"].asBool(); return settings; } diff --git a/src/animation/offline/tools/reference.json b/src/animation/offline/tools/reference.json index 51bd82483..88e5ce953 100644 --- a/src/animation/offline/tools/reference.json +++ b/src/animation/offline/tools/reference.json @@ -75,6 +75,7 @@ "components" : "xz", // Components to import, can be any composition of x, y and z. "reference" : "skeleton", // Root motion extraction reference pose, can be identity, skeleton or animation. "bake" : true, // Bake extracted motion into animation. + "loop" : false, // Distributes begin - end difference to make animation loopable. "raw" : false, // Outputs raw track. "optimize" : true, // Activates keyframes optimization. "optimization_tolerance" : 0.001 // Optimization tolerance for the optimized track @@ -85,6 +86,7 @@ "components" : "y", // Components to import, can be any composition of x, y and z. "reference" : "skeleton", // Root motion extraction reference pose, can be identity, skeleton or animation. "bake" : true, // Bake extracted motion into animation. + "loop" : false, // Distributes begin - end difference to make animation loopable. "raw" : false, // Outputs raw track. "optimize" : true, // Activates keyframes optimization. "optimization_tolerance" : 0.001 // Optimization tolerance for the optimized track