From 323fc0a7982f26448318ff502a34152e92a4f868 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Sat, 6 Jan 2024 20:21:04 +0100 Subject: [PATCH] Add glTF animation support --- .gitattributes | 2 + doc/lua_api.md | 7 +- games/devtest/mods/gltf/LICENSE.md | 2 +- games/devtest/mods/gltf/init.lua | 26 ++ .../mods/gltf/models/gltf_simple_skin.gltf | Bin 0 -> 2712 bytes .../gltf/models/gltf_spider_animated.gltf | Bin 0 -> 239030 bytes irr/src/CGLTFMeshFileLoader.cpp | 336 ++++++++++++++---- irr/src/CGLTFMeshFileLoader.h | 38 +- src/unittest/test_irr_gltf_mesh_loader.cpp | 87 ++++- 9 files changed, 419 insertions(+), 79 deletions(-) create mode 100644 games/devtest/mods/gltf/models/gltf_simple_skin.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_spider_animated.gltf diff --git a/.gitattributes b/.gitattributes index 06b76c6c8..ecd9a7a29 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,5 @@ *.cpp diff=cpp *.h diff=cpp + +*.gltf binary diff --git a/doc/lua_api.md b/doc/lua_api.md index 6cb578810..0596c1e2f 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -294,7 +294,7 @@ depends on by supplying a file with an equal name. Only a subset of model file format features is supported: Simple textured meshes (with multiple textures), optionally with normals. -The .x and .b3d formats additionally support skeletal animation. +The .x, .b3d and .gltf formats additionally support (a single) animation. #### glTF @@ -307,7 +307,10 @@ due to their space savings. This means that many glTF features are not supported *yet*, including: -* Animation +* Animations + * Only a single animation is supported, + use frame ranges within this animation. + * Only integer frames are supported. * Cameras * Materials * Only base color textures are supported diff --git a/games/devtest/mods/gltf/LICENSE.md b/games/devtest/mods/gltf/LICENSE.md index b0ae5fef5..6c3828a4a 100644 --- a/games/devtest/mods/gltf/LICENSE.md +++ b/games/devtest/mods/gltf/LICENSE.md @@ -1,4 +1,4 @@ -glTF test model (and corresponding texture) licenses: +The glTF test models (and corresponding textures) in this mod are all licensed freely: * Spider (`gltf_spider.gltf`, `gltf_spider.png`): * By [archfan7411](https://github.com/archfan7411) diff --git a/games/devtest/mods/gltf/init.lua b/games/devtest/mods/gltf/init.lua index 294da9145..1a17ac05f 100644 --- a/games/devtest/mods/gltf/init.lua +++ b/games/devtest/mods/gltf/init.lua @@ -27,8 +27,34 @@ do }, }) end + register_entity("snow_man", {"gltf_snow_man.png"}) register_entity("spider", {"gltf_spider.png"}) + +minetest.register_entity("gltf:spider_animated", { + initial_properties = { + visual = "mesh", + mesh = "gltf_spider_animated.gltf", + textures = {"gltf_spider.png"}, + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 140}, 1) + end +}) + +minetest.register_entity("gltf:simple_skin", { + initial_properties = { + visual = "mesh", + visual_size = vector.new(5, 5, 5), + mesh = "gltf_simple_skin.gltf", + textures = {}, + backface_culling = false + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 5.5}, 1) + end +}) + -- Note: Model has an animation, but we can use it as a static test nevertheless -- The claws rendering incorrectly from one side is expected behavior: -- They use an unsupported double-sided material. diff --git a/games/devtest/mods/gltf/models/gltf_simple_skin.gltf b/games/devtest/mods/gltf/models/gltf_simple_skin.gltf new file mode 100644 index 0000000000000000000000000000000000000000..3d6c24a6c680d159a6a46b6ee91a4179f7701478 GIT binary patch literal 2712 zcmcguS#O&_5Pt8^*w+#p@R7t-AJ#`3BF9JVrmCW-!WcUwHXv9#iID$$XJ#=hm>%gv zk?h^sIlj4IHes>j`Y!9}n%u>#bC)nbSh~2=HRJwpuVr+?;wR51>8%?-z~-PO%z5xO z%gBZEJM$h7L}dH%#ui?HXL=3Hzzj`h4lC}_$x7Z0J z6E+#othqHFv5uj!>#;SO&wl8vV`}W%!1{b8sm4KLp1<@Q)X@(FfC0!LJ%8^;vD*cS z8yjqsR8wP*f#(YpF*FDVT(*60OFm0I=fU=UcO!||-i8}DqLc_bTDt8N3%g^7 z;l^{w&-!{JR%cS~!@`c;c2nb=cR9a-h4(qPIA7!X71vLkPmu|$gXl3cm zD1^`cnU?HtQlg39zbNt*8u@F5Mtzk={&R`1(qw;-ndgL`(vYXct8j;|zs8gs?F;P0 zH{~Zc{KuW*L@o=dt9YgrXZ-)^tNG15v3Nb(XPYkNbKP%dC%BnnJuLpdaLA>8x&A~e z^+O%9Hz?teJ&9McTj+<3SM>K|sn!jXK0|M*bx-zA2B2(;SPG-qs|5y^_9}Btf^WdH zdRcA2UO867{MWvgbp+e}{JrU6%XZ=Ihc_Nu(n7j7MLIxwSk=w0}Y&0+yuqAw@9uJ%o(K0wn`S&LKreHH$&3V&9w!avbh@JqdBHersyr-r`4 zx1Refzr&KIDWHCZnim>7PK8GEvJC?%Np&;X@oE+0)IeEP*DzrwtIDRT3{y`PhF3ry z%wUk=>@nJ^G>k=6I&Gq((~TlG4bgUBAjUyDX!3x`aVOY@f$#ca{u%;W%P_%+6YP8l z#Dp0t=$OrVdBm2}qJ2PNl;L^bDpx1y3Z0#4iNVGIFyGU&DB64@8*IIOe zz}rO`2f{wZ$sH}?G*8@2vvw?y(FVUx?fuH{Q+tG5&RebyaB0*xe1578S^X_|uAXN7 zn6_4}1syBXRh1<$jLmTj6Z`NlfuK4~r8@E^r`OK)bG-1Wpj0BL6|(4GUaVs3{Dk2} Oj4q1|>dOv?cRvBgk0FZy literal 0 HcmV?d00001 diff --git a/games/devtest/mods/gltf/models/gltf_spider_animated.gltf b/games/devtest/mods/gltf/models/gltf_spider_animated.gltf new file mode 100644 index 0000000000000000000000000000000000000000..79221b0c739ac719b7219848b0756fb9eb08c05f GIT binary patch literal 239030 zcmeFa>vG~umNt4T6ZKcsB>_72{*@rkCkcaXJTb8YFqQ$^*kB_=$K3mVR;G;P6eXFd z`KrH&ZO?S&yU1KCPwSLV3h}QWlkIl?`s1Jf`Vq{Z=9|gudh_F-Kic=3_0xL$AHl=0 z_8;Yk`P1!u^B+yM{~x=;FY8yS^yBA`-F&lMte?=h_)Gio-#>qBXAtn?pIY=^+aLe@ z&wu@RnmppO9~Zpt=Z~lL?VKtM`@jDCzhd&(&Raaq=9^y~$lt7AC$GGN|I~hICEF-j zR?)CEvsf~V)=zxqm{tM*XWPYsVV0~SJ~Ij>yJS1MX&0Q5X6X3HHVTGrSVlo}bh}^| z(CzDH^0a;Ukp7S2uVTq4SbD*7G|MXJwm$!dW`nh8mrSeVSOwcD=vt}x^EXZZ>%V{g z>&NVV@o>ADV?q8?qp69BwCGXK~okxjV&?pDfFFIfp0W47%NybjP#re;3}0g+q4}h z2=tXUlGY>I$f8}uXXxzbKg?fdQG?Qy@JRtCW62;lUsRLbSXQY>BO}{|Jz>jCD7tAmhNh=<6RWRT z*jU{tKuZdSqnVIMD`eVssQ}HyjNlY(leANJv_iowz~*h;f}Wn574;EqRBt|8kyU=4 z7xu}rY)!WfOLvO8ZaP1$Upf@hF{}c5EkH3zFg6cXSi}aE3WilQvF`Yo4-M0VF=5|q z9X^NjRdO|@UnQurPD}3;VfzJ4ikwu*DPV84l2$001BoWV2nEag@X)3hmj)zDM5SF zPM3N}EK7HBdn#urAPX_c5aA;%jq4w_%|gmkf|Dp9JeWMf!Z*T~K$Wl?7(c=;7KKa` zOJ%{I+66PYC~>-Af)lG*;>#G>go*3{`IMDX4cc(ZSg3orXe<*~4VWcDIIW3&Ec0Ft^}|S8`9x9kRO$79{u3N zO?W2kk^u)+qJ1`zSzy!QxUdG;+=O0y?05-{k&Kxzv5$|AN^txnZj@e=;x7|hOjd7b z26-RaZi>b*Uemsszg>RYayt zvxW21kj7!YCeI|G&v4i1+C-3o0wIc`8GJj<7!DR&Cbs#*XM9+Oja&q|aY3V4CA8wi zN3laqt%~`}fqW2-DE-cN>RJ(APd9ZW zRs~Hn$Z4P`pj)^MM05>#HI@QyqlCqQqlFP+JK!#9VlXX)Ti6lY3{Dj%v?KPDNU=Zk zBTE~sU(iZh$6*A~39f=d0Xa|j8n{wS6JE@)N|2>v`7i(^anNFD4jQIaA(5ETI{eOg z!e7JT;JAY%3F)3LVhHvevqfaZ1fg#{>4qPLogxK8nuQ(4$lz|_a!YC?hi#H1F8#UgL5|E`5hkqu(R+3&?f8y+z{5;{AvE8{GbT+gY$+@r2H|x*R=An z*_VFf3@I|(aO)+kEplRh`ol%Q(IdY_x0r3X6SV>(mg7>V;F9Z{fvG`d=7+cdlfY>P&L5K%=Wi%Fax^Fdj)6s-S9l(!A`l};=J^h_iNM{fu%hXco{ymNipEQ&%dxEPPHcizhuKmIW(MnF$P@6dy?-=>pCcIGh1eLV!eS z8^IEk&CzIJMg}=j8%B(FWY9RE@QnE*UY>FkQ6_m}S>lgU%R)yR4MvIx2;>BIUHAwb zPy|A%U{HVsFa&sz-k6ne&`DO8h4~5<%rzCfQ%m5%8bd9eHoHd=o zFjXAl;kN;UA>E*Z04x|JH7)V1n;$tT(h@vC>7u+Y@r+5k#NTS77k{}MallL)$qV4*X484Sv=)sX<4xUfCiv`2zfYd zlWGtkiyRlOo-(*1kHhdPKvl5SbbcWZgOLHoEkUKALl}*f%ric8BXJ=6_~<+z3#b}~ zfnMO^DGSE=jv#al z#nHs!MuP<)B!{vCcrd;_$g$`g3OEHH@N?$TXc8!Hyb`I62T*2Xae;vgoRCKcp2z_e zM{q-yiF5!*Ma+?)aJY2jV90`hv+AtX=7(ehXFMGI4T=~d z!-OxP>=KIuXAT^T2VyK1kYpU3O{D2&GSU9n=R{nAs>rS)wFo5~cHl1o{Xs4gOyFul z`?0D}bld@8<$=rnw0{A3GZ1o+hfwBoQujaSn}w6UvIsB;=>#SZ>q0aD4oLt7Aab0* za1KMx58dXw4wFUl311E6fE|)i!gWH?=s*r#;L!YW*Q0n5$IKJ+KYo;vc40}NnTTc> z2~MO1k#^D9yl4O?MoLixP()c!0Vh_R5h!KHLcy=|d|i$p#n1U@;dh=A$_!P+xgUuK zA_$oR5>8;5xVwO-#JunsQgtN7I1N~cW&nCPdJ3P8MS-DGd#)9qT^t5VSQm0;I;kRM zwM9_G$E4M8&p5>5h)IbD!XT!P11r27Qb}xcDpe?cP9zJzad8B38f3-@fyfJKN4Xou zsg34{fIuH%;5cysia|n0xNDJS$ss_@2q_-m(35(ez~VmCJn>QbYz0Ib9bl8ltmr<4 z92H+s89+90#egJ_kX>LR7y-4E4$=aIOy5@s5E954qAD)gM6lx+B$6N`p>QYwHqfnr zAdyaD=5$ZP{SIM}h`dMyfGK}+>>v429PEyhjuD2BMzIB?%$<%}bkM?a2X>4Uo%R>Q z5s)OHUu21n2{nN0M>-0}iu4-Id2*Rj?9VA8 z4^?L(lIi(kCbGvnrT`hMVjzfN1jy>)hM*uk67V2{gD;&)v0g}nfQkUBhv$I0;${e1 zjeDDu2J$&))X5K~mj#}UFiG5`(OH8)Wikg2N)nX8Gi{g+rBH-P0%gEb**JL8X9APN zpa{9r$RI!b;Kvdqtl{IM*y(*`Rp2#{!Rmm|$=3kbumv1Jo)Bpf011Rc+-jC^ET?N^ z1TkFoK&=eYs#L;Q`kXLE7^So;iVQ z0k*+A;IJ(M0*4)O`3V3D9dhV20v%!8s6;x1d1io)ANrm!ffK$W{ZYzY;{FV&IBXH) zL$5f?M*fmeNVs(*NXSBGF7e$a91_k5=L5RE&#n2wCz#5sy?7wDM*+({cJ&rGGyc_YUu(`zDdO5k-kkO5|QD4HM_ zJP(3Y8TphZ?utK-*f`cQBn+)0P|Id6<`Ole*4I1D_R4S>+5 zbPy*9VMBytqB}g~f!vN?H`C|zFasb4B;82zaGu0O1&|3C8M!}c@QH5BB^+I&ty1(umw z8e}*UmWefhJyR}5c?dm}Lu&w=!W9c#9s(^H5)Neo%LLj7xC$l?hUA^shkhim%(OQ- z;aTBW5xB7m@N393G@9GVB$N^`oRhEyboxd*d0d1T*cQNLYSIF@#1_+)CZY2*L4jN1 z@&db$q!zXVE8(~$&|4gK0Y%`dpjg&rTp%HMIUKS2qB98(>SwJYI zE&jkQ$yzl+SMV9SJ|DJ>lZp>DIN?lUWjWzFaESsVhEKp{3eqh;Q(9jRTGFYGd?-D$ zg9nE2JRZCl{vV!$L;dM6^EmB;WQF!l5Uk+6CD?7xPU-(!J`rgy-`yB;Y}&Uif)DA zUMS-NoQNQXV}Ni9U_7ZOUWJoW*Xp@u&sXbF(UnQ3M%l)6N@0@lMy^g&xat}Apal&4M0D7Fo15U z&^dpWhU1e6KX5nnxrY!;}z_ABL z8a+bnp9 zBghh`B&2|NUVtv@37f*n4W~mqpoZfLSDTdc)IaB=5ojqb3t}PN^dZ5gR1PtcqxAsr zU>>M}+a@epxC+w65)NQ z0F-b@xK^V$L49)&6QiS(JxciWx_B><$eI&q>fTpGZ&VV1JQb;<^A%fx9Ytew!YcM5v~l4!{=tAuqBN zTxt-)0+^a0F?xD9HCe=YxErAFaCUfb8Nd|IO&BXBZLltSL<<*I2vFpo;UlsA@X-Wz zLd6Uz0O-wj>N!C?e**M54zDq$!mCjZ>fphT#5@3y{-=l0XMe zO#!=vCuMK|MGlV~2DTziqWFLNB;e-wxd{m#o)?7^D@ijb{2OOL0S_988x{n8nE%Ob zphx785y9~2b{$3`&I|ELl59bCb-&U1-+Y`_`>$964C{Z<6#h*!_+Pbz|C{IVB}4c( zP2h`mfLw^|0FQwHV#n!?Tk_+4d(Xdp7a7FG^8#PmLxkI|7Wh({CVfW@_cXwtb?2v{ z{WJ)e=#cHDIIx=e!#kl4G2icocS0Frrjx@vp$as~Xneox=Y&4cdggGQPzP*Y4$}#J zh>iAl<2#`fxYf<+s(3;p@Pt7g*9n!tZD}6Y3AMmCTk^P0CXBgl zjRNCbm|l>$yHl2oTdx!gF#eAb{;s1FxSzM<) z05ati3w0i?M#czFs7HeFcOyKZ9tp0KBRruSG|~JKo=}ejF2v6xo%ynvn)+xn^z3A^ph>Nncdc=&s86j>F zvba7*7-Q5Qr*AgnZ^FS@UYxSUk$ds<{DwF8V3ik7uk+3G`s0;0KRV4`l@W%qetYF@ zY&vS6(cVe4rz1yh2Zh4f9K{63SLRSCB^bUoDE({>Bf;^dL754LuMNsNo8#QwE1bQ3 zMGg-|3i73C6q6hNm6a+c_xo#IBscs^T_pGWOAP03cQLu$U+N*b+h1Zhce9Jh&HhRc zrQ}|JiQ(L>E+x16OFf*s)1`BFx^(VNm(Jbk(z!cbO78TR45D=IR_n>F{!$nExqGdj zyVv@;d##_l*ZR48t)IKs`nh|pC-?d*7NMWJ)kbowztV$o?oJ!$?zD04P8;X$v~liE z8|Ut{aqdnV$({a^$r@E4kTU8k3dW?XQfD;q@!Y{1d0;VfDJw?+@JnSNri#9D`f^W^Z^w1W=Bv zYO~%D6!Zj#&D+I{q~Y-XF)zBn<`bpc?2qFwy@GaLMsv(+BUo?-U#@Xlr6#y`#<$+- z!Q7vCW)jbY;fs%WDhyBLYd@Wzc&-pniQyyTC%w=RE%2;Z;U}(a=>42FJvWBS5!`g( z+5%Sz^b8qZjH&$u0uMMHj|u@E2i6VX8Ynb9TUPi9h#24M19%I>6ksfnRluacJkPvk zpJDRb^+wEc`gV6W-&`){Az#ti`tf=FG=F*>?w@h9j>qpXXJQ90C~9`#Y>e| z7eIPv@TVtAPB%*cwCR6ko^+X?(@)kk>G6d>VLz$U(tgJMpID|GoyPED_Vg~%_5Gg= zKS!JKt*>PNCx)M+&w0bo(b7EjKUGqi-XAO^ZuZyZUs`{wMwC=w5#RIog*m z;7|3B9<0b(f&3BVXkfm8Kh;5c8sU$oKo3FunSejlLwXqC&qna4nn;g6{MiWpR1=Yw z{^1DzR1=W^XO1ApnbK1hf3$@3a7UJay#D`G3+WMutp0ONnVzipGXa09h4dWBpN-&8 zwU8bd{<9I}XkqRy&?6RqGy-}8;?D&9sV3ri?mryCpK2nWCI7<_{HZ3=LFmtR;XF-D z=f*~kCVuiq>C~1UT{&X{>Hg_KmOm4aqlbADI8O^tOyE2%JRu-Q3-cy$o(`Vq|2!Q$ zAs|Nw^Cpm^fs!vw_fJpB{E-UL(=u5Cawb5J&1A9XPvB2=5MKn%96{a$ax^f10)MK5 zc>nhwO&~`D^C$49I%wzUV9pNUOWm3CPfq~QR*YLFp;!*XPWP|#&ipB$?=*pz06Y4q ze|N`!OpT-Xg8eJaxr7+vOR?a4?@|Z&3Z$0R2~~d{Qi(>H(FeZdonoPnGFa%{5}!N4 zODD29=w}B$trWg?pUHvWw~-j|g*Sc=^SkB6+ZiPedJ%KRH0Yb$pE>AdAXyyJ9Pn-* zJF7W;?;)ujXz%dbs^3f@-5%?D-URUNZ+_$U)gJJggEQM_&iKTV(rZgI=8&2*t{Tp4 zFYfk#GvgB@f*xh9YHCDyGufH#i&<-WVnp=nq%+oteylF5eYyv5I9aQDf&*^{Ig>3vFP_CYUO$k_4pe&AhBXU-gNbU1V7^fOiG zjf%gq`K)=+Z=9Xk2fZpNqYv3$@iwb-=Z$vC}T+J?IRZP_|BL86kcJ5 z_aUCyOHNchH88v~Vm)v7aWjAJ^zoMPbEi+QB|CRucsEQYODcDx_ts}lpT5GG%&qYALCAEo*6Tzc z^lOu6vd~YYWU!=%g~ukc81S|~DG8+)#%CsFr&#c+s;rzNJuJMzCyU`!ANb9KOsAKg z3Er2U=~z?4f&yn}b0_-1>zFc=p%X0F_AHk4u#mTBF`QT}JaLn4izkMKS4C!NaC%tu zez*)}JT)x(b(%9-@cRZ?EU97Ph0>Wea$;C`w{CXMoL()w6ED+B(!;`=%g$7JdhwP? zkHm?f9iah92msGN;YE2r-Zl%o)Gr4^&i{U&3y9s|4i}2 z!+2NL?cM6xQwFAbF#WK3rGA;Vr1p6W+U}W z^nJKrTZQY_`B=373VkRXs#QB~pGDJnmX`ah2l04*M^~<0FFF3N(8=*q`>)U+itr^m?@@8{O8g;!9V)oKbQ8b@YDFu zq1)F?-38WmXPLeEou+$Yk&1-Sc%jt&N^zJN8h>+%hKiE zj>fb8J$KJ5E*+0ow9ljC@rw4pr|nARZMRV$>YYMcQGE4F^zO=k z7#3gqx4LMbM+f!6!?ZA%-fl(vJo;t$P1xyHZVm9I9K_n5lX6ZeA}4`A)ai9IJWsw7S0$@}1?fF;eqr z^D2DU756o}W4<&?wwgyT*2D2`<9n|gL%%NuY92kr6_b#!msZuWnn!OY&C1Rq`MT>= zY&DNQDC0`lG`9~+Wq7sq)jXQ1ck0e=)VBxcy<#`jJbKyPHiUfj>e1Y)d9?S&s$rMp zEBj%4rRLECr+>LKy|>$g@>p#JY9770+j~2GJa`-`)$nGd=F#EJ{!YkOFV-bp&7)iP z-4&XUuX_7! zRn4Q7;`OtTuXG!mm6}JJuidB6oo<^4rDH>X^5{lSn}uxks2+^gU=yl&bZ2fagnXy9 zz17q_dh7+)LcZQ8-;UHgy6&&srmDpUbn$u)eQ%hw4s!WojN>tc@Oo ze0%ve^VK~1rry1Y9x^6j7kM>B-qw6ZW zisav3PFHFk{p@M&NPbg)vDG}9? zrfaKtbob&di2Q%?!EFMs$fi5WBm6V@fBR`Qtg3mmU#Kr4`PJ&(R>`HAG86vaSygr` z#m}L=X}1;mf3t}B=FzwFYfJcNXXLU_&7*r0)(mUp|LvWj`Dz}0(@~8`exqKks(ExU zuI?iL9M<1MHIG)eY$^P+*&3DuHIKe6>Ti+!WvftC^Jr(HEu#2hUsW|VkG{MZZ6p6Y zZ)uvEN7pWAcaeYgSaqf5(eLl$S0P`SJ`8jZduh=^XRr2+zR>5^TS}J=F#Tu$`|tOo9X*V&7*JLD zVGY1Y4n3&{&qBW0>F(!F9(^|`Mdu6s!F;XQd9-!6ZAbojfS*2ga%pGPi2UQF^5_@FBKdY_wQD>SP6i_-kABeS*TVlh?@xn3$)n4I z$5|v_Z9VOsJi2mwbs72pyUQ!Z$)g=Js0;tEOiQ~uo=nN6?`BVh5C43)eRT3@Pj5Da z|5r=p&CJQ8D^G_~B)?Yg+D;zb9(T7<{_i{w6eW+gr+N_O=fkH#U&*6qweVHQw@bZR z*U6)MMsOeb|MiPrb@J#t)qfHGS)JGyfs;q~E}HYmKVRQmYEB*73;N;P7ZM`4m=l8?0?&Q&fZF3UI@7;zKC689#>|P}Q z?zKO0^62$lCBlE4*Y~NS6G=={+r_1@o$)h);u^0LO!TVNG^61+A)yMf_uD_T{9^LYR zKXClV9tH?j=^{c*`NB4&ILnQxwdf2OZbn&fuBjlT7?LtxW=uNk}i2VOmWm#17=w%Uj z2*-b%r;EL!=Fy>9#d(3_OSW5lQS<2M5gQ8mc7Igct9kT8Z#)$KSsBeQ_G%t|dp&AI z`T64Q-cP^k1f&Y6F{+~q?-tS5He-=%6zmON> z|5-HQ{X$-h|7X#J_j?llpG6bi?@9Q77EO4+kQd|sSv2AOo`nBr(S-N=68@h>6W;Gh z_6W)*Ki$3B1Sv2AOo`nBr(S-L4c`^Qa}S@&7EE@P6TsWBfmhCcNL5@c%5D@cu~N5&zGk3Ga{m@e%*eq6zO8{y4_} zvuMKmBY8*sKZ_>3U&u3#_>Tm`ESm8C$lo9F z|16sD{z%>t|IeZc?~mji@&7EE@czgjAMyVzn(+R}A0P4mESm6sAuq=NvuMKmg}fO5 z&!P$MkNoiw|Ieg>_ecKti2rBNg!c=1G5()L6W%Z6#rS^~O?bcX$1(n&MHAjH{C$l7 zXVHZBNAiyNe-=%6f8_6v_3KazLE z|FdYq`-MMNj`)8TO?bcX$NCZf&!P$M7xH5KKZ_>3Kl1lS{6C8(ykGd^82`_r3Ga{m z@e%*eq6zO8@?!iyizd8Z$cyp+ESm6s;g4hdKZ_>3U-)C^i2rBNg!f1Cj`)8TO?ZDK z?}-0r(S-L${`iRhXVHZBNB;PT|7X#J_X~OU5&zGk3Ga{O9r6Dxn(%(%k7N8lizd8Z z_~RJ=&!P$MkNo`+|IeZc?-%lnBmSR76W$;B`y>9JMHAj1`Qs!0pG6biFXYAee-=%6 zzrd4X{6C8(yg%~CNBlpFCcHoL_ecCcizd8Z$cyp+ESm6s;qR3r{+~q?-tS5He-=%6 zzwpN~{+~q?-XG=rNBlpFCcHoL_ecCcizd8Z_+#gY|7X#J_ecKti2rBNg!f1Cj`)8T zO?bbM7vuj~G~xY`zdz#tSv2AOk-tCU|5-HQ{gJ;v;{RDR;r&8hjQ?lRg!c=79OM64 zG~xZiAKOR#KZ_>3Kl1lS{6C8(ykE$R@&7EECI6Zg^@9vt8~<71|MS9+Pl*kKa)@Vviu6$ofx0!@AQ05wa=Q*srFg(Io1BO{AfLTFE@8X zV_kk)sNGuowL9A1-?>WZaWPUtt)+Oz?801i### z*Y~e(?xx05`3c_-<$sOhbg5KdteG-iJXoIj&>D0~RmHW1Ja&I^XE}#v>&lV&<@VIy z&REm<3N$|6J~5tC^GUYPn-BH3GaMRU`8d9u`J~!s%!l+TwO_5b!Kh=0v%!T}FSq<& z>nPLJrO>}r`%m>)f!51lz}VzhWxzo z<@JAP8v4$7Uol*>y>-BQ7wHD=DOWp3rZABZr7raN;xSN`#VTq}7`%$4{y>`(4 zpoaT#Gq=47dAp@&+zm}*SgNRUf3keJz1*M7e`>tmzM##Q+E6L(6`4P;z1+XtAB{ID zUv4k=C$~RoZ>{C~%?2+uc@2d>Y^pCM*BskdN%v2pY-6WcYq!vDOI|KawM@K17kvag=-KRfb2a(h{xS%yDb4v+pvZZFHrYft>U zy>av}^7!Qb^V-Y(%k5=(r^c5Yk1S7akKGaeBHw<))oL%|_t)$j9P-z__@ z^QmLkCtat7_3@tU=fY;d*I#ZQqCNci>wKs->XUwM`*s!U{jjUMn@h+uRk=S|zT95! zPv)1$t8{v6uhe+IwDdvMS|~DqUVFKJxj$LH++OZaZf|u)*3Nl;Zw%F!x#25<|E|7V z(t4X}uh1NLhfVcjsZ5OGRc^g$jVuq3@;9dHS0G<)ZAH zsrktDdGh7)$ozTrvV8UN-PoD%j|cPR*{nKlSi9YkKc6~Jef*xq{u1jyp4nAv#MfVL zFW0+%?KX-BMX#yYa(h`mUbX(PyCi?tg?vSo+spN`d|9469`;x;yrN3}zy5629l2hX zC%2d7%j1#T%k_Ek`);qbBY!+_4P7V8c<<^%QVNbIBy!t%(JB(NOf2Cq*tJHY&>d%!gk5`s2*UR$v zy=QZ$^vM6O9@yYW zcZzS0f3=+M9vS?<{e07}*cUGsmEDz!{JdYE!~f^ilRV-7jccd4b@Jr5%!`V5(?NXc zH|DT6S-xDKCtn`#O>r6de`kKvXQ}zf^?CB;@yPsn^|Ji8&a1IAmhk^Z`=+Eght*Bw z|J93Oy=~uMeRh-nv(dTW>o2#L>kp%w$p0(-i(=E3+spDVvA^En?QY#^^Yxe8%k{E+ zS)M!|lIN8?_nFi zPx3s~d4&JJo9fKTliwo!A9r#8C;X2rU#`!S&;3tgyaS9kIuA4lLpwDexjs+6JRX@p zuU?kln_bR#>J#BV?#=aFS@-USyN50Ozw_$9z2ObN^XBf^g@5k7zLdslUVV3aIgk9m ze{(%k^5h%4dDDBCIHPIr?WHtH$(QT%zW#+thsI`aJpacx3*(dRhMH z-g)0yQ}X{?UB5!Yj`e=p65hkt`+0JCiTgsF2b8g2EIMClALajh|4~b|m*rXL&wD`r zzoGq|sIq)&pOh!J&l~UMyMylsZQ%cnwXTm&&MzrnZl5>aQ|*)TrXfzw!<){J+}i;|=S1^$WdR5&mD%8QyOr%eNqZNAfoV==(S& zKd(MdzJKFi?e;tJ|Lgw6JT>0D`g7&WSg&YnB-32`{oC}?u)Xy&nJKHD1`qz z7VvZ9K=%V}x*w3+zhBUO0muJqg#XL!WqJI5huf(-e2{Hgx={Y8xb=gH6G zCwzaGxKB8H{5kxDm+zAJeueOIy8l)03I7*(HQf)3`vIbN34A|~pYU)G`2OMp-#=4+ z9=`{9I|<)U&L?mDdHiy{Jf3sq%lz*ge;>jh3w$3h!xs3>)L9e$uU@?y_`U_*4{Ydu zpaOh9^7jS8_ho*$eTnW5fbW0c+2_ia`DOWpzYBkBZJuB1jyxW@Ke>J0eB}1>c+QnC z^KZAn%Q?R9I3M`Fm%#rCzyI+6bU#4&b>#mY!uRv|<$8zg?W%Fa|Id{#^V5Ara=dRg z?ko1VuihwS!vFK+=kd$^ohv_&zg)xpUjpB+cL^`&_jStAr3I7uKzL&uN z1zsKB4-hT39}@QiT68}!6!^Z(FSoy>`-95{;)}pH&y_Fp z%kues0e`;7oP6N>ne(Chjzs%I-h8C?@_177k=o1hW&VN+y!>!Y`Tv~b`=qb)`%-s# zA?`zf-wXfmH0geT@atUy-_PTh>-%(naAWxI9g=UWbU$#8e3}178n3>?eZ~H8#Q*c; z=kd$^ohv_&pZkvlUfyJcmvjH$CH$Y`)e7*O=zhSa?;|Df{XG6A)_ZqtAMyQj<>&EV z@cRg!?;r7fFEO9o@#pZ%^5yZID?i0g`1@M@gztL~;SvAubqUYs&xi8+0m82%|IhJ# zJBQzh?+-Y>M)wEj$j{>^{9WY#&XVK%(s=U5pT{rP%i}p$zRWN1^%&o`36FdVj`%;} z@m}J7;JHor0UYmnCwxDzJ>lOGzQ^xZej`;`Ni|r`k*X5x&1WGzi~s5x(!xdaW7Z|NK5g;MMo^e9#Tu4-hTg?9LDbK8 zy6Qa~!smnG6F;xtaQ=#QyA}K^lCQ5+eWqM{m8XcG*FOfWFp@t|Z*7OyKd&n@SD!28 znc5rRgsNNq${W=)= zp1LgE9jxI(3)ODVPrqrg8zCMmJjBh_<8-Cr*sZh*miA) z^Eas8qVFatb|t`X{4~K|u#fUv!~OJNHEPAEeh~U=w;0fV>8ifNeyqJiQ7zXPz(kC1 z7j%?^s87g`$9GTs1wMWocU#^QA0OxE-y0GCvQwQ2{>SQeaB%F$y{?Qe zP+!^~<#T>6U+{DN$B?HC4O2SG}tho4QKyuIlzp zaP#PyZbMzZ?^q*9)T``RA6Bl_FUGY=`rU{x_^1M*QRYO;peM zdA+tPUpG~+*Y&)GUBmbT2m1fQ@VvRRbUFu}*N5)G_a;I4W@+16Xe*uYa_{W}6Z*O) z`7u9UWq%RXbAFT8bN&l1pYv<)_xOG@@t1~mtZ&nUeZB-(AHn~^uzohPwUm5^QUARD za2UGqFT`K;wB2WZj9=z|?T+W}2S4gLzxkjRHFfJfKY1_L>U3!1$6JuE_+{`f&EY}R z@4aWw+l?634_W<8dA@UAz0fVdU-npit!|F|N~kmg$OVnGgd2IFjB>r7VnGFR$ujl-R zq+de+r+y3f{ak-}J!{=5{UYh7(Eo&fJ=6XR`-%0R^V?j16ZNtFbAD0J?IWfC)W6Vg zjXl9zN&0`S%wwe~XVV<`?!C^NacfKeu0Qe}X?`_d0%mvas<=n__#Yw91&T z>ib>K2`&b%V>*kKddY4AuehpJp&!+iuWtPHSk&A2McJ-e-+>=*P{;3;a{h@wy71n2 z&g3BKIlrm3>#jXfukd^EtT`9_D(}DW-l}?PSan@{O7(Z%_}SCf)tjaBTpl!|@f|Qe zQNIb$e?mUz7vsC8@paYhQt&IUm3#QdGUW5|CHfcqcp1l5?Rd_{GrN_+(%!OKQ?2>c zH*X)Z(bDGiZ|~$U!wcWFw`vjN3w6z-WM!x<*=j8KOLo5p|KMQ!9$uRw_(eVEf7cs* z$S3|rFf)Otsgpy3|9w>MMD>;3Ysb^p)v;^iRj5qu_~i>PA)nV<+X3E3!EgtshWK&t z#ts^Q(G~D z0pj7B8Lqjg6j@Y1stjHGM!lU0em(RLuHf%g+%Uy|@B=&IudAhN?~VAQ{z?9!d92s& z+6Pf@@0%XO^Jaw`&$)JX2kU{Y!o>KZ{-yCP%WhZjs}t5ozRLNhNB;U9_0Q)wb*}=o zGIv~0*BIWPbRc`bD%Y+&bE;u~5Z`pQqPFh5gWVpv%A-@?s`yk7Lr>kk_G zw_BFhRcA%?&&Ov!`RJe57XtJz>P7!u-hUhY^ZD`q#rRqp`WO7%e!2aW$i9>|?f)cz z{W>$UfBR_v+UxJ_ekA!{3{v%<3oImm(VV`8*_9eT}h5bh3JK8Vz50U>M{)B&t{ez?j8l>Kx5cz!ASe`zQW{{U_>ivV6nu%aHxXYpi1^R#Uu6{kx&6!WpW9a={=f71PxdR~Kj)Y0WBV8W<8=Jjj`6>Hhxq-z zLH>}4|Az+U7qNU{zr26$e?)%4?R&mM{=n-K{E7L+_MfoNWc&{({>S#q^MjQAQhmZd zB|gjN&M)$Z*ng}8@H_GRM1I5lPaMBR{Eh8D8UM-t6Y*cfZ?a$KAjMZHe~9d# z`;Q*{Mu#xPYL@M6{}J1N!hS#O8~Ho8kNx&&-xt9k@DlMqw(o@h5caj9_$t?P{zQFze)zC& zUDNJpCtUpl;`hK{O9(+dvdOZa{QO`|AhU^ z@qf1@ekuM7{y6_fzV~r{NaPoC{1^87mH03GiyZ$G_AmSgKYx&aP$$RyU(TPy&Hnn} z>?Ql={z2sbiTHm!KiF-`@8kF{^7mH;JS`F5WB>7+_|NU1$M5$z{`2#Ri2si#@_&(k zXq3NrKJpLlUu5|re~JBv>X3c#_?bBW$M%spKa2A2Q;`~~~Z;sD#`xX9!+h-#G7xqi|VZ#0s`NK*3PsDHG zKL|g}JO7LPA@(2Q{2%+Tl>PJb0rwx`d=sCKW&4)nXCnS5>_5iWPRH-q{y*&Z)A>G; zKZtsM{+G`ON&BY!Lg16q`9sbR68NR?F9Lrg{wV(-e1qF>Dn7>e7{|}J{U_pcjL${+ zIpx<0`;YB6vOg*RPuTzE5noK4|6~7g+CK^Vr~D#i|I+zC%l?T!X@BwgU)X;lzo+~; zasKD$bDkgAD&^TXb&JRx7fAai)jQ_&^Ie)@G&R zHwFI4^J8KE9RKI}b!^Bwv`S~Eu9}@N-$5&yW74AP$=YO()e!sB&O8gf23D5sUejnc- z#Qr0~Cy)4`=s)3~#QEWfU&`@SoR3e&|3v(bijRo|Ac=@#BY9n;P#!!AB6uR{4f#!6Zab&-{km!u%C8##NT=To{H~ezjFK+_j9uS z^Yee~KT_w1xIT&BC+;7~eq($fksl=NUylC*KaB4eWBX6w8;SFOqCRoImcYjo=VP9K zlYJ)QvmE~w!q-p7|M+~L$j^Tt|08_yct4mC|C9J+>_52witRr-e@OU7;(kKh{}Dcy zJpTu{zf0!-WWU0`6X$<^{};!9ZlB`(f6{*>;=jQ6=zM<~A4tUi1pc2m|6d)?{{kP3 z$H&kAC*!}EUyKh5{Exo_@89?j9-0%s7{C~RVZ`62#g8BTTe{{`{Xa6_GM~?wmbOm+ z{B8Q*vz7lxmhyk3A7xr}JZOr)D*f}f>Hoj4e}|U7!hd$;mDc~S(`0`Yow|Obe>#7g zrr%Dh&~Lf19sSe!%XF7Ib?G0*f5q)@({1Xx!kyrn{`0TXT>nonK55%vE-|Fr%#&Gn7vACJ8M zzf3o%zcTl~bL#)E)1-f8?tkxi|9_eOw`W3J-+1bCPxEj7Wx7uNmAUENQ2&3O=K9C| z?}YdNw`s0#-2YB_|9_coP^V?;l+Ea$zfISu(=sUm<4*9e(_H^}*OxSi+TW(RzH$G1 z#ryx;G}kx2_1C=rzf8Ma^|=4_sQLbbt@w@~-|-`*6yNdVJAR~< z;yZqP$B&d&e8-RP_>oeI@A&Z@KhjF^9Y4O~M@lQcO^X{Gp%AK&pK zr4`@t<2!z&l;S&ne8-QpQhdjc@A#3@itqUG9Y0b^@f|TJaq}zT-zqDZb;! zcl<~z#drMpjvpzl_>Ldn@gt=a-|^!+ex#M+JAQn}kCaw?$B*y$ky489`0*V-(n|3i zKfdEfN-O?vj2~6z2J}AVD#QDf@k-}4{jSWA%4*22S$V{&0qatU_m6t~M^8f5q7tvd zzv8cA4)C@_h`^7kbXXbk;`h>4NysQ*TPpF=)H_yb(tF!CRJKF>js=9H^A(kNpZ*i8 zZrP>J?y1E4#ot(M%!UDe`2;08S+dHC-g`f$vQF=zc4y2Fm`x>K3jEGojouNeQHht7 z?pbxr`XMV&+4C`IOrjeygUY_oiu4lQL0B$R>HAEf#pnm+8kGaT?6NX`dMPMl-WnT* z?$nZQ#tm_b5^t2I4HkT zIrYmg5XPoq`I*XzUw&lu5u32`EtS`Pc|wz%gb;@EhLvwvU1#I4Jfjku^F%u~3(E$T zbH7}q#hnM08kP5c#id2O4=ZIV1HY`WhQ{tfOa|pGEALrj#)1GZwMTi!%I~Z>X7jLI zpz_wojy40f2rC0BSAL~Wo3silw|ukw%9OQ2_7qlbsC@J*SFAN+4`HQCB{r+gnl^h3 zDkCZ%Sf#~UI$MR69+gYKGG^^2dtsFgFGKw5W_!k7S*66wu=2t>O;VN}FT+ZKwKZsF zWy#ABZ{%%{+1jtXQMm~!2i6(0tql*pa(GITFlZ$`@X z$jiXJVf`j6G53y_q5HskI@5f2$jcB))*F+u-cnie-6iYKnC`nHDhtfT@3gL%8M^OO z+P=FZCAS0Dp;Gl-i`7+UtLn!468eK5tLg&^@hs2U!AfGt=tZ)4^+1N>Jz)D zmK!0AA7zJC-`D`{S@ng=c2J#@vDO1P3zSV(U9e%b+zqQHmHnWqv*E1V_iHsO2Y$^Z zqZ|ZPl}azHI&7%p4f0hOvkv2|?%8l$_JgWTrN^BW_`_;}jb`PZUn{fGs(caF22_sx znny-I3Tn4hUi-BvyM$8+;G9rivDy{89F;G_T9?WptF_rkFJA?<5tWzt5m9z&mq%f( zN9EA3-N1vE$6@W6%86flWLH6X64VM*-ukrzIf2`-_D`@yRhD+a>?p_c8&I|-llR9)D(6dmgfPS5lVQ%3cIP6S7H5z%16I`&2I3c*+Kn* z%4fg6B&YTq)B`HltUhNqTKOr2b3^&S>KE*0TwVwD8I@1;iw=`n`7x|tQu*N5@7ZKk zehKUERBrwHj!jv48`K>tcYfU>=eY~u3{k$ZdXY^><=3#jrE;Kjsno;94$ir31dSUii+*Fu=33bd z8xK?(e&dPFtL0MI*ivb+#v8jud)9cN(hM4Nb{m$90h~KZoi!Hhu3EOkhC!v`H%ja- zC@TT%a~Hrqn=YJwr5qq~qC|LB*qv5ZLka>~*_yO2?(Ir7Z1$*h{pJl@AQK3i&s4Vk<|A7Km3G)HQrQcd2MQ9su=!49*KfX2FzSTO z6_qX4yl0D9r5iL~sqFa81zV0P?V$NUWy{Ae1g%yT&u_U@`eDnX;57)E29Y|DhGb6!B*9Z7q+@o4q2r zvxlH^6|^3yya`$>_Smi5_^p7-F>B4)gI2i?TSF>GVQau1#+7l}6Kj`0aZt*M57-UV_S#-@c*p(QmKV zi(Pr~+YeMe`|S;T=~gyD`-RGvpnb=l*OfJEZ>dC*@W`IS$~tJzseB43=vOPRLEEJA zEoc|mW>yKqPJ_z#ux+tzv$FF$Whz6zQ)ip7vZdq!dN2XPqULdf0hp!OV62E@J%5Wxi)2*>hFDV^FF1 z%_<9Rx8iqeRF?g2hlO3&4Z3YAD}kr7-5P$Q+hyt+_S~&AHLS3}Q`j!7r~#6q`>I^orFm2}gey>mEFzAg~0sO4zQt5}i7Awx&so%S&a_sk}3_ntP?e}h| zyz+Z1Rri!hVnGy89UR@2GqV`xneuBdP6=seEMpIWtD?g7v#pBALCR zWOxzuuc&+okklHQ`w;d!R4&5)fSFB7Qa8K|`wz?0si2|K9z-7CZ?)9qM&QUv}|8F#Ag z=&Bp~s&{?=h>-|+9G|KLFFRFrE&5HN3Y)8X_w}i|7`c}5TzV1w-PxnL9D-h}vqj4% ze%5pk>cv*u3=8&d>)t=&cp-i-A;LG>@EK*eL;S(?6ti>Pe1D&)bEVba@!wE!tLye- zfoRWOHfkGse|&|Nl+I=E;;LTVs8(aCqkqUNZG!I7_DU{Oca@PM`p1#7*;Q1Gz5CpH zzO}^r=lwVBWv8qz`$MO*U0x}?|HdF#xXM7+_o{(kaPfdm(DA8PoT*mtitU!O-gZg9 zDOX{7FgSQ`n{sFw7f)9g(fZBqUk`fssoqtK%a@CtnE!=2RMbJM)YzHkFd%-c=lqTB zz0jI%+N-r$#qyCrWMqe@QhP!*{-`*g7qrcX^UfKow+4!K&`p<}I_zQZ& zXYb%tpJsD)d(*Fc+JB^kFMgFZ5x%Ie+sgK?w;0T|PU-cfMC*^g-pC9#L(rz$e6ai- z|IL@maNjVs)qU;Yy*v%~R&xKVY~5a$StZ*4ns!q^OzS~ot2|uX>-_$pSvI?6t?*E} z*LU`W%@1P!ljplh&mA}YjnXRZb&MbTzn>3w%VOXyw(6U`wFQ5r)2f)N7pUs)asN;m ziurqcW_p@$yHls;y;jBi>!reTr?|%+DVw6dp!LK4FCMREs;$*E@9Ds9qxCO_Z}Z(s zZ%&uC*&At`ACOY;u(WzB=gqm^KOdt0tzEgIwpP!UH(6G#X#c0XcHdj>-I;6O_D5UM ze|LI4*{JHa8Y*{H=pX41enfa+RF!IR-`_2E+sRVs|K;Yg>uL47t);dNttjSi+_t)w zv$qGf+UrLA2&Id~MD2SIaTSaB{ zGQFVvL*$>oU72d@zSZ45*x_TO|81{4^wzJ3`Nm$o-(Fc3`EYAN<#e_ZZ%%Zt(I;4A_Ya0LE>*8j)eyLGLqEA65`C>P-jxl}<# zL4x7|?*tJAm2)`!`c>#V<{YDc{ab6V-S%F)&E5KYG26_bmm#52sZ>4nM3wg?+FJ1o z`;D)D$3Hz57aM`Y-m)apVZPyC&L+2u#i*gJ_<<9<9)8#ef2kXGIwjCXSg(piIW?|0>|+`ulp3KlN|Kr zRF>oWLtn2Xms#fsR9DiSn|Iz(LddhQVv^hrDIa06{G&Z`d?`s zWuk2v1G+I1!}^83=9vQaEPEUlcy^T4(f&XXvVOCaq}!@t>)5;fjGvk21X+kh*2PJHP+S2`*RU6L~K`Y7{3ulL)`ys{Kk_mjLH6% zxtPs&a8@42PmE&uK4RLw-j(@qD8YQ^~YK5Y<{cHWv z#Ue!Owtupzc0uL$`{Ob%sCeDfo|IjO_9GwL>a+@!;BwS(TRxnx{m{{U|3VOR0*`Az z(a-o_=k&qw&|e2p)pq0l1>ax0sM|r!y=8_w@fdx-ziw;qBew0dr;p|C1a9c~x9wJI zmS68`BKr0+{p!Cx;VvG2uGANP_gn90{QQcG@Ri&9l-zw!{n{VFYEP_pWiLSMX^uYM zZ{qa&A&~ag(j_P7F8}C%_U_?mqri7&_elO3e-v(bhOKB@Dv!wc+JA`at+$DCoQn-^ zZBeJ6H=V>=1EJx=M%L9z#@=`|Ka)bhmoU zuv-TKdnS|BXaAf`uBTLHf_pbTt>!cBJO8u7yC&*+Rz_s&zTf~net*sD7cLITv8I*8 zJ%84J9TqlwKhHrz=>oaz4{sdq_5T}={Skq0{$`T59t3hYi_g< zo?y=|TXWQ3=xfGlzKYIPqMQ|j6G7llF@AqHK()E5OdD|CW z_vkPCe`dl1%N$48E4JCs_dmM#6Mbi0RAOI=;21Uj+0wPK6#unx$V9l;pYczo;=AuA zXS};oC3qR*{KNRy=LMo?4y{!r^+W&d-^@}NJJB&h7iGWt%1;n@z1U6K4dYzId0+j# z;A2XI*^DMibDoX+H|W3MoMHbT++c@p)s+9(PgB3lWC?lky`e+Jdms7PL_f}stVW)o z^4InIXZ%*78M+qk-W4fquNmcG{g+xMA?j~h)}`|9XZ+pfTF~~n58p2iLiP&mf#B%bkNtS# z=Z;ERGYd!xTggX%C-W%JRLQ9I8QPee=4bq3)*x-yzE9Ni=0W+j|Bg*+DsDG*AoDKa zzV_epkF~D&qy5t)>HQu5#CbVAAqlD_!=#ytJ!D$P9|P-tJ&$rNtW2wr}oGGl@Bk_s$03hH?{w)A6Wm7 z!9X~0D~~A;EI`;`{5vGsj-umgA82j>`ttdY&Mra$moIun%cmRk9Y2H`?&U;WmeordWiZi|Z$|^D7$1|5O}(`mgbC!BjxX z4Bd6CAI!y9|G8ZjSyG-F5!Ncs`Rx_r~X7xD#@E01!j`BHc23gkipYfZm ze^%k}x;|>ld#Q}`2N4k5yfPf`1)e2ak$%4aY%*wzj^@ui7{E50%6I&ikjpYyWc|H?z{$rHoMbRA1lm6Rj1G@RDRfb0T>^_MeaQ1>9hVP$!-Rzg5}Yr{6TeozW-_#?PpROG9FbK_;| z8sX~(9qq~M=lnB7UW!k>ai{(5nEd$vt9%3Y+gR5sAI)|L^#aHB?*< z1?vvRm_8f(S|?pXl?rUCQYF{uyZ)CB9Dh_}3jQb8cen5O^?S5D;)}dIGCtaEf5wjz zsb;bj87f{iPWq9DtgpH9Fe_6BpHy4>>i@KXGdJoLVMkwh;{Vt`zi{(*U2hhFcD9rs z`_J$2#lnr}aVU&?IP<`(n$yi}9-&B|gZ)c#>1Y4z?}9{&$mdj1ISbr({+Gv_WlwXm z;&9sdyYKkFpY}7q=qBmy~E%1U%&r({=pB;CiOg}tc@;nZ5o8_=s&~v z|Lgt!_WRHm^f&wV_pkT++wXs#f8Qsz4rg7}maqG5!uDDJ|Iqk_as0}&b=$SVv!C@p zj%@tm_B^(>XMOmN|0vq#cmMnM_y2zH@kjsmegF1-KEGf7m+#LXT@6p=>;xg{wf*D& zjK4De{ofsb{eYcx>lmyZE%*v0abta%{uGW`LKUa>MV|(^#^Pk6K+Zfy1&vSolFFx(hb4blJg*mo&pXZQ*f$@LZpXcq^ zw#T;jc`l7@=hGgaPe1*;V|zb7pMLs>R6evn&)xCgLuwr6@ALfA|MAoQJpc3$sprGt z_<8>6A5yu{{yhKmACB$Q=lQ39NR>i+d=Bt}`QZ@nXE6VebA#05*oM?1w9jMP9NUji zJ0IKj*oM>}JRhMAGk0vKV>|!ILkb_h->3bN_s90@)Beaqiv4ha)EWHyk9_PJe%vX} zv2|^{T9Sgs%c-{3R%08bGPGwyXw9{D0__{0LG;eS{p0iRy;uFDjSCo;*JRiF_<~J? zzD+w`Kkth;!;-J==yZ7B`c$-H4KFSFX8W-}yAHbv-Vt78NxGP{*%o8?iXcTO+Y2^v?vnd&o^L{_F<~mu!by zNb-H3$9lgTCQT${KfgD7m4H5^ub+Qk0oGNYzskeU^Fnv5#JDY=Km9c`!PgC6-v5k0 zh4CbR8_!*TWW#*bdq4j^(T_}@&j20r?XQ@WybjYhmA>tI;wj$z5Ki~sb~?i-F?zfG zdH$NPDP-#3ar}K=KDIO6>Zb8u|9;#*{y(&LSpabh+&injt0}r|&NnXdngtdS){;k9 z1s;A6#}h=U)H@@P5;Uc^wcxO<@lU*!9qEt!pZWXG(4QS0^;Z$>q`KU?Li8BuZ+jy* zm59idw8uGJKyadqyt+h7ghg)C0l;>5F7J_gO4SEGVAh?aBx#3*`I2l@%g(SA;rlyf zW@_HBQkqRtMINIF@B#bt!JrGZbIN8M5s2HqNj6IH@T1_{&X@K z^@`_hKzWs2hHVH5ihXY!2Hv_u|0_-jDoNo$esep;Bz?nqu&*RwE$ z<@#3p7K;Yae4aG?3KrW+ZV2?>E*B{=izmnf{84qp9^|#_YDZ5C4dkVZ@EE%Kc&kIk z9FNZj82@XxSyruef2+sd>mbKRWH`?b#>%2=1>S#ood9?N7a%YD@p`B+^zY6hIFU+Z zso`y3&Z@$ap#S;dTxluGG1YGOSY3lNhj_(Zc~oB_kPocsqnD@x{pX4{G-UjKXaD}L zt^s_%Q{{N63NMeD7Aylx*GB&*S!Bd1J^21aao?A27{7nyPq+U`{?jL32l@xkqH+DSf2|+2miLJ1d-<>Rvm4h>oV`PYhx`WWohsVB{~_O8is7L) z(LcxU^`^lh;r9qIa~9At!g(M zG)M7WB-@ovUp;yU?HWP+t#hs!t>fu(kULLRw4-|!s0{nIrv~}}_)yF9nSplK1WndM zh3rb!Y0aONvRAv4kL`KO0p5)5JSFbgOw+GZfp+EE@nkn^0bdjy_so+}G4sKsEVXbOlAc5xpo zPXPae!{XiA&{uKVTcQ|uE%D+h4hqyr2M1UzT**v`4^e2$K-gqJo6H$BBB!sZSTT z!+uSr_sP)}5)fNEKII+l?D-7v3p!LTC{(4?S~Yu5^QMq*nLyD8Z4xvLeIG!4(`fe= zK{-2cpU;2-budT|Eq$8frp7qsq!P%ubwwvO#7}zGg(o5)di8wrB z8=@uhnJTZTneZiFxnzJED~hhp5%3UouaVatPpN-#CLUJpLxq)jZqUFont@X;OhTf( z3Td0u(;)DXv8W93zK+0pV|HU=PeOz$@WpqHaX?Gr{r8e1AkH=+2^0O9Oa7VTvy7vKVf|i@570Tp@F?YJ%th>(PTmUAv+9s29R&EJY$>g-el?_ zBbvuJPEAU$L)ClYT#8++?w)6=4(FvOybaAKnv{Y5-q6uQC>UIl0V4!G1L8>6E-%pE zIM{l0dlu_KJl|-HNIH+GEF!>bpLPXDsH|7CIf%Z8ue~IZSQH-gxH*f1Dz<`)AFoFY z_}J*B3(>wd5w2EmDo9Q_h>B-wL~_}Zre=Og&|9}slFWn^-5U`b6q~&&d{&W0k1V+- z0%uw+5})&m5;Hg?vwV+yZ`xkZ_k{PCMM%`s?Ljktan`kZade4xX?l8TGfkKM2dux71okX??3pe`60K0k z@mDI#>1q(;NV7@Q+A}JucZ???TNT^$C^k2{io+yNT51Nr>9DvBm^lu;7JaEmp^ZT9)jP39maQd4XCgN zdn`VmR!al~K=b!@bMi=inj0i^8j*Udh$PBXI7rx@0zfB1Wd(Q~s0cfMo&>nlsS+CU^9nGyAv!mPgF0YLPBtOQmjm0i4dMw z6**;WPv68m55#KLHi6yuK=B|V0l)Dzb%TW>1`3+2R@!y9P%I6iV6{CI$uhXPaqYj(OTYu z|1y857146Q4;&{G=E;wh&|-eJlc@h_AD(!Jnlm)H$R+LHWB<)B{m=fbf7z&i(x`u1 z^^ws1a(H_D(x@b~4>lRa{W$*UGk)o>@oV9GH6hoT0`{sag+V>nN7zi}uLV7XQ4apo z6`ANBpHr~^znlO3qrb&J_4hpQw)A9`^}r1$2KN8@j3}>+-#;I}zX+&A`rg3z$LOe= zg;iu`}^AEAD7#Hao%{s<9DTamv0<776(cb3}f=s&8magO@-qraxtm81XY zV1m7v}f@D;4pY05#pta#+kP(6I>Ix02qsV4_fkI0=q zT*f>SCIrf5_Xr3n8g|`~hboB!)WhuA8w1{6a4cahN)gK5SB`dPO&<7{{albrTr60z z6t6lm40}%=JE-S6bLCx?Zky8{OLKij1MYTLh1enyN`>=_%0^r#yfe$!#L3+)Q;ybN zN6IpbiCgV*7TUQ{BLB=!aKb^oh6^=zs+c7AHSQ9qr`C+UI`m>sW`vdQjsZE$UMEaA z8ycUhA-f_MIIslLBRfgmlWYkW$fVqM8IUmspPnvtc!ZJBrq_PVyLL|5BL zryD!FjbY(;KHBkA*w9Vphyk}RV+BCzn;q32#{v?=C-NedTGb=XnPG?<*GJ> zqyKnZ7SYFlWMBWW|7-tc7cSE*`8@iM=a2sw?U^Np5^d1c0RK@rjQ-;}(wvX~$Ugof z*8keS*yum1U;puIKfr$f*pJWo^E>}w|Nol*(SQG+$^T-1dII}5PtIU}gdPaST6gpx ztI>bt!GC0@OrMklkN%_1ga7FBKua?EkFy{Du^;`1>(lB9lv1heeHi^mY4jh@s1ezY zM5I(^qyI=h{^O;rN+Da*89HX*Kg!wYzW@oGK+fPlvf?`WkDJkd^uT{)(Lt0-Wik4X zt26kI$F#`;QMix>2_^WC?QRDC(-9(1Mxvse)RWq{<-j@|EQO>I{J?n6mP(P{GK1);gGSYQ7!Ta5l=uD0svKdyiL$9nW1^-=%E^9%c&Uw*H@-Dm%V^Upu@ z|McU({X_qakN*Mw!{66W_m}_h4|#8t2mkSZE&pAAzxMxI`T6hGZ}*q~^^g9Szw3Yb zm;dzl_y77|=`Z8A{x6UJ_x9t{Klp$D7(ZA(IKTcne}DPk|9Jn8{r|1~$$yb=KjR1g z`ycYZoxkt+f0h63_bEFzbJ9O9!L>)PDgFs9Y;taX)KR&~_VL zk*C0!HM`&LwZ+YDz(1i6DSGbQS{DM_a`0_Y7RFm-YA%sdvE*BaIQa}G}#_vC0%SMh?o7TOoi#}3i1EJ zckd|T-f3dQ5GR^JfP^7JSeDH_W9O{8lT@H%l-CxCFbr0%O%>(}j zKx%ztbbASC&Zwgvz4q6S;j_KG(w+5st;@&DEVxpXp_=02p+wZ`qf_{KAJ!`g_$Xj&nZUpf z1*8CG%To2i=aR#@9bf2MsnScik1D{}x-y|EzO52UN^ucTTSug}!1FTZjI^O*T<3>We1s4n;6P>IL3^>_1j3=>PQZ{t1j<(w2`%=~tD* zbblN_+C#nY&h)#+Rc3RwM)P-z-%l8S{u;;M{cHS+4Du>>b~EMh5Gw~)Ag?{g@mJgG z?Ie#Q5O{sY|J(UT^Kt&*yqy4x|0Ly1Yiotg{D2%^>fzLm)q?mN%VD zPr8ey@%&xsY?Oye`3hg}8Sq8)?i*MqM|p*wRZ5@;+LLFAUBCxAMYXNh(STR=0PBy6 zQe}%zke|w6|0*ek{K8B$)WuQ0DqKy#{^5EL`79q0W8GntU+UXv(5M0SbATFxYHNY~ zP9E*&!Qx>3XWqVC+VV!>By9@B9Qnpcpup<5?vlofYF$u3{>}6ZG49z^hanZgxYnl% zKPI+HbC+F9LmcJ%a;{R13!Oo#9UT>u#>?xK(nbR^Cy>7k!46w-nJR&|u0VdJwhArP z22S*y?7X-|dHCsHm75@B=LqCK{gXZOQsm}oV6RW3d;*lBSnq6^KH$Wg=*1}Cg!iBE ztL0+_?1s%L|BfFG4=+S}-&@-Y{8WOSD7-7&2cbaUZxQLUOpach8*P8)UzMuP?sx?9 zpZO=kBH5!d0T#y_6!`~UMnK*@jQLl1hr#gV=EVgV&WS(Y-fSWCaN9FYI$-|zKzp~pN;?LFjnF85~5 zr5NTXJX1C2$DT7@b@xpLc zsIZ7Gp87;X4+IV7uD+x=0{^2HD>yBb z-6ezKA13=O*@XiruYUC)=`8!`;h=djha9_gjB?S%_PGyfeP1X_idxR$r4sn4APbo3 z^2;`gT#$3KKKkcyK&5ms(6E@Vi|zfgEB9Lw^3MfY+-WVA_ebm>(}Fjya!-}vo_Xq3 zkuLdSp0DfeG77=Mjq+{RRezT+9gwf=P=$T|-|`IPC4}C6@n7YK-^h3OUGaDMv46<{ zF0NwE|J6V7x?DqYQBR8|61VEX+CaTcj}~VN6Ni)(m{NdtbCz>J4Cke~1PAJ4?oDF8 z_8%7TEjW&}M}+b3;<{d~%7be`E`Giwj25L&if2`JNB)4$5*@O1pGZfC9`k$-oP#@I z6D2ql2FTy1iC0{Mon*Py6!fo|rAOLqiCm;vZ8hB8t0CtV7Q_?%#rBnFLvgOHG=u%~ z;f8x6+i|wuM!Rddr33J(ouueS!}YQ(pptPPagu%qe{i|KC*s8i#3;?Otlq3Tz`HIq z;NPG;KPgTdaS7`u2E6Bgi9h~=hiqzd4}584R&pi9uz#D?lk%lE*TI#a6u>a3YV@gC zE01V==VI^XuGx)~u&$gQz%LX#AdmQArtx!j@%9%jIG<;9@y#F*$Yy zbM>7BT!B5vtAh>sblu+u+|G_{L{`Qd#`9e2xZ4{SLZ0kFYoP#n?Fsc`zO`Ba*NiTC z%DjX6sqIDzX%D!@{*&vAv*6x^hnY z5EpG2(vF!l5Fk#fp|taJ$}P_e9iNMPPmsS1Xmz(N$j>)Wf4gUk88e~IVvw5<>d|Kw z#Mii+77evhp%&%18u>Of;z>f3nriR8WvEA;IRh!)EJk}7O`NLi;rn%&9)l(gz^?`@ z$NG~8Vl%W`*di6)0*3s5GVhf~E+TqQZW|9-p=;tGpS&9ab-{ytd+0W%+iF2oXW5m^ zCpY+JQriMQqYV6d^)YdP#KfLZbk8gg@I4a_Z#EB;89Ks)0u>|~K3;%fmWQa3)lO}Y z7ri#4kOVgpQZEn?{N7H4Rz~O*mk2DbV>n%g;n|Y_)bh*&cxl7jM%yfk^M>Z2} z)^$E8k#vVJ$Po?7Nt@Zfe}Ci{2E?;Y%~~r1tkG93gZ@>($cvK>FeUnpVc;K$W{@A8 z#}359AwJcaKs1Jiy8-wFbkaS1c}P1gC4olaAhL!+8(^fzj?wpQ8JUou%Q5I*=NSj8 z0I?{hbgGJZjVrWg z+LI26YYgPzVx(0Duv_JvxEjc{x|_h}z}~>ws3Kif@FT_{0`HN&l}g%O zG<`sIt`yp_7YAB*iRB?_5li^cy@1$Txof&2W)BT}QfO0v1{~FPzmFvF>}=pC$nLWN zWpF;x#D!PU9Ck@qhtn4JPq<)$%>%3?Ds4R%PJY#jA}N;v7q*WwD9}rJio|-RYmu|* z)w=fz`eb1LHMNI&-(7tQ8zB{yL~4k>$NGjdX)h94W#|s_x3C31J}sGfh|Z{>9$j40 zj1(IWs1xE8ccHdnw(2Q^>y2KWjB{Jy&`+(15rMphS}=pJoqyP+sGb-+_OQm=s%2j>@=SUTo8 zs9*N*?#xU`Y$eKj1$?^814n6_y>hFK4lcl=TsNb9u>$^^#|7peC`)w8=Udo++}%XO zyO~n5HLIpNf!h)G_;^l%k9hXTg8r?wu%EO(_)lc510EEgFIsK73o1T(@PE`B@L$C1 zm&=@TKLx%-^6>Fv1@k`?>```x=K44hCmHaU2Gc>CCgwx)DFD)?a<}#f5MBGgM+o_a zdAxw<$KjD-yp0#&FDxHH_nfsM79JV+ahwmoKkcNKc6O^JC0PM>m@Giv4WktU4iCVm zUDm*lD<8;-HSd}MKCr1D$e2TZMLEX@RoclVM>YZTSuFd$PLlHz_^k>=s1JD?OT8V0 z3ErR1C-$D4naAth=7F|{YfOhLe+-m*y$1O(IWtyOAb;xT4eI$zKu@Uw`GuRxYdGHI z5o3o-JaA!F_h_}c0$)~){6L*c&y+ka)|CN2=V>6j`@%s_Y8Es+3u38mY{*YP{Z+~y zxSo65jUx&I){YzKUwICstrUl3(?W?_T$vyb@c$#KR)9BKbD5}09NedbZT0$O<$9s& z13@64w}1x(vix%?0cuFn&i5PebxOu*K6h!1j-3`q=|b~ zPwX+hg8n?(=LH=SX3&c=0sXrkkC5N~o&NG!xU=NdO*4o(#@F&$27GX##uZ4JEMyuk zcfG3iOTZTv=;^ZfFv}BOi8nK3-?k5`teOMlf0MIK`sTf7rRDgZCie3OT5JIydkY*u ze+k~vz#BF9SvA}fzynS<5t8&P_)Q!x89k(;A$Pf2docA^z6lgdVVJaidG+hA9p81{*smm@L}uC3l%=- zpQHjnKvLnd9dzi#Gpl(9`WLwT8u%+u0=>2uUX^EX!vxlsXNOhW6X1FK6#>Y+0|*>7 z&Ud9+C1FM4(+T)plchh9%hGtD-3jo?5n$m1#Vvpj_P6to4!47<=4!wZRV{sXpMw3s z&l9k>E0I6|52y&1H62MR|0&unW!FWVWc-}?ge-`UDwA4+e~h4e^XD2qq?3%Q0;Zl#S)0A6`! zZ=HwztLu)(67HBVFMuTeLOZD~`?GGorth(>Dfk%A3L*PNe`zMiC%8!WdiSiol_e%FYq9>_< zo~uEi+eyNea`YAqynEGPADBxLQNUn7Dc#(`-ME6%Tv`)0G|Z*cYekk z-A;FdDofX+iKpgf&6=yDB|MP;pEJ*8s87HN*dKJeO^D}}lsz09@CAvR0{Z8m!c7;= zxc>y^%fym7cUX#T&Es5LgMB+e|KJ7g1Hf-lm%Q)Zk)jb zuz9m0lyqHrPaA}Km-ne=ra>SrfCU{gggVdIA~<*u0- zy;t@kQMOBWfu*c=sXQNUfIs1;9I9kZXWF6g62!1HLwFa)v?lH}&|=MKv2wXgS>8@e z4@!jMBaV(+o1)h%4wMk6@6fD~=1hF1o{jGT{ z7ogA(|JngwZ2oRS!WnNBt`)0}gLVb9H^ENg(?g+ROZ7)SHz>Q=NsnW2^G>ttTt_GdqqS*xC+ysTncg`{< z3TJ@4D!}{|_PQs1vQp@+g=?P30Bi#|6i0q$$S4d#_k@HpC`CxsE~+gtw^ly!BMSJS z%M0{xe0w5Xy_$yIOAA!l;w6Z`QVH&~1DbP%T*3dl>Gd7z8{h&=xz>#eNDNd9isuAa zKPp^&3Ax+D8p;q(Lm=7d!o+Z^HVSeoz~^DQ#sM$h`Vc8M{ABg${^YRg=`jDnRZr9gC=d`{UqiSLAXZA@@tU~k?Ue${WBUa4MQa3jvu1!4 z-m68Hyr^ORf2+TovyxR2i1%C~8!7_+-|Co<_T219c(~Q{1VmV+`r5$yZNqoS4uvcx z;$sec>(}=a;(_x6Z*kk_Wx@Wybxlt!#DTInfc|tPBpSCfbMQnx z!vZ?YLI1VeiU8`x9@il)Rg`F0z$C zV0X0!d30h6qKC$Y)|*OGA^#8f$gRzIwiQluT^brTL(iatc=R?WDraoBA)4kTWBD-Z zPd2b4F@V;c7Rc60so2k3;8$OE>=>MY|5$P81CY7rfT}kN^RGp4q7s9p#(>WN=1+e_ z5y20gqtVvEz@Kfw{QY+Rd+i~q-0I2p1QbtZ83OyEF|%>V>>PmoctP4uq0bSY{5$*e zNB+Qc9f# z{);|)KiIxebzR=yoC}(EV||s3C9pqm*&GsA<%8AM=zpSj1ozQs@s8%^LsS(=hZ|62 z=-t?U;Xpm*^a$}6n}_;PDBKlGzJ%LrQQ{>R_PU+@1TD;30DP@>r1jbrEM-HFHlD{r z$d2-4p2%4M{O2#}kWf{M1eKeiE%2XT6R5sZ7~r$nr7p;7&mCxkui$@LwP54h4WAkC z8iM^QA8>xQA!|ZA0{9=^+iq>7s!Q;n!z&9?2!tr#@#G{HLk*g!CD6Wfw37x;H|`+8{!12nyq{ir=Ga%_4XF$AE}K7rA9tHt1KfXP zYpaKNXNfgCd*C`99Br}CUAlI13GTZnp+0RvPNzndy*hylcAEtX%PruumB6+#8m%}@ zs!n%SM#9;H`(RW!)$Cq6HOU|a04N4|+iOiaEpI{6pBK&o1qkGY(q1KPx#TtNX0w>Z zfe7`@j>B=<^)_GA`Rf4hFHfL8U^hQ+px}4EOM1k?`vdvFlY;u3^#1xXObvjLF@p>A z&IyDj7p(?(Glj0mTe6f?Kl*U<5Co9tOK0Qt5WSuu1)ra^-*xw)?RT?Kd*-K@H5|xm z*ORo@idF+`#^wR?n^7Kuss)`+SJdl64dRF45$@aTg?OH{_VuV~IKN#We!`sx4G=yr zE#e`5xk+Ter&suu;&LOyG0S|>LC_YZfV^~DQ?fk9s^BOKm+aSX#_?-E&LiW+=z!E>d z!2NCf_6W>X+=T|+O@Q2&% zb5YH-VL0{F85$Mh6B{_c|MvZnb*j`ho85fv{Saf8i=+K`@T#M&CvSnSq#E@9O8&d? z|B?SQfBzx+lc;bX&{8hR(7kU2A$Lk%a374_sQs{ImDb4OmA!>6tlxx&j#lKbtpb+< ztUup@^>)W$4g|g39RVF*Q z@1~dI1=}OIZ&SQr4O}(=08?QnLq2=o08h-4AM4wUbKs$g4)%u_+?Bd+!Aey*0GvB0 z(j~hdPD_v1V9!9s3HA@IiKiuG*rqLM4-sMa<0OFko~e5zWuyhvv!K37)dP?pB74U6 z5#VjOD^nHJ4RuWi_ckdx>(r%iiP34wN~Qz92-?Rxg0}9NLL#FJl&K8$dFv)N+3nCQ zJZ>`!A-}l37k~%51AT-3?lSVY2H>Ab2_V+_$^At<)eUI~TR+6-E{hPCa7jJes*~j) zl5zi+Gal@paI-C7_}JM5u8hi9_Uios5#SUdUR&jKeX6eB-u0Y|DVGN@H1#-ke{D$*tIF`q&tQej(`%!C@Wb>s-47TKIk$ zqsM~D!&{o~Pqlzc5A%AyT zOdy`S795dha6jOsOHBCpRZQQOSPiYY=rx=?P2jgVD9GdH8+EUFTfonEHiQ1;u|7KQ zfuCcx)&bA)PT;7O2wA~ zSH2p~NL>18@jV9LGwHVFNQgPW{b&on07q}W@g%KZh1!h4{jHqBjrf<>-bLrfwxKKg zMWNn&5BLc?m~ZL(+$^CYTpT#y%j6OY!KLFMzmAFzwMKZ=2~XTCy2q*E_#?ATzzl4y9rKbvJ3L!(i@;uz{J9; zx>EIVIeIFb?Ivn5(4d^^{)s zb)OeyCdLHn%}-{6Irq3nSKI2HL%W6bf6D&1=KqiUpZWX$pZ@HlO-i2|6F93^xd&HB z%wi}(!Ja=Ta$9mryp*6`OgWhY-YpjI9&Zd_utMM zqrP5MjcmRnGqwsrM6lMJ&vZQS{iZ`rj){gU&bkj~^m87_=R1NY_8_R=O$vi78o7+n zUmM^3EO)hqdIgIZRWFMG=TB8INt+jhdiK(p|yIWk(P{<$NM z^7h02t3B6`uvVdQBh;TvZW561_%1^|X%lF+IftD}_#d7}Tj>s?t36;Hq55&@LXX!h zYjV2ieJCPReAY`3_aEo3l$PIlTm9Mq`)BmT_t$v|_wS{}aJ>WOhWGi-vpErYFT@o0 zmryIX(B@hxh8yYdfkVPU%Pn_rSz+&=rpr2E(eTwj z6()Y~5#vt>(ViCREhg#T`1z;_=pQXMC;b$@dDa#$jDJPog=9EV&{g_0as{+;nOh|- z^yDZ&w*9xdWn|2tebw5JT5(k^MI4Ka%XP|J2z<#=tT+S}jj8eq5lx4-Vm~mBa(a z%jq5CakAC~*PT{|BCJLBTGaUb`G@hwP&8~O_!wUtF+MADj2G9Q#s?~wlJb4I()sQy zzPNtPS3^}bfjyr(KHrVP*a($cU}^O-a_yMo{C+9zn(edg23PESyOPghfzMyf`26B+ zu8f-Z(LdJ@;3J@J1b9k2Wpp(q=s#a;^bg~nqbwS>P;^`tw}~uZd7yBCW9>OAv@^WD zJt6NK>jqc8b<3DtO{2vs(BXEz;ru^rkq>v7Ed%wV2WOEsu^*D!y2{}LMy zfOBZ@++qHyJLup3asC?b0*KP?@x%OG{>|Ur-~27Z3;HiI^t}6#(>1NRVSK9iz?DjU zP)WZRw;~;Z{!==p6Z-dk>KI?heZjX#p`SUebodzx@L%`TM*XY1qyK$;`Wv>}l^ITN z5`aSU0{Ro-xF0nUnTouzKKTu6mnZ07@}qt=-~)OWeo`)8YpD9Vkt1UIT=FCW|D6({|9~m zo%G3TjlP`{fqm;3*Y7NCVMc>ZQX)+0I#>{Ugm-#F}&8Ggo9)XE@?Rg*oQzfA`$&UH7b^R)imUZib* z7x+69(HGhn_4BBxz(+da`9*@q_67ZvyrBDs#^s9jK>u?0N-|i28!HioGxS3l{8@BP z0uk?YpSxp7%RTa$y%PGPm3H}pPvxHBCsnY2kR$XL9|PmBL%FVmQiOhKtkJ(7#S_LS z&ciXnA}|?Ly3n7%sEqO9{z1>Hz3J;p0sYH?9nUQOUEG;ReGdAm%`>Nico+Y8Ne>q$ z3Fx2Z*@J#&f4IDiH9y?w=^1jGY?;unY!~yHT^epx2>^wv1UW4QWs=p3Bp*& z(62y!MU2}LJnFrxEPM{WVEC?e^v9#`)v83~tYqBmJ&!mq+e#~Be}~KPJ8T%qI~psoJ>_CqlqIwv@5d11DH?@4FSpIfsbyGHYP>O-4MbyJ1>bf)7mcw%1=lWl-l^S-Igh z%Yr)}zA9U`37LoCQ@%L*0pAUx`L{pmL0915f7-Blbv?|+tv_aZQoXhqFIc&r_fMQ- z!8KvNlbU${Nb&itxq*K2?!ElZT{f|*gpCVrZJ64yo4FzqoE^(I;ve|LVEYN- zxZh{_@KnH;%dg{!8b7YGf7{mm>gr}#Ga)#DFL&ALjh3|J#q7Jk&`1*D{uk!k3pIdX zv?)rHt%eV zU#*tV54hGUnQpJ`RgVjK#bnh^6Ww|*_WCN+61J+mMXRF<3BItWY2m00eLEjML&gOj z>QQJ?Uglr-5Jmktpgzx?V$5G?xng~&*98rC*}~U(0^LLvoKzOSy00yE9*oH0TDd@f z8|OYRpUK|X812NGBKX_Cl~gp!vVGFyJI4Ny z;;KslK4YS=ax$_0(Cp9~UnO_xuJ^fIR%@(3ZE*2fGA7+Do{z(d1H=^i(Q({DKUyn| z(c+Dc&HMyDTo55Pvrw-l-46zSzv(S&E`Qf9J4(M~7J6ksuOZ#y{<}{F{FR$^GS)P3 zLBMV?e~w6j(|e+CXnavIf4uIGZ~ag6ch0L`o3}4c*162tFqCI8{>K7U-)BKYlivmN z_sTH-UZOs~)U0g<%k%Jn9{Ya6`TJwH{4AsY-M!J@(7a=6L*5NP`;gpwx_j67{wwKF za&Hs*iX;zD;8DYo3sJ}4{7W%E#Bp#+V|mdxOf~WS^`s}geup5Ao?f-3kt$f8I8Cr^ zX3nNj&E~g6@SXQOP{Z54^XcRGuD&$(0W;zk^uymy!t*zQAG$2hwr}`<OBp5InJ^bm}rzq{$KR!giKo?jialx@@QJI_CB z_;UO82Yhhr+vXFh@{x{YzYUXtfW!Nuto1rpYiPX1yOndRzvutIfBpX*|L^ttzc+t| z@~boJF>~{fmcxs4^WBlk@f|#a=sq|`Zgp6H+Ap3zUO`P{kQ#@kR%!Vhjj(?;3u>|r zi!JcD{?@^actuB>63nK(9 zT#WZrI^8V^VFmBRF&qUnr6Cw`{-gH`LDQ|7XyK!>#+@vj|B&8UG9R7NO5B61$@$%w zQSz(h9L224G@77v82?j|Z_(X)OOLkuAKh0ZVLU{3!mT4{FO#A&JQHN2nAPP-%l3I5A=WS=P+@_`E#I}`GmbM z+3!w93cCqYmdSo5e9ykR?G4Z`tq?vz4*e7Uo3e1&*(sb#+`M*IP6tT_1d-!+Zq@>x@Rxvwl(UF8&4nD?GXpI=xwbRXTNLYA;v85CAr4+e^k+j!8DEW zb&E0&st5x;HN2PW;T8R^hB!4VocUX!G2FtRiG31$-P6YB*)nT1H|cSQOAh+4#mkBf zob)~+s@6=?z%}Bzm~_yua{8zG$x)6Bh)VfU^>=h~Fi45IdHRU52&?Hmuk?blUIkC?!@{uqX>x0M z_5Qbi`T4jM{VU1BC;aCOK8%-+EJ;-QGZ?GQ1@d{U!+nw;>qcaoll;?Eeu4J#J>D;R zqlh>7W@(Q4V*|x0X)Y)rk0NqV_XseAUv{4%g9`XSZXcf?Ia< z;BWdUBjv8}zer;mIy%3zp6sK`MS`X6`!GD@0#j$C-(1~2Q5#Q}X~FUh7-G`{?Lwjz zKL-Enq00Gji~29SPBZH&8TU{7BgbTH`=i_I0@q)OaQ*A}m$O?S1oH&V3-osyBD+;F zqM9VX23K#cy}`=!5Bd|!o8UYDC001j1Nh8U4E@MI&R><@H@>t8)CQ}-mt*(fZ~oer zy)r(BcN~sg#5CpeH-FU>=hsVrTPEX{<|Pn`18Jaa`-~oxg-X7y28nmum2Y=uHmg zl`_F!!;lZzoraF30eMxujkk94aCToRwUyI~yK%SI*eUTb?Gz9MclzzCd(Yq+{_e`y z%7ch}3crIsaP_k~|J^ju@1%G1b0rDlf=Sp-Ddu)&u=naO^4x7ns#_L`L*!jTzcbf( z{;I=i&35N#nRIX@t7LlN{_u}+r3sJtG-!7_#hQFx;rCZ^9OL{Oe$hWs!8cB*_@7+= zf5-oO{r>OGp9et%@7@x>GyBt|k$Q>ePw)6mE>5v;YP<{v*#kxTM9-8dHH1l(!*PZB zilPv#ADgOtv(r=z^G|oR7pdRi!*XZK%lRQQTy6DKMfRpxKXuo9OlbDnRVZ$>4*iQV z)nQxZk=M`)M#WaW2K`#J94LQ$1TFHKhDd+EvL5`7u7G~-XlKT8gnn?9tXp5EKkOeB zSIR88RT;vuzw)7NMwn*kt=pP6&5LO zhYtBzN8~jiswdDE+Iz+NsmA*ITU5aAZM0Q5jP0CjBzQIOA4k}uh6A&IJa-9++ddmQ zZaeVg;;fA{L|kW#Ekf|&)!gxnmmlLq@HV7Y<<_qg^6|Dyw!0ufjcME!{UdP!|G@6F zgMTIV8*TsbAM9SL@Gp!*{V>!`vI-@HBlz0xg?x<4K5tmQkRj{0xt~b<1M(qeq-NxT zGhQ7HQ3vqfbRG2nI4?eoJrWed>H8GGSHP~$8Sc|%`;Dv3UGM-uJf3p=eA|7ITzZ*Kk@m~4gbHIh?B3(5n`(w1w0*jvqRsE zQ}vK&iwDP?G($iCJ%FF|{`~1f%iWyg2n#(~cPx=e z_zWJ*<{M+YeFlGSJAVoI8V{)*Vg??WpG04(|31hdSr%6hZT$e_&$3q$m{uq9!QEhP z_JW>jX5a@U>7+9-0+oD*xflAuWAlZ66`mG(^+mmBD(e>=k4ud210U6^g@%Ajnq$1G zqW}Igh{n1RkMr<;S`l~Tz*o`Z3i{Y-MZfxZ8K5VnGxQootHU3{h`>Ctg`NyWW$^fN z`T?Kh-nV`Bi)8L5LoY~T2o<#cyqC3L7kzh2(~AyPhy19J_Lf#vhe%C(Lw&p z=e4Rv_cl7*8@UE_3I*b;iaJL+<@X8d>9c1WPlu&sX(`AT*NkPNe~;kDnQLxfWtyI5 z2u5}_2fsP|L(8kGX~cO#727os5(c5u9 zQ{_52Hs|>lkrdYWrtky>3E@G3=D75IWY6Sxn%PycWUd9O2=&L3&$U9^s>lEZk4x=^ zbw%hB*MEE2%gmn>%%|pJIb0s2fBe|FUOQwpcznlKai?73@wxgy=}pH8wxQAnc|9ae ze<>lGY}bpRh|gqIK|lA8-(Lcf+E=+7?|_C~!w&}~%1ll!{6#$f9l}v4pvP@o%oqEm zJ=k;piAnE#M(|)vJU>8+$@u-N@bqVXhMr)7`9=_11_!mOmvrN8+=@KS*yWVAy|bRf z`^6B7(3AYyJ?aPBs|>!x_w4Ga=wLm6h~OvQ8tB`FTFj}ZLk~ob-5osoe_B7dK?L7I zPNJ7u{jDE!jrD`mx4#$Z9e&d{(}?v$aq;>8j{k@C_uuRHzh?eC=i8Z;5-jxJkYfMZ zeu@WGOdbA$?`PYqDpcvzxD|MQqZsqQkd{s_+Tiz-ACLVif4$G_zUtmNBl{idIRf}| zv-+1`G>)X#vc(^t_Gx)&&#evc9j@-1ZI$cFHo^?+72o?A`+n#NezgpI)arx;I_Zxl zD?1-$rAO%yK~Dp-p^+_-)+hju%_wKv;QQ&&dw+fK$=(LSdLiH*YVNyfWbjyB__H<# zYU#J^w8nnIg^6AY@K<)12>BRZzeo)Bgh@Vhj}X1NrD&-bcl^KF81#Ub+WdE{<4(c4f|Rr)O+8{#h!bgoQ{%jqxwqu)E;`b#(7yTp@>Vw}Wo9G7>8}JKns1A^o7%I$r znBXhdmWBSmF*mHc)EGaO`24F`cHjXQ`2OoN#&;)-mrB>SjkSc|Kf>F?(8R{)13-hC zk5Z=e4nK>JA~Ez^+8+Ek&O0uVul>GGdjF^<@cEo0xbn~4i3CW~W;A%iKOO9#U(g#K z<8LF*FVY77b$^fkq0*w_sz$~5FpiEkf#1-gi2mp81wVa}OdLyoI`X-!8RPxBi0E7U%ePWi$fvrK%Y}-` z!Jd`k*=5G8<-i}Z3FEm1#_yHmlEudX`mIq}^04qiOW(&HYsK=$R%Ge!@cR|`;Z^tW zMZIhT{mA!#cQ=yWMqp1==n-z)FS>c|9sG^)XqNO-@0)RqK02*!CD~qh{RhVeYx^C3 zf7vV=5-m?K=1u|f1$7?d`3LkUzw`ak_Th>PNn2w6jPq$Z%SYUQ{hH+y&i5ba2NB4A zBIdwi=4}4>B^pKl`XA@-APRP(od?wW^M{Q@#m)zR;1wWWT4<1$x9dR1Q{Dym0is}1 z+O#^=#1~?^z!zMCU-hYR|G(p_H+I(CvPS;nXL|=@KX$2KVyGyfRnPiet3w`&$bcf4=B<(ZJe){FtjN$)(coaV7FeJ$ZGv(lPem%)qXoX z|NoBvhxPa0>-WEA{zz7Rq$SD}+Aw=qzh2zm{*#fFjb_sCs;|&|VXxEkn2^D{cjTd? zAF)w9(kjLuf9%D(@UkiAw}u8W^n2&W7W}FFihL*IE)v~}(SJ4eTl7EA!edXmW`AIA z0_25ZHV4#8uJEJw!eOud0`#IeH$21?u2S&8hMUD}*Gm&J;|6^Vr1;3^A>+xWTX~KC zCkL4?XD@JGDt|Pd$6dU*a`*JP0l$lc30k%%M{ho6&J=dvCB^|7aZ<(NiTXBMYr3~~ zz_Si73);LtUwi5fam?+kfZ+FUcZs4Wex%2m+aODO9ahHD;<5e+p+r|l)AteLjq~TH z-URdo{P8>ryV=5d82*uI01#1oXP%>Lbv-i@eEnH(g{1=%-*<*!x=zpcW!RAfqf*GFNX>x!UTB0eFOgE z$$|ex84J{p3!X!!kmuUSLs;_US{%_odv7s*6F*FDi2m{|-=&XwFSbUcM}U7b4)GUy zFfCRMCvzMItpN0I4=?MC1z$6EeB7Wvdw!rlh{Z=skG}=_&ER{-+&())zxFn}L@KiS zYh2hK{KEVO{acV~@UzEv&>~NPiQ`3x{{MLLOjC_p{Xr@4{^jvAsu}qe(8@$* zFzRCe>-OLYpP|A+zchvZJ(ZweTd!v5DV7@szl6@=k^OBk{%pbhz0*4A)s{Dz-ET9- zi_2|c{65rXhe-k0mUpm!3YvdJ3)LBO(Q4q|SnoE_-?t?PkaOJy^tWH7fSz}}xvJur4imF4^nc7jzVUf)^2{sO;3x4G?*CWw z4)lN+zI;6HpkG?O+1S#V8?FX_ua}I7&~K9UK`o8vp{@aw+Caa%dqY3?kMmbC zuI((v@Z}iX2l|=G;|?>q?ApL!eZa9OAEBQa>}SahgR0Tp^?Po`tyQL6jj$Q2g+45J zIP9hdzOco}Af^>(EsDsP?LK6ejc?|}k)XdE7Mw+IFXUVE$JShNiGRtcmfx5yJ`nJ0 zsDI<MXxE8l6z-oOaL`b5{rYOOBks6^{;jk32wg%}4_MAFO*{-GDFW$8Kt|(DVm`{^lRA zzwU%sKR&Xh8oTe``VnIN;1)K-7TMM8;di<(d@_IjtKa|c_h`aN>BOr=<|$xZS6HP3Ht+*0|A2ksq?QtkwwN1>-i){y|T%J^J&q${|40;ppHVHLUwk<_hoab%UaQe)f2}d5+)-3QfV>`G zTfp*@Y2%VVGHH2B5?y<$oneYLLl>%D`9wN`fA>2~40=fXo?)x+UfgT>@hRdLp(lSo zz}h+{UCySF#+AOT+rrpS8;7|u^jh3^i!?r}^cu~07xmAdwh*}|vN@@Cz?n^eVqDc9 z@S89#?{@Idt@Xe#QF~yn_;h`JJ=S~Vfq};j%;C$QSE{w*O6*uv9^MmpGG|;rAEX5a z)NO>kjX3BGqnn`rY9Fq~9rQi;Vc&H?58kQ8`*UH%94OotHHLJ?_={%gf`a`@Jcr;l zz>2~fQ=1NKDQ-7D@~$!dVzMm-d9Cv&gxtm8cNiAVUz<4*GY#YW2Kqc=z+bH+E{Zo0 zl`=5LGcg{rMVDJg)s|i~sq=@R#OEK=9mey2JX(ayTRMue@Q(;J#$W!%OK5g8bHaGz z@;Bbx-|_t`XO|D&4*YFc6hG*HMu{!Vf|KfuPeKO3cLx4j1>L^E?^bN_LsdjcP#opt zYO|%;0*9qh43RH>x-CC+lGbKY~IY&O|JqvJgj}O#8IrLYp3H+n8`mN}0zm~Om ztwVnpYf482^8_!yb_z4SI6MqiRZz^5cd4F?ux&rg$93@)PJa!Y1cNip5Q(VG8SjHn z9{w;sx?=wXPkW`KfU2>l9zp++R$grY2Z#HIQaJoe%Rl``^8gPo%GG>SE9l?qWC?!X zYw@c1wEv|Dfmx~S)t{ihI<6h?L`MY;{2HDarDqs_)p?oXh;X#~tZIAmd>B3Qb;D&o z%hs~f>Ef!ve`-NL0W$rhZ&I9Z?e2)4z4wLw4cf-6pFIkn{%VB&lRUvA;WG8yqA5Ms zx(YNEYv(Sv_2v-;+VqWL>d|`il}Cc>e~#pmF0AMW{pdLW|5P@>|2u47$tbL@rfP21 zKI)$*+<$TpBYlmQVlTB0@<+G?`it|UIM8#HjcwN>&X*jb#g`iPDM$o}Gg95Z%prTx zSBCY|=W%|&L^5+OWNzi13jX0g&R^~prit);?Losjz+Zkx!@1&G2M+Y*YjI{?0e_tK z^T2<~&8NMyQJskt8Tk{ql?7i!T(aO z1(%FrETx(+yn&Q~K)izFWZ(_YNFTktkYOtqKWWW1ZP3y55pX`UQ|L#oHuNuopT)Dl zLARuDjCID|Lvo%uX+|E;`TVq)f5;?XPQKtT{^R!_@VaXYrxEK1$|G1mAYw`MMeGbl z8tj(L{4D2IgGKtkxPSkS|M&X+Z<#-);nN&l1#S49ke{@ z_a#dVg*c93n8Ac~`!D|@ow0wu1QVd86r_;B$^!5Mi^YddEDG@c>n%KeJ|!@iwhKJ} zd)e9>&b`%pb?45Sj2PjX)16aBuM3B7e~UNv-~Dj@QgS=kc%QeuhvpV^%bo+l^Z#RJ zWc*yq>;>*S`||IC^~a0Ag9r{ng!*ApOr$w~pg)cveEJj2AqPIFd{(C_^v6UFOI@)1 zbN{{K93$5T*1zMndQjKp8~^)syBATNzxCe_q~eh|lgF1(ngU#nnC&wdDI=u-|Qq{m<`r-~oP8sTa=M0WHoi_Wua= zSb8{L8P30T;E|nz{ePNm@N9ANS!u1+T8#A!mej4Z{Q|(ufNFD8J@@u-5 zD!0(}xvexKKg;Gew6}clhk|Lc&>!bVy4!(3qMj<`c=~};GR=Ji3BNyJe7|v9==BEF zhqJqdhvP3Vp;;y9U(*Ta^T#{Fc~`nmU@3AI`md`1=X-&u$$X0Sj)?aN{r8W%%8f(} z;D3yzfPPdV0KbG=WE?MuC0)ATIZ?-~0)MCv4tT$S7iPFt1y4Q+^1=naTIR;tb!(kR zAPoJ!w_BUj<1ywC`0|%gvzaSs^}+j-R?VLA^j6yQsSafDQ+R>O@@nbB`gAPy&O3(m z2R$nI+YR{I!t@*{jb)54#(B?;UY9ULw-re6NeZ2m=SV<)8Ti_|cY5t1+1DujNHfMu z&H~Pv^QA^aPBzcl3m=J-%06aLHXf@XUIaYReyGQbi;dIjW5IXuAJY{@k~8!dNZH8H z#9Z+-)V^IJ@1hF%BSn2>*t|E1P^X)ctX;&wYBj&nxHYu>hTUH&?Isg?EmA!m94t-n ze<{x=($qQdnr<8T!}-K5;Gcqln{29Ppwc;jKU`X^3*;9~7x-Qreao z%`6|@fBF+0er!QfZ??EG)DHPz4*VO(^(Hrj-TGuKQheW^0Q~`u%XmLg?D0dhw(4ii zxFUa0a`=h~`YN@>LMj{*#}V{){26A2;(_y?6yg2sS2E1M(T@ScUzrl!@A}DBUm3># ziVQqs=xWGZD^Ze{u;q^S|PR=Ji@bYOt!p0#g@A#_ z-p`Dt#d#6A_iNe&2k@7+w>agr_2NhK>t(D|#5056`mSH__bC@!*!n>Sehu_)(tYLO z{mws`1~u58eul!eIW2y?H~<4pdZ}b7QP%Rq5;$)e@4A-P~ViD zKYjvtqm8|SUh;fg5N_p0J9;t%VV1#BKF-hu4bl$~U%(r|X(G z``|ep^mnw&LQck2Q49!)gZ>5ib!+&;KsYqMhc=+~ zOp!nR{*H)k-pkq5u-PQIk|87ViU_>2?$0!~0vsUE=bdmx4=f&-K( zeWq4ZPJHlXkqSzIC0n+})PTR_3aUh_Qpfu5fqcrX@;IP&xa<=30Oks3F*#!6WsH+C z1rdQ1*} z$RE>q(QFNMpnLTfVgGpgQ<8AO@c;9<3_dir>U&ml=+ik!4Hs;6*{KospPz;Gi}>&Z6kak{vfV@fAGW6}BT==&dGmcSNuJ{TBUsrA{T&tjn5Sj$ zXt&S(%5a)7<3w>VX}(qXL_sDM>UV`3F$0jDS*nf@EQ(K4q=tF6G-M{j|E+JN6Rj?v zFyVfjnQ_z7&FN`k%Tq z>|&-|C=GXdkP4|%lm_RAG8R{~cYRHhuD*`;v;+PZS9bSGeIUKgI9cG!m4^-VJF0g& z0mAr(95Cwe{&J3?{|g`13IK{+(}dRs?=S8i{62Aez9&kuxMBR~7P9Ag3B?^>yAk+C z;Hi8Aiu&Pmi}CThx9RJ-cUmRb6(TuW;8z=UQ-Wh@-6!%G?SM7l_L;QY3J4*wBzJF%V&V)ZDLP`(! znl3u6z*mNz@QwTnO@)5aaNhv9oj(ckY)5+yeu{+gN_p%Es{-%@x9d@p=P$-;k^4%K1YB0?beq1UynuK3RZ{35gPlAOOk9Z_)+gle9AxPKW-CUXjr-oW z(lJMV@bUmGxtU{-dmZ0&+ebLbj%7QB$J@_({{t!V4yHZ2Y-E z01Np#;5YHsSXb)3M&3*DME(dY&42k_^~WLU8~WL(M*gfE=->w&w;rWcflL#jX^}ti zTW<;~cxsi%^?tlD}PoyfKca6n8=HD2ifBld1S9X4P=vOVwU+IDQ%fSDG z-yy;LHSIO@2c3ua=f@v^ark(c|0_kvrS})_&yaFr{%a}5JBtpyc~o23Jwkr79vjVx zBq8!pPLA_ef7|}??<8+YA!;4Kk5oXXS#OblC#^4KqPZLJ3g3%-&HAoCC8QnS9qGAS zi@c*@Xw_YT{0tzPu)eP<4J8HyM`ZJ#^;|90zy&VFK3kxc7((#{8c||3;hA&?gFxsaX{=Q3;s8= z`_nIgtU&86Fp2WC$Me??;0H>_v7F`Isq7I9AZ=NB4>5mj_q2!qgmLV)K>bdM{`i;w z!S!GGxc+j<7`w=9hJGT3{_o%M|FHi4^Zfny`uz`_Kg(l`_fw_QePR8@{`uHp{}J~F z`2BC=78>i}xMR;%f#*-Yx+S@OHR#XX1FtE)co?|i?s@2Lyg6V>`)vw-1oSgwt3PJd zwT2a$b?L;ODVj%y;>j{GedJ|44-V?@?gjb@J+mTc(NYt6s7v_65)=F?@nzMs?7gCp z1P=7Ozs-RD@w-sKe!kWgc(PD^Devtw;^<{6=;Neg{kb{N?D1ILGhb=9k-LXOrsEizS+UVWMGgi--+t zXb<(Y)+5r?u%zuiuVt>s=Q-liPlf9@LS?9;f|;S8T1@EaibJA&PbK`K2R>n{H*71? zQokU*$(!sMC^q7v}~6 zt9Hk(3laHG%?|zVR}iK+pWanX@^04$In2U=DyjUelt*-fciYn%_S4UYgaiiaD<@G> z*)5Hr|E@0>zxWX7?`i|r-}a$@XOn?6*9QGV_3lMTmsi&ut0V!u|Jn6)a)7~t{kR$W zn||cMrEpXTz4OZ|SHgV+pTn!ssJib)TRVN@6+vFio`V9!$9_aX!xWR>y5z zd?3jSVDPbSG6_sj161b%TZK)-PKBFh7s zc#Jn*@P|&u=R~K;b1S))66DDQ(0~7A1Nw(|`iO{L3=%^gs-`K;fcaZ4c$S*i&thrxA?B5#3*Dsu1UX_=&+UK(Ul4Fnj z*&#K(N;Ns5CzlWWSr70$;Qx3&8nQ}u7v$OK$lo44==V;V{H5Ppdd!vI_4go~hXMaa z(s`Ia#*+{t3*f(;u>VRk{Ki$ZMcy>hz8|C!w(yTMFVEH- zCa`g4~}mz-MU31K#vKG{~_7e4F4rA=NT@A>osf3v@V-zY!Pe^8Ka@&NyNFq!&M zpkI#sF(QHm0%=r0eF zE0Fi-KG{V2Qo;H=@oPN)>zzp4tppA7$_0PWl#$1+*7}RRK707HhZOwF!->b;oF91q z_$cg3+U#(ov*CKSY&aFJ2T1!2@*VvCqK#{E&XzW-HXPQkO^WMpb>)ZNNfGCA5yD^Q zq)JAI(DM@6R-B$#m*mM_^N9Ob+jxFNcibSaK7qe(H9~*6%AGfTZNHJGW>77VcnSTY z@Z>g${2Aemr)$It|Bg%OFZD0-ESG%=KU51fwtDmN`TxQ7uOC=HWUKtbFRo+#umxQI zf5-pB`uorG_uuRHKXCr2ALyq)TH&>dw7I3kVOT!~vB&!JZmo%YH^^uEZnk)ScH_e& z1oAUhor#Y9`j6kYxIpj!p5q`_hUFFi+4*Hu<)GJBzG)H@fVVpKm;=1Oce7=Z&59RU zb=1{Xbv(R(%LDR$Dwb?fcnJG1S@GaEPuCv)k#Cehj}!gnH_B`1=XvoF5coxqM}|cD zRw*RtfAQ+r&~7(vHSKl+|Fu0U;2%3DKS7f@xsvVjZ&{wrgN^=k;^6(&K_2<~3;y3m zeL9>}1Ok1c0SO+|9`!+A-ca~2m!Yk^FZP3cRPa|d^v2-dovMax7qW`_5B6=NMQj?^ z)!-FZ^F74*Nr)ycLbd|vzG`th%kJ&*C3}nCGBxVKU-o{pOLUcgv;HMmEw_kJe*^=6q+R#g zXlgBjUu;|jV45MWoaNi}c;NgTp+1VltHjuDY~+t{=+wUT)JA{m=%9Y0cc@`xV0*~= z`@Z6g8CfvHyx3KW16M0iqwc^A|FJ_KRYZQ&)~N|=!+NRdq7D5WJwA8zhhinpUrY37 zo)HX*ItdOoBKY=%gpGM-n)H`f)q(d)$o+D}`}iCEQPdP<6lDeEF;{Au%XN(LR@D)z zc?SG12#b1=jZ>f5fHNihlk9LJBK_bzv)in|&kbkf$ED}nc9qb*txx78L%HRctpwjc4t4BI1>^Q2>i;v>DcWzz#mvY zUVHAVzY1p7qMr-Nk*vs1R?r`_<16(3%Q-bTT3y-Gtj^$0A`n+^KMgu&N90EK^=#QJ)XmXE!fB;|)X}dvIxH>qsbZwRQP{<)oibpCL2!O?wHv z9dYqH<)Tt`6M5Cpm&xx!ub6d6W8=8UedJq&&_`IvTGLOQ0I{QjYP_Z^&{qjZ_#LwB01lF$Txm( zADWQGr>lN<{TsZ?7v#IvH?XeGrx^2v?r{HS_b=b|c6qWym7yWzSA9%q2FyFR+3mom zZhO2Cd7$So^a5-{9^#e3A9R}?9am}P&*2quLV^74o7Fp-6!_PaJU-~3D~zAPTR}ez z{%EaY-H}5+?@)TZzWU((=5)Hqt!$r=PyLVcSGwz%zr%$2yOydu`=)>M_w@D%Y{@e6 zUBt5te_n*|zct5gj=iDE_K$Tx`BZU|>3e#JEOB;8R~pXe zI`Y{ct^(eS1TCznAO@SrE7S+c%|t`~=kT7fI)Z<|1_z(N&tIbEA|ag>86=iU<{@8t z5e%$9oQ?-y6msbMj`wpzr~Rj@KELsh8G8PXVd(0R54=&&sv`al3F?!H;44m(g0D1O zp%9?` zUi=UGLbv5(9SLe0yGRxE#eswMf9VGR`9^n&Jl-+*LV}0=PqZUXn-eezjJUEl>@%d; ze`o{V?Ze+94@wE4&u>NO1K;3dFdHOzOKtP$rk6q8v5wp_7$s= z^vPMw6YRfet~K0&-{TDAE>FG$)WMe&&}mbpieV;;Q$aQM^Lo!deFVOp_T0U+A9vof z+jYeHj}VbPXR5bT53nqFc-baY(O=tP{XC^xe~vb`YN%@jhc(jmWb4c2>V+L1*xZW; z;hOS!y-cp#ZY8#;epr9g59(v*8hLTt{&CFbWne#y6trFTHIB#G=te8mBWP+^PVQW; zek}Ayw!PQ)LeJt{e7ER-4>$D3^lRBwuRBmF#^L88UxkZMpL!lIrZ(n>H#@f<&ewjc zzy8Ygi3o%_xaNG@(vS?F1N^;gS5+f_lT(A4)rWn4fBEdiMg2R2T3G*k3w*)pfZu;p zk=JVVMTP#F2fm=WLq4@cK8W>d->zzGKt5`>&MCi#7It(}Z3L zP_YvFElvWjG5Y(ri+=+p?wVYGarbl*4XffYFDBB-$QKdjWN90}(QJ%z*1*RTeemuO zU>$J>8hJEw{`|X#2Y5+ki18ep-?OmukY5Jo$P@W_3HUa734S;8ZwksnA$VbKe?M`| z!~0hDt5Lr>B0(1Wb91D|HuYwaOK+%Gx5)q1%lj(P1u{nl+^# z^NUOvug!maCU)8-{1IecVw_P=A;NQQbJE^983wAC>?eLPUVo0zYqro=6qb$sZ8=&6 zx4X!Hd2M=}@Mi)1$Z-|m^MzoIyzQDteobzdaJv`aA5NPE>V>~?16hR)K2ughfA$>; zj9(*vQZ(p=5X4V>C~u!v&`W&pgF%{g4nB|%{g&59$m`}-7Iia8(v)A3>^ZC5LodSV zSAfbp0=^-{4s-}~Ba!86?IFb+`P28#3TLB75EbRYK_Idc^bT9jz1BzQFGpSJpHSHNfq}UgGn4`)Gr=@0@`-xxIDNJ1_VQR4J&IQH$)a#oJzguK+hvNxsfaH(EE`J8o;J3QN3`v3iKdA^)IQCR{u(;1`5moqeQygZyd!9&y+QJqq0n<_}&U|C8(g z@A!YO-~YY&gOHE|{WD)BLat%|Je$y$X+Q8)x$}168n-5g|M2-k?F;-E@B+&rq|sS{ z(8xn}0GC0zjl8J>`9X*gNy+7@f2hKp!8^TmZHIu?xnsX10>ADRnKGwjX+YOf57v9< zVEz4$U4VRA=xb&z2}APvlmhfewN>E+6Z3i>fiK#e$#y2qH!3+X;a$3H@qVwRZG!&a z_OJOW81nps>(L|iBT~?RcrIof6@HP{)As}Xt$mn4IbIf=rTAqiSfi}T$Q&xU3oycT zXg`a2P;1l&Tn%}(LNi#^|6}i6n%33TZtoA22IoVXAfPBn5EQW|iU&|plyLa;@3`Og ze^>3@e4d(}wQALxnILxv$#sn}e&qALtLCb$9=u*}(ElAw>nlXdCGt!C!{7P(mSR0e zHV$~Jm&%#8Z{*|oIr3u$`R9PQ+^w#ru~j}`iYOtzha2SEc57xcTj_N4-)iuV&kLA| zK`{`galJ==S9HtaA5q@mtsk+?Fwf5d{g34q`t{*?^M%91NWnik8Lj@fA%6xreH&)v z^-vAS7X-iV75#?JBOML%=HWbb6<~71UgFpvToU9!6UO{2*X-$w%2y?H{ao<52Vdz$p5F@|i ztBND?=SnLzBN1HN&B90i#PNW<+wZlvgvC*W{#_yH-}6^x2|)i@OqZ&-5*HNt7qcmc z{{QP+4*BJxPi+DD5&c&Vq2HQWme6MKH^&zl`odt1@ydhlkstd*rXuftSK@lNE7Rz8 zXQ(@zAn3Z~IYUn@cW8Ndlz=!8%5#~@j&a$^mXPibd;Wq3_Al>xeI=XS8TmJBDJyhw zwZiysbzrywj88@n{V?eW-s#DeC*x{JCPaVkDGMW3A5VJI zgEM>IZrGm(w|PXG3;NfmgRE5Ow?aQvsmq#I{0B_zNu(-yZ@_Pq|M(B;NxhadvWyG6 ze!-8Wx%@Cb*fOT{!J$>3r?qY#hRcO<6@e~f zzL$?=wyR?9#-jp#h480;ayL)@sfV#Z&FGALI(vCx#$3Yq%_*3f$7l~2M-BZMk%#$_ zB&sfQ%S@EXfKnK*_np3Yy-P&oKgOL!)*t9E`(<$qv}bNXe~CAnJp&v4=U&P1ev3)R zGwq@|+FeHe-)}=x4o>VGdByD9kj{aAQFZAiFfJkL@0tB#?3LHKLz-v3A_(&YPj)J& zRI6?{AI&zl!*A~^`VVOU|4gmfSMUqFO`t-&vLf>f{4?)ofb-R6&vCwzZr?P}5FoQcAd-->#|_tq)D`t`SIS-f0pdE<-#Nxh z$3w!K>SF|oq0pQ}2-yK|{IDzZv$f9PzcBXfZl5uKjy?M6pb@*Mha!f+IA`GJ{&W6P z?Xoir#0JQS`Dt@@voU`KmDoQhtbQX)>Ip41UaJm-El+`wkRK!u@0BT*1#f>nWD|D%Z?gn`1}1oJpcd3|M&d<*Ug{VV*{c9*1$k4o@>D<%0i9)ZmGHuV;onUWya0wo1`IUYkqkOY}YTJFrNKq)E?P259H4&`Njpr2O4@ z%uLG^`e&{pe|SM!zBwIc$ZsawuRuKA=T#T~0%?@7zjJQ8kU_q8gFXj54Vrj0_IG`c z{FZysBxFgI0KD_Hjr{h`RYCXs%=ibi3v{nGeJC{5EoR1ScMY_Lf%hASmd*NKu}#Ta zS`Y#?)&bW)St;Gho-62)EpU!=XCdou>tBqgX>|p^0&*5=0!qx#0^X*S&GU$CCGy1gKd>et z`{Tc<>j>YIOaHVz&>udN|5WDap$NFCK^!3I5IH~GJ zxWaCN{t0yBk~qeuK-~45$ufPQe^}`&n8Q`@=+>{;A=2V4lPR4&p?@A1DK!M_pYJmr z{V!Ae7;+{hv9Ok`o{jGn_$ogom{VihgGFM13Qub>NC>_II^XXJ{CshNdeE28a6V^O z=+FHe`{`#KBky^>1D~xwd^;LS$N&)~%fbHh1b=mYDmKW!o1-7>NXYvRWx?T1JV=4G zdZGuosj(lQ*1R`#H-vsYd85BA9_BOjS78YxHwX3B?Wcf7X}&?;c9aAluZ%ldD91ai zNu{BAse>GBNRTXKRBc`wdyaeP&t~A;Q*Dl5qH`9LR9`=!U$fWKou3ia5=gueY7%SE zuZcGR+Z63pBLB^O8SH9*;Xi0ZdZfUnLf7{+1n47o&_7^fy5=Ww7z(jiUR3h%23+4K z$m0|0c188-1xE0@h5muA+L6xUBm4)#AWX)pM*ikbcq;sI+dNv?b+Eq$ni%@I@cApG z`l8L~`sSk_&t|xO^fA!yx@AkA*B1+VtiSwD&(H_HgV`cb#2~f=YwZ;blF)A;BNZI| zwWP7&r!H~*x4P1)v-zOEd3suoa5B6vLWT8?A9nS%kfQi+e;pxDQZkB3Dcrl`j z@t7PF?L!c(5q|^y>372QMS|a7zPgTIF6z~gy9YPPEJ1#=3I1E46v6UH^AdOeJ3f(L z2>KcFsMqO_pN6NI8d`w*@*TT9etGsmtJ5>~e;4O>V^sB_2d*9H$KGcH`-chCT9fwH ziXbas#lQL<`qHqTqn@|6X8d)ol0b(q!6d(QeP`3yTdl4abwWPfD`D?rkNv|c!1&dC zOL=S7vV%}1eE%N;^u*n;2@U=7=U8}yTqmi#^cv(p+~zg?{j`kXd`+cz;$J(P?u>o?`8v$fQAe-3VJBd0?0LN(1Gda_GZH@HhWDK!&LA zx)rr9t-w0BK>p%X|L%0UR9gqvq5=kNh0%Kz&%(TJY{(%1k<0hNchkkt|-~0{n_# z=TIl;BZAM~oWI4ZJrQps#lo#LXZ-zz3`<>%s1J4*J7uERTCEX(W8|T9rq9gv5&Yf+ z5oH7dEgku*8unkaI}&f1DH&Fn8YmEf+I(q3vvUZ3N8Kh9xtZ6JJmbZV zpLQbpYd3cb{ifFh{ByM=L+R|zSNKNnIaBVEgZ|*`+|eivk;i*6p#M95H*ba_B`;Y zmL~kqNu1~K2a*}`jJF%?2W8J;a7fQlzv-{wnR+qy144;#m}F-^NdibSzpcdiWM<24 zEZ9rv3%R`Vz#F;TupUou7yn2+FARFWCB)gQ*y|w`+A-Z4#IaC9;vReFk9az zZn;a^j<=w+$cycu?_-NE)CLwin&oJKPoY4|DbOwaq;RiGd<}P@qj?nip29wkJiMN1 zF+F39k1rm0QO%|fCg&GLW-!AvtehD>yW86?GOY}L&{g?{f?aH(&$^%Y`wn!yAB>B? z&LyXIgZ}ZX41<=Qp-0rrJ+u!;`2b#7U~FN!e>!{#xgbgNeHNOeBAYF+>l@>qSlHYj9(ex4ZR&$$ zR+`V{9{!vj!&7#9g4cENs_AmZ>wViM=wt zsEASf2|c#E8v3l^S7~Jk{^GOgX3)E(d7;$2K>hZ6{^8%1UJ6OrJx1iuJPn2oI{34< z-C{=l{+#mD)q=lkvq65Ki%tYD@8?Y+pKs=g2-Xey#%d9_vSdOZ8a-bT{MjA!Eg<3{ z4>^PXdlN~Lqh<%_ON_O<;>i+zGC3Vlf75om7emI277T?Jm-IDXoUwn5@%*%fIX7p$ zLizfvE0aE3AiwUP^S7_^uhHAH%h7C*I>6o&<}a1w@B=dp_NxWEDwPhqNG?n=_LG5P zIynD4e_zx`H_%rsbuUmJy8WS-knh=R;T$HS4Vyo`=l05X`-haO;tKk@Z#~J!i zbq(^evLLcK-3;9S#lrpjGV(mF_$Bj|`@{S_QM`Zu_s{>o@&7%)|9$hvS{>2P`EF=1 zzWXvH2x-Fi>pJ|Pmp%X&I{x-8>%bqHsAm>O^O05`?oD%ZG8zN@3(>`~aSl$bF^n-k z5#{o}FtUt)?T{+ZY@IJQ{M}BYti={t9{s4sQ0r!m-=>+0~AV0>o zE9kv?1!2@ypHPujFZQ^Q9Q(EYGB=ZcyW(KXqklAtSuDg=C2)^2rxi63-`{3|@3)>q z$tIm|n;Ccw0lxo1i1qW3T_gHg<}pu!bajmHg!Kb?xYz@UtmITR~rKua5Zr2k5y%wa4@8=9w#GjSeRO1=V9bgCIA}hi*wsw5=xg2@3ST zcBMJOuFWa8_mW=ezbFfZwK47aI%BpW&vMj1zJL5*Q0B90M(^FcVfA5gN=dScKNR;U zp?|e(D#JCUg;)#Zs7-Z4&7FTXWjfZ zi7Sln^@Dws`KzrJ2jhQB^TviV0`m0rEQt-~!VjV?2>2;N`*O*0KXy@HUs)hr`q39k zl*m|TQO5zar22q=`p1jCMb}il+s?^Uf4Rzyssw+w){tL;{@{VRYkzH+=N9@ea>*9# z=T8TCkggT{=Nsq;$!~v8jnzXI{|-G8_#8Mv9;NORVMKlHJD{=9&#`v%ez^~+AHFo`$lyl@GQ&* z`@aLo9+!WI z!`$GaEG{ST!?wsr8aEwZZlA0$hb{O$Ve7#^HFOJOausMmCq=B7@KQ7U>-MXop~17t z$KMhB;AY5t;%*nWu0mC`6~qzz-G9zs<+&^1pKsT_u?8uPPl4uCCNmRVyGLr>mXzolZp82F%Ns;OGsxiS2 zyZhj8@34N|u(#^Q`~rU2BZ2;}bPreL%lW7FI|b8Ius#;fze=)C4XHfdjWh3MF00_z z(_y(oKI?hPJ4ODk1Cyy(fB!@?3~{AaJ8q8Y3dvUuXrYaMzIV8{?B&-V%EuhTQ$l zf`H0r2mJIa^0pgMSp?%(PdfB}T@k!uzetUvK11fgT5UN|2jmB*!rmoHyPI?0biuFt zyr)p8hMmIRak?+Y9}N1!;i!>E8x+Z@uR_wwc=>_%BC1zk&GRzY4a*-3;;j!uFLQ zzh=54|BihDtUBK(@XvPO2P;1K18TQWMpO0Ti5uU11%Ac>`A5I+PJw<5^&;w6v#an^ zq6K?>5a%@sV|o;(D2Ko3`@1{(I+H)<-2yKGpK9nw-_Pbs1a_-J z8xw?3F{wr9=k)Z9elj^?|E%u{k-g8*&jFQ;daB=~NA9op6!(`vKWDQ&#y2}T7#G!sqX^gh0}E7*;avmRvv_$oS%RBXTwKr zm7URFDLFYZ`vJ0s{JFpWi|Cw=Y`dD%$|y37=XP50ZBFNvVnXFa6dC!6s`o(uyYWZl z4}0j}Jv8JG(%%o`-Rres3DSiz{x3f*4-1T!7vQa7_q!$6J@VToFVF({{hUtJKqHtv z9yrGkD~{k-Ue*%Jj3-Tv>@UBr=$#sQEC{rg`orY}KCO-W$L`0FK;d(d_Z^^rF7A*Y zvt?`eeQ>VgbJT87h_BKAWF_+P!2kLh(!;*=#7-#skF#&ziV*U4G!UJUJt~6y!SrR$ zD1zz@_)A06QS}S*7k@w0pU&xw7Gic6EHb_ic>d+LFQk=?n4o!C^~m@8`~4dGMl{To zBhT)=nd<%*`o}{o%>TkY`$9xOI`(JZ2lDsw?^q~6vmmRu+E3VDe(?K)V+g;lMdr{C z80G{-E#{B#c`=@^=r&N+F6Pf^#QdS&9-Okx!q(}lGDP-`4a}c=06jy7)EXWR8_Yjt zvorraeo=)V%2 zAJvT@x00Xeow1(n(Z6-pxc|`q$*8QZ*4=X`Nva(b^sk*JIPnYBHvzPEI6q!4+L;kH zms@4gUGJv-yD04Af!`%mPvQF@8P6y~meB?J!BfB8e&RtrSuzn%`sdU;a zKj$0Ekkh3V3%;m<-&zG~3+62Nli;V#*)VnBKkC;K`WeH;`nyqQ4&B|n9QpHqd8}*n zdzJES!Pm7}BR}s)bVKSrq<_d%4cVBbTfG11djebjSHC>i&O}XP=L)w9eD`$u*FV@u zH|U80Nrxf{`B3t?{FY{N#CJ5ub2=b-zy_0!Z-8|GYUdUgOzk9-Wk??MN`(RYM;3RiqO51Ow%&WisR92$w(M1MdC56!Tvj+YD;b_B;yV!O{FJ3RY86N#5 z@@THO-vp6?W?@zoGN%gq(|O~agp2plxdXbH(MxZPk2DJY;r#UqM&R~J^Bv|(Hj)za zZ`SRP=CXROFhyBwt_;p|<=z7^e&v=WFpLTq+ zTAQ8F>$C?K^Edh-e-85-MCy8bOhI(9PSBqYIa4TLeLu7H8`C)S3nrT2&zgsg*%+6C zJei{f&@aR>@{`>7wTft8zNazEpkIJ1>A@kiXFD>de9d$2jjgA{M+F=1J^iyu1{wARguTuIaw;4pU9_4OZboG#>xgy z_V$m<= zS`Yom6Z~4*JhZmIUuH+@^U#Msx`lq`eZhQ!1YyaOLC5@Ae0RX4{cHaA%S{iYE*FmG zaBM>CXWzS2c--2=%+39Y2Z#%B%x&*&1wHQ)9&BFkVpJ^XPma|tLos~6Z-?@b%}n

B^}rW?KC9^LhU?ca8gkDF6P&*mSUwKQRcSux|G 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 #include #include +#include #include #include #include +#include namespace irr { @@ -51,6 +53,28 @@ core::vector3df convertHandedness(const core::vector3df &p) return core::vector3df(p.X, p.Y, -p.Z); } +template <> +core::quaternion convertHandedness(const core::quaternion &q) +{ + return core::quaternion(q.X, q.Y, -q.Z, q.W); +} + +template <> +core::matrix4 convertHandedness(const core::matrix4 &mat) +{ + // Base transformation between left & right handed coordinate systems. + static const core::matrix4 invertZ = core::matrix4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1); + // Convert from left-handed to right-handed, + // then apply mat, + // then convert from right-handed to left-handed. + // Both conversions just invert Z. + return invertZ * mat * invertZ; +} + namespace scene { using SelfType = CGLTFMeshFileLoader; @@ -196,6 +220,8 @@ ACCESSOR_PRIMITIVE(u16, UNSIGNED_SHORT) ACCESSOR_PRIMITIVE(u32, UNSIGNED_INT) ACCESSOR_TYPES(core::vector3df, VEC3, FLOAT) +ACCESSOR_TYPES(core::quaternion, VEC4, FLOAT) +ACCESSOR_TYPES(core::matrix4, MAT4, FLOAT) template T SelfType::Accessor::get(std::size_t i) const @@ -340,7 +366,7 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file) auto *mesh = new CSkinnedMesh(); MeshExtractor parser(std::move(model.value()), mesh); try { - parser.loadNodes(); + parser.load(); } catch (std::runtime_error &e) { os::Printer::log("glTF loader", e.what(), ELL_ERROR); mesh->drop(); @@ -397,61 +423,134 @@ static video::E_TEXTURE_CLAMP convertTextureWrap(const Wrap wrap) { } } -/** - * Load up the rawest form of the model. The vertex positions and indices. - * Documentation: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - * If material is undefined, then a default material MUST be used. -*/ -void SelfType::MeshExtractor::loadMesh( - const std::size_t meshIdx, - ISkinnedMesh::SJoint *parent) const +void SelfType::MeshExtractor::addPrimitive( + const tiniergltf::MeshPrimitive &primitive, + const std::optional skinIdx, + CSkinnedMesh::SJoint *parent) { - for (std::size_t pi = 0; pi < getPrimitiveCount(meshIdx); ++pi) { - const auto &primitive = m_gltf_model.meshes->at(meshIdx).primitives.at(pi); - auto vertices = getVertices(primitive); - if (!vertices.has_value()) - continue; // "When positions are not specified, client implementations SHOULD skip primitive’s rendering" + auto vertices = getVertices(primitive); + if (!vertices.has_value()) + return; // "When positions are not specified, client implementations SHOULD skip primitive’s rendering" - // Excludes the max value for consistency. - if (vertices->size() >= std::numeric_limits::max()) - throw std::runtime_error("too many vertices"); + const auto n_vertices = vertices->size(); - // Apply the global transform along the parent chain. - transformVertices(*vertices, parent->GlobalMatrix); + // Excludes the max value for consistency. + if (n_vertices >= std::numeric_limits::max()) + throw std::runtime_error("too many vertices"); - auto maybeIndices = getIndices(primitive); - std::vector indices; - if (maybeIndices.has_value()) { - indices = std::move(*maybeIndices); - checkIndices(indices, vertices->size()); - } else { - // Non-indexed geometry - indices = generateIndices(vertices->size()); - } + // Apply the global transform along the parent chain. + transformVertices(*vertices, parent->GlobalMatrix); - m_irr_model->addMeshBuffer( - new SSkinMeshBuffer(std::move(*vertices), std::move(indices))); - auto *meshbuf = m_irr_model->getMeshBuffer(m_irr_model->getMeshBufferCount() - 1); - auto &irr_mat = meshbuf->getMaterial(); + auto maybeIndices = getIndices(primitive); + std::vector indices; + if (maybeIndices.has_value()) { + indices = std::move(*maybeIndices); + checkIndices(indices, vertices->size()); + } else { + // Non-indexed geometry + indices = generateIndices(vertices->size()); + } - if (primitive.material.has_value()) { - const auto &material = m_gltf_model.materials->at(*primitive.material); - if (material.pbrMetallicRoughness.has_value()) { - const auto &texture = material.pbrMetallicRoughness->baseColorTexture; - if (texture.has_value()) { - const auto meshbufNr = m_irr_model->getMeshBufferCount() - 1; - m_irr_model->setTextureSlot(meshbufNr, static_cast(texture->index)); - const auto samplerIdx = m_gltf_model.textures->at(texture->index).sampler; - if (samplerIdx.has_value()) { - auto &sampler = m_gltf_model.samplers->at(*samplerIdx); - auto &layer = irr_mat.TextureLayers[0]; - layer.TextureWrapU = convertTextureWrap(sampler.wrapS); - layer.TextureWrapV = convertTextureWrap(sampler.wrapT); - } + m_irr_model->addMeshBuffer( + new SSkinMeshBuffer(std::move(*vertices), std::move(indices))); + const auto meshbufNr = m_irr_model->getMeshBufferCount() - 1; + auto *meshbuf = m_irr_model->getMeshBuffer(meshbufNr); + + if (primitive.material.has_value()) { + const auto &material = m_gltf_model.materials->at(*primitive.material); + if (material.pbrMetallicRoughness.has_value()) { + const auto &texture = material.pbrMetallicRoughness->baseColorTexture; + if (texture.has_value()) { + m_irr_model->setTextureSlot(meshbufNr, static_cast(texture->index)); + const auto samplerIdx = m_gltf_model.textures->at(texture->index).sampler; + if (samplerIdx.has_value()) { + auto &sampler = m_gltf_model.samplers->at(*samplerIdx); + auto &layer = meshbuf->getMaterial().TextureLayers[0]; + layer.TextureWrapU = convertTextureWrap(sampler.wrapS); + layer.TextureWrapV = convertTextureWrap(sampler.wrapT); } } } } + + if (!skinIdx.has_value()) { + // No skin => all vertices belong entirely to their parent + for (std::size_t v = 0; v < n_vertices; ++v) { + auto *weight = m_irr_model->addWeight(parent); + weight->buffer_id = meshbufNr; + weight->vertex_id = v; + weight->strength = 1.0f; + } + return; + } + + const auto &skin = m_gltf_model.skins->at(*skinIdx); + + const auto &attrs = primitive.attributes; + const auto &joints = attrs.joints; + if (!joints.has_value()) + return; + + const auto &weights = attrs.weights; + for (std::size_t set = 0; set < joints->size(); ++set) { + const auto jointAccessor = ([&]() -> ArrayAccessorVariant<4, u8, u16> { + const auto idx = joints->at(set); + const auto &acc = m_gltf_model.accessors->at(idx); + + switch (acc.componentType) { + case tiniergltf::Accessor::ComponentType::UNSIGNED_BYTE: + return Accessor>::make(m_gltf_model, idx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_SHORT: + return Accessor>::make(m_gltf_model, idx); + default: + throw std::runtime_error("invalid component type"); + } + })(); + + const auto weightAccessor = createNormalizedValuesAccessor<4>(m_gltf_model, weights->at(set)); + + for (std::size_t v = 0; v < n_vertices; ++v) { + std::array jointIdxs; + if (std::holds_alternative>>(jointAccessor)) { + const auto jointIdxsU8 = std::get>>(jointAccessor).get(v); + jointIdxs = {jointIdxsU8[0], jointIdxsU8[1], jointIdxsU8[2], jointIdxsU8[3]}; + } else if (std::holds_alternative>>(jointAccessor)) { + jointIdxs = std::get>>(jointAccessor).get(v); + } + std::array strengths = getNormalizedValues(weightAccessor, v); + + // 4 joints per set + for (std::size_t in_set = 0; in_set < 4; ++in_set) { + u16 jointIdx = jointIdxs[in_set]; + f32 strength = strengths[in_set]; + if (strength == 0) + continue; + + CSkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx))); + weight->buffer_id = meshbufNr; + weight->vertex_id = v; + weight->strength = strength; + } + } + } +} + +/** + * Load up the rawest form of the model. The vertex positions and indices. + * Documentation: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes + * If material is undefined, then a default material MUST be used. + */ +void SelfType::MeshExtractor::deferAddMesh( + const std::size_t meshIdx, + const std::optional skinIdx, + CSkinnedMesh::SJoint *parent) +{ + m_mesh_loaders.emplace_back([=] { + for (std::size_t pi = 0; pi < getPrimitiveCount(meshIdx); ++pi) { + const auto &primitive = m_gltf_model.meshes->at(meshIdx).primitives.at(pi); + addPrimitive(primitive, skinIdx, parent); + } + }); } // Base transformation between left & right handed coordinate systems. @@ -464,51 +563,75 @@ static const core::matrix4 leftToRight = core::matrix4( ); static const core::matrix4 rightToLeft = leftToRight; -static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m) +static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, CSkinnedMesh::SJoint *joint) { // Note: Under the hood, this casts these doubles to floats. - return core::matrix4( + core::matrix4 mat = convertHandedness(core::matrix4( m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], - m[12], m[13], m[14], m[15]); + m[12], m[13], m[14], m[15])); + + // Decompose the matrix into translation, scale, and rotation. + joint->Animatedposition = mat.getTranslation(); + + auto scale = mat.getScale(); + joint->Animatedscale = scale; + core::matrix4 inverseScale; + inverseScale.setScale(core::vector3df( + scale.X == 0 ? 0 : 1 / scale.X, + scale.Y == 0 ? 0 : 1 / scale.Y, + scale.Z == 0 ? 0 : 1 / scale.Z)); + + core::matrix4 axisNormalizedMat = inverseScale * mat; + joint->Animatedrotation = axisNormalizedMat.getRotationDegrees(); + // Invert the rotation because it is applied using `getMatrix_transposed`, + // which again inverts. + joint->Animatedrotation.makeInverse(); + + return mat; } -static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs) +static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, CSkinnedMesh::SJoint *joint) { const auto &trans = trs.translation; const auto &rot = trs.rotation; const auto &scale = trs.scale; core::matrix4 transMat; - transMat.setTranslation(core::vector3df(trans[0], trans[1], trans[2])); - core::matrix4 rotMat = core::quaternion(rot[0], rot[1], rot[2], rot[3]).getMatrix(); + joint->Animatedposition = convertHandedness(core::vector3df(trans[0], trans[1], trans[2])); + transMat.setTranslation(joint->Animatedposition); + core::matrix4 rotMat; + joint->Animatedrotation = convertHandedness(core::quaternion(rot[0], rot[1], rot[2], rot[3])); + core::quaternion(joint->Animatedrotation).getMatrix_transposed(rotMat); + joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]); core::matrix4 scaleMat; - scaleMat.setScale(core::vector3df(scale[0], scale[1], scale[2])); + scaleMat.setScale(joint->Animatedscale); return transMat * rotMat * scaleMat; } -static core::matrix4 loadTransform(std::optional> transform) { +static core::matrix4 loadTransform(std::optional> transform, + CSkinnedMesh::SJoint *joint) { if (!transform.has_value()) { return core::matrix4(); } - core::matrix4 mat = std::visit([](const auto &t) { return loadTransform(t); }, *transform); - return rightToLeft * mat * leftToRight; + return std::visit([joint](const auto &t) { return loadTransform(t, joint); }, *transform); } void SelfType::MeshExtractor::loadNode( const std::size_t nodeIdx, - ISkinnedMesh::SJoint *parent) const + CSkinnedMesh::SJoint *parent) { const auto &node = m_gltf_model.nodes->at(nodeIdx); auto *joint = m_irr_model->addJoint(parent); - const core::matrix4 transform = loadTransform(node.transform); + const core::matrix4 transform = loadTransform(node.transform, joint); joint->LocalMatrix = transform; joint->GlobalMatrix = parent ? parent->GlobalMatrix * joint->LocalMatrix : joint->LocalMatrix; if (node.name.has_value()) { joint->Name = node.name->c_str(); } + m_loaded_nodes[nodeIdx] = joint; if (node.mesh.has_value()) { - loadMesh(*node.mesh, joint); + deferAddMesh(*node.mesh, node.skin, joint); } if (node.children.has_value()) { for (const auto &child : *node.children) { @@ -517,8 +640,10 @@ void SelfType::MeshExtractor::loadNode( } } -void SelfType::MeshExtractor::loadNodes() const +void SelfType::MeshExtractor::loadNodes() { + m_loaded_nodes = std::vector(m_gltf_model.nodes->size()); + std::vector isChild(m_gltf_model.nodes->size()); for (const auto &node : *m_gltf_model.nodes) { if (!node.children.has_value()) @@ -536,6 +661,92 @@ void SelfType::MeshExtractor::loadNodes() const } } +void SelfType::MeshExtractor::loadSkins() +{ + if (!m_gltf_model.skins.has_value()) + return; + + for (const auto &skin : *m_gltf_model.skins) { + if (!skin.inverseBindMatrices.has_value()) + continue; + const auto accessor = Accessor::make(m_gltf_model, *skin.inverseBindMatrices); + if (accessor.getCount() < skin.joints.size()) + throw std::runtime_error("accessor contains too few matrices"); + for (std::size_t i = 0; i < skin.joints.size(); ++i) { + m_loaded_nodes.at(skin.joints[i])->GlobalInversedMatrix = convertHandedness(accessor.get(i)); + } + } +} + +void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx) +{ + const auto &anim = m_gltf_model.animations->at(animIdx); + for (const auto &channel : anim.channels) { + + const auto &sampler = anim.samplers.at(channel.sampler); + if (sampler.interpolation != tiniergltf::AnimationSampler::Interpolation::LINEAR) + throw std::runtime_error("unsupported interpolation"); + + const auto inputAccessor = Accessor::make(m_gltf_model, sampler.input); + const auto n_frames = inputAccessor.getCount(); + + if (!channel.target.node.has_value()) + throw std::runtime_error("no animated node"); + + const auto &joint = m_loaded_nodes.at(*channel.target.node); + switch (channel.target.path) { + case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: { + const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); + for (std::size_t i = 0; i < n_frames; ++i) { + auto *key = m_irr_model->addPositionKey(joint); + key->frame = inputAccessor.get(i); + key->position = convertHandedness(outputAccessor.get(i)); + } + break; + } + case tiniergltf::AnimationChannelTarget::Path::ROTATION: { + const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); + for (std::size_t i = 0; i < n_frames; ++i) { + auto *key = m_irr_model->addRotationKey(joint); + key->frame = inputAccessor.get(i); + key->rotation = convertHandedness(outputAccessor.get(i)); + } + break; + } + case tiniergltf::AnimationChannelTarget::Path::SCALE: { + const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); + for (std::size_t i = 0; i < n_frames; ++i) { + auto *key = m_irr_model->addScaleKey(joint); + key->frame = inputAccessor.get(i); + key->scale = outputAccessor.get(i); + } + break; + } + case tiniergltf::AnimationChannelTarget::Path::WEIGHTS: + throw std::runtime_error("no support for morph animations"); + } + } +} + +void SelfType::MeshExtractor::load() +{ + loadNodes(); + for (const auto &load_mesh : m_mesh_loaders) { + load_mesh(); + } + loadSkins(); + // Load the first animation, if there is one. + if (m_gltf_model.animations.has_value()) { + if (m_gltf_model.animations->size() > 1) { + os::Printer::log("glTF loader", + "multiple animations are not supported", ELL_WARNING); + } + loadAnimation(0); + m_irr_model->setAnimationSpeed(1); + } + m_irr_model->finalize(); +} + /** * Extracts GLTF mesh indices. */ @@ -722,4 +933,3 @@ std::optional SelfType::tryParseGLTF(io::IReadFile* file) } // namespace scene } // namespace irr - diff --git a/irr/src/CGLTFMeshFileLoader.h b/irr/src/CGLTFMeshFileLoader.h index da306769e..7674fd46a 100644 --- a/irr/src/CGLTFMeshFileLoader.h +++ b/irr/src/CGLTFMeshFileLoader.h @@ -10,9 +10,11 @@ #include "path.h" #include "S3DVertex.h" -#include +#include "tiniergltf.hpp" +#include #include +#include #include namespace irr @@ -26,9 +28,9 @@ class CGLTFMeshFileLoader : public IMeshLoader public: CGLTFMeshFileLoader() noexcept {}; - bool isALoadableFileExtension(const io::path& filename) const override; + bool isALoadableFileExtension(const io::path &filename) const override; - IAnimatedMesh* createMesh(io::IReadFile* file) override; + IAnimatedMesh *createMesh(io::IReadFile *file) override; private: template @@ -94,7 +96,8 @@ class CGLTFMeshFileLoader : public IMeshLoader const NormalizedValuesAccessor &accessor, const std::size_t i); - class MeshExtractor { + class MeshExtractor + { public: MeshExtractor(tiniergltf::GlTF &&model, CSkinnedMesh *mesh) noexcept @@ -114,12 +117,15 @@ class CGLTFMeshFileLoader : public IMeshLoader std::size_t getPrimitiveCount(const std::size_t meshIdx) const; - void loadNodes() const; + void load(); private: const tiniergltf::GlTF m_gltf_model; CSkinnedMesh *m_irr_model; + std::vector> m_mesh_loaders; + std::vector m_loaded_nodes; + void copyPositions(const std::size_t accessorIdx, std::vector& vertices) const; @@ -129,16 +135,24 @@ class CGLTFMeshFileLoader : public IMeshLoader void copyTCoords(const std::size_t accessorIdx, std::vector& vertices) const; - void loadMesh( - std::size_t meshIdx, - ISkinnedMesh::SJoint *parentJoint) const; + void addPrimitive(const tiniergltf::MeshPrimitive &primitive, + const std::optional skinIdx, + CSkinnedMesh::SJoint *parent); - void loadNode( - const std::size_t nodeIdx, - ISkinnedMesh::SJoint *parentJoint) const; + void deferAddMesh(const std::size_t meshIdx, + const std::optional skinIdx, + CSkinnedMesh::SJoint *parentJoint); + + void loadNode(const std::size_t nodeIdx, CSkinnedMesh::SJoint *parentJoint); + + void loadNodes(); + + void loadSkins(); + + void loadAnimation(const std::size_t animIdx); }; - std::optional tryParseGLTF(io::IReadFile* file); + std::optional tryParseGLTF(io::IReadFile *file); }; } // namespace scene diff --git a/src/unittest/test_irr_gltf_mesh_loader.cpp b/src/unittest/test_irr_gltf_mesh_loader.cpp index 99cb4b1b5..674f3c0dd 100644 --- a/src/unittest/test_irr_gltf_mesh_loader.cpp +++ b/src/unittest/test_irr_gltf_mesh_loader.cpp @@ -8,6 +8,7 @@ #include "irr_v2d.h" #include "irr_ptr.h" +#include "ISkinnedMesh.h" #include #include "catch.h" @@ -371,7 +372,91 @@ SECTION("simple sparse accessor") CHECK(vertices[i].Pos == expectedPositions[i]); } +// https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/SimpleSkin +SECTION("simple skin") +{ + using ISkinnedMesh = irr::scene::ISkinnedMesh; + const auto mesh = loadMesh(model_stem + "simple_skin.gltf"); + REQUIRE(mesh != nullptr); + auto csm = dynamic_cast(mesh); + const auto joints = csm->getAllJoints(); + REQUIRE(joints.size() == 3); + + const auto findJoint = [&](const std::function &predicate) { + for (std::size_t i = 0; i < joints.size(); ++i) { + if (predicate(joints[i])) { + return joints[i]; + } + } + throw std::runtime_error("joint not found"); + }; + + // Check the node hierarchy + const auto parent = findJoint([](auto joint) { + return !joint->Children.empty(); + }); + REQUIRE(parent->Children.size() == 1); + const auto child = parent->Children[0]; + REQUIRE(child != parent); + + SECTION("transformations are correct") + { + CHECK(parent->Animatedposition == v3f(0, 0, 0)); + CHECK(parent->Animatedrotation == irr::core::quaternion()); + CHECK(parent->Animatedscale == v3f(1, 1, 1)); + CHECK(parent->GlobalInversedMatrix == irr::core::matrix4()); + const v3f childTranslation(0, 1, 0); + CHECK(child->Animatedposition == childTranslation); + CHECK(child->Animatedrotation == irr::core::quaternion()); + CHECK(child->Animatedscale == v3f(1, 1, 1)); + irr::core::matrix4 inverseBindMatrix; + inverseBindMatrix.setInverseTranslation(childTranslation); + CHECK(child->GlobalInversedMatrix == inverseBindMatrix); + } + + SECTION("weights are correct") + { + const auto weights = [&](const ISkinnedMesh::SJoint *joint) { + std::unordered_map weights; + for (std::size_t i = 0; i < joint->Weights.size(); ++i) { + const auto weight = joint->Weights[i]; + REQUIRE(weight.buffer_id == 0); + weights[weight.vertex_id] = weight.strength; + } + return weights; + }; + const auto parentWeights = weights(parent); + const auto childWeights = weights(child); + + const auto checkWeights = [&](irr::u32 index, irr::f32 parentWeight, irr::f32 childWeight) { + const auto getWeight = [](auto weights, auto index) { + const auto it = weights.find(index); + return it == weights.end() ? 0.0f : it->second; + }; + CHECK(getWeight(parentWeights, index) == parentWeight); + CHECK(getWeight(childWeights, index) == childWeight); + }; + checkWeights(0, 1.00, 0.00); + checkWeights(1, 1.00, 0.00); + checkWeights(2, 0.75, 0.25); + checkWeights(3, 0.75, 0.25); + checkWeights(4, 0.50, 0.50); + checkWeights(5, 0.50, 0.50); + checkWeights(6, 0.25, 0.75); + checkWeights(7, 0.25, 0.75); + checkWeights(8, 0.00, 1.00); + checkWeights(9, 0.00, 1.00); + } + + SECTION("there should be a third node not involved in skinning") + { + const auto other = findJoint([&](auto joint) { + return joint != child && joint != parent; + }); + CHECK(other->Weights.empty()); + } +} + driver->closeDevice(); driver->drop(); - }