From b03b3b9c03954aee8d860bf4f70841b15505dae9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 24 May 2019 17:22:54 +0200 Subject: [PATCH 01/13] Initial parsing for header and more. --- code/CMakeLists.txt | 5 +++++ code/ImporterRegistry.cpp | 9 ++++++++- test/CMakeLists.txt | 1 + test/models/JT/conrod.jt | Bin 0 -> 61578 bytes test/models/PLY/cube_test.ply | 2 +- 5 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/models/JT/conrod.jt diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index df12695d2..53da5019e 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -292,6 +292,11 @@ ADD_ASSIMP_IMPORTER( AMF AMFImporter_Postprocess.cpp ) +ADD_ASSIMP_IMPORTER( JT + JTImporter.h + JTImporter.cpp +) + ADD_ASSIMP_IMPORTER( 3DS 3DSConverter.cpp 3DSHelper.h diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index 747815fa6..69a50bf27 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -200,6 +200,9 @@ corresponding preprocessor flag to selectively disable formats. #ifndef ASSIMP_BUILD_NO_STEP_IMPORTER # include "Importer/StepFile/StepFileImporter.h" #endif +#ifndef ASSIMP_BUILD_NO_JT_IMPORTER +# include "JTImporter.h" +#endif namespace Assimp { @@ -358,13 +361,17 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) #ifndef ASSIMP_BUILD_NO_STEP_IMPORTER out.push_back(new StepFile::StepFileImporter()); #endif +#ifndef ASSIMP_BUILD_NO_JT_IMPORTER + out.push_back(new JTImporter()); +#endif + } /** will delete all registered importers. */ void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){ for(size_t i= 0; i^1y6e#0NC?sm0@5Yj-6@BZ?vxPe?h>S1LO?pC1SJ(z6hZkm z|Np(?-s^jBeBT(~n`5ti=A6H|*PeT?xni%ij-HLDmxG%tod`EC-Tw^&?#Ka5v-~7a zwzih>kwE$Y-{IIY!T&~Qg8~4-RCiYZ5R5L;|6#vF0{|g7|6mw6e_>`E03a;(FNXaW zmSF<`&rtux@czOmLB}R3|HXj6u!BedFeUvLyZ09+n-2i8*#BaXzp&^E0Fe9-4E7f` z+zbE*|AC?Zg;n5pjF@5s6YE|UYL*gK z$HXXT!Qo1eSlM`9%B!Rx4B&8rgh(WGK*JuvT5Mn-g@#`Wt3;YQkZceWd1rDJulV@* z&H3D3PTyXi-GDBS3mN-Ci8=4UI~0P(-TWdU;UtzzAR4d*yWk_ zxczgKYSu{6iT`fwvvLud&uShj`3^txw-Y>UMGk**3frA)=znyy>-s7;I@*&YVE6fl zxE|t3%3g*FCcT-)X~ znj+tyKa1$nxJAsG*xOfZQVH*HUyyJp=du>#^?&V>lAuGk^2mQhpRSc|&FIlyPov{* zy0r4q?hMKwu))RuHZ^@ukT^#Qf~c@k(uSGX?3Ix0ZgK7 zhlvug%5G52qlHi3j_?Y8gQD+h^hZ&1)uej%WAfenjLpRY2Py7ARo zA`BTOC>cKA8-4~~W(+&r+C3BT#5_Do7e5UZsMek&A zhx>!!=2L@#bDJm*H_@`k+qRVq(&TcINwHtYcA#Fv{5*>_0V-TRGpn6b0l$Y%`IE|) zCSQBcxK5?y?oCa96|)QJKFE26{zp_FV`PrMX@>+5zdg8DGgZ|4G(dOh5r;lCZH8X& z@pCtnfC^DOY4YLvEbb)9VbMfSP3PAXai#U#+(yj4=ph~&Xn~NWBg--e@8h+L3qwPL zuJzd+Q-jgisfSP9mfu-n_u-h=?N`^th4&1P8bK`dh--3`qF?a`IDI%GW)4#uE$1!~ z5cUyG<2#;z%NyBTE8jz6!+oUt&}pL5;25i>CN9xGHt_|XoQW>u7XtD%e-@RQ!w#I7 z7hxzffX==Z8e#0(^AhGDwF6!^0#?q~?9W7tvfQp*AHSG3r?Z4orVzgM%#^DM2EG1T z7f4WRo4)c=08$^;f)t?}6{UTVMUgfoxV6Hzp|`Rsy7th|e60vsy-qYDcW#F&c(RIQ z%E(l($E+B8SplHD=ljajt@hN}diSy*eOqW@bXO^AcytC*`S~2VNXDUVWxC@uX)b}I zXUY_>Sil-+!;IyI;a)t0wvx_ED9@i;S!Wnka4+=2VP@l`!-rH=OO0rY1}3gmTWeh` zIVNWs7F!&o%Mur*=A4j)IFW0bFRW}qXy~|n;nn(i1+c`O)e^#)h3Ru0Mu(XN?Q;_z znQ!|hy8W;!GB+v6_wsER*Bz6YQ**8t=#QmDF(56ti{*<{C$m+)z=@?&BKwuc_ujWM zk*|@g^E(CAX*4kVxqMk7$@mOuOK)loxiC`u(C2DYc7KMCujw2c&?sFK;ZWb(-+JfW z_h+!l|GAqWI>)EyU%H+=xnxY9d&$kaUzPviHgoJl+Eap+I=a|%dP2$yuClSuSehYK zmzV%mL^_lal}(Dh-&~N|9JYY^33GOf*FG#Y`hk&{)1Q?{@^JCDdVVW{gx#_%QxDcZ z5V^)O#NzAQlRQvJBB+;T=1C5n@^Ce1K$l&Oo^@eHS_x;Pe#5BkOoo;>jclsF?g)K| z5&vN{P$NJT!*%92U(C_VyBcNB4WE-u1#8l$(*jme-Hi*)r|4LpP^U0zbCQYG7>}Hi zo^I}m^qo!ovVCv#0Yd7FJtXmK zQ0cE>r=&b5prnP#=?D|}$Kpi1CS>~PZT#lIt_V5MAKb4ktjC+mx6p?hL@W;W#swDKT6 zvwSYicuwoB^IRk(sl_rtjCm(XYAi%ixbltSfEho-m@?TuCA&8xrR_MCoyf%>WLYp{ z%7lo8>X^do;Za2ZKQkF>h8P-oADdS5<|F7@DzZAb)*La%>gDa8ya(_0;KD;Uc|C5z z^+?%up+laFl@`w6`LzB}WY}d{(>&wN*k}#kR`tc4g+P;RHt@RNdm*dLs?6b=R*nxd zdl5$AqwzyQq0o{w7y}BkuqT^4dH<(>n_5duK{Zr`Zfd)eQR2yXPo3#2pI3=0x8QvDDy@15ne>I$-%3ID0NobD0l8w1>D1$z z^tARpM7QKk|DPYIh4=1p(f)Sr>zyuIgxbI#%GAVa>CQO|nhP82U_7g&s{Nq z^cU95VStq$_hW!lsuTPNnwNOVn>b0bSOG2fmmdh#>wi(Ek)4m^c|32`~XXjn$1{#F}a9X<6{h{^<;C zRQ$n(O>8d*^u?+)mu3fhF>4P-N7-6ITo==L21XvLbM6NUk1x%+T{0Z;Y;IjBcIS7- zZF3$_zF4whi52I5OJyX6Ua>74!0TkCm}2Z3zMH$Upf=GUJHuzhoC+-;*cxDL#^-4| zKMsolWiYgH!O;H7ZzTI&K(jv)tatpecj2vmz>0Uk>)H^6L666rI&NE^#WJ(C_~U{Z z_6vAJ7dX;YdCmT|STOdk??|-u5U8*$mgmF_c%XM}ON6kwKR6cUza~zF-3D8)Ib?o; zRq_~F;#%2e;6~42aJ<6+P>RpJB9XME8V}lb15~)O46OlGj^Ko`@7xN*xiFlenE*Q1 z2j51u^jnqb-+9P#Lk@~raLn#`eOw^WSFnJO^>5tLb>Exe+FfNt^S};f%;RR3@MBWK!u&+O9M5ErH=eamQV^v~kCYS>d#n)F_6ZAmyIagY@eaDh9sWrEtlA#4 zd@5+G7%IT`?mpqdV4=dj$AT7qF^$m*Io-hobTfUy7eb>WgDVsPpUTQ8(7;guw+C&B zPDJ8bA~J1%$3CYw<1^N0XG}%3ETOD&#Bol(WImx}QI{Sv(;v-XsiC`AGYdehbBXBk`P%l=gJ-(*!!x-%T+sqqmMJo4|2m6e*{JHHsk8@O!W*J^`KZ%_q@I2p|4SK+y~#bAEkURa{NY@-pYwdfqKccMl?gw}cQ{WFx zo}m*6u74wOT}i=0J>&Tij=5O+5HV;!?=|P>X3w_#scm-q+@Q=_PnK=fEJu_5lMaVQ z;ntrn4dL3~rYygU7Fqj^cGi(5Wzw_-tOv%X_mi>2m(^)j#)eKw%&)3O1AE|)7hSCC z6yzx*nYt>K+U`-x%e7ug7kpPo41fNfCi9>%bcwJCyMB2{Z^P_fa%|_?ctlJ6bA#0_ zn;>RknlPv4IkD%DC^qG8bU87#QxtIP+m-G)Mnd(4wCeetiKCxm$0b*KXs462J|t1l%v z!X4SxYPtqLM()T3*B%WOk`C;ID zN3q?7uKVf<{sp1d*;l%uHL)J{@Cf6I(O;uGLi9L+ZA9K;6E^{7iCF@~p$@a^c+5(v zkpw>G*TJaRi_h$d)f2tUNCy(UZo~<`JKku;oYuUIt_UT#I2e}|uX|Qez0^Z*S?Tj$ zLO7H$8;SWrBAX_rhlqHM=VUHg`6%!KolLcz^MXYW8-n3pe8(4^%MPN;Qmj97*b7}@ z^F{_3Q3b9uX7P^k&w{xW-ao5Z-f^)=9_1apYUS7o8W=AeGThV0HED`Fnd zD}6b|1%?VUYW|Y*jp6VG_gD7~((UPWNX&IW2R|uC+7HaH({-gUe=Yj!L*;sNHfk$- zze-p%6n}oe>6{u)^{iH25h=YIPK~1ymrJGEk*ag)TLythj z9?H})(JK!zyuxnav(S7H2E&*%^Cx3GN!3qV@}DN~p6HqeJ`p2L0?XngXT$$=O$WOenq*stv{V`0h*Zcs&+;dR-Ug z__2?8H{Io}B!A*a1n!=vF)9%%m0>d!8V)Wf0jA7Y(#bxW>7 z5hC`2d50V~81xB5b&SYS?IGGIz70aE#lo8pPF&t!cO;zkPAA?DlC2ibCRK@D$2Y!k zF3{EAYM6gDyV#&H?A99_LuJGmHs9rVOL+N(&&4jW&Sd38qWnl>I-XUl&$K8IxaxiC z+e#y)+d;uszO|}zww?Gn?o8D*TPW&WZfPHIK{&;3>){7}_@VH6{Vb4Sk#Uj4Ls0qU zan*&mb~X6}c{y&?)0EtoxG$67ycw5$c!b_(8y{a4==n6(!(a^&)ea3T&Z+9%)fnHS zgwj$Uf9we3)pL8gICsC+``hnULpMfeF0I4R23Bh31`L(7bNAU0C|T%h$+=+Dr7o8x z139!1S+9^M=DiWoHXBqf(T>?qYm{*9^OS;!2or`>Ja~QvdM`EzR}~9a4>_<~4vJFg zIy}cD%(R7F)(5kNJ<{1w{5~{?+8bDY6yRl%XggWyI&K_;T(e(aPht6V-@+KnLBb~v zg|&_Is%ns+E==cpcP8xRQ+B!hfij)DM)MyZqYilrPqjgu!I_eU`HvUGVNxhGj*LN??5&9D!uT43| zO_SN}Dr|Z)rDJw%X`v0wK`A?e3~C(mjpfAoqRna+g@bamhMUWk*-kx&OyQ5(JG2=}a1Kp;7#M^kXNX=VBoBr1 zF%+q1-Ov~=h)Fi&yww3F_DT!R@ z#c^~Fh)oe_|HioP#(UoLs=cVuvOzWK_xiAcq2rf;TsZH~R_cVV3~o{qZl>EMqtjda zC$|h3xsE^Tzxcd;x~z2iKBwB-t+pX3*eUlj`=g6Z46LV*rEqaZF@tnbP#1Mg*urn# z0y2F#)zRg9QG6!$)ob4|2j#lWzW>e)e2FE;_0@iKf8Y5r0EFP#6w`V-MO%uhbGwlB86j_AJzIh*#zB9X-o&5Sf zg=?YrQLLCa=sYRhpc?pey;lLR6s zE|XAOlV*rKZzJAlwj73PD*NqAezj7wVH@>9B;7v=M<3~_E1o&}J_r}9aUAxIfZx|a z2^A2q<~$7VaVdxFEJYbUI~ zBGlj%zq6_6BQ!OBHB{A}03D zL`#IsE~7uO>%Di4?3*7cgQW*U&}W{^YxXR;ARM>4ta6W{onOLr^kWy0wy7}O&z8FF z>l`+d@5+zyEcQC0hC=p5{ns~sLtq+ZM^8xN8{PlN3$6HJJ+2(kE0`moZ!ef*TKjZH za3R2uP`~??t(`lN6Jl`X4Y=e;-Ztxt>9V$3$mc)lR=8 zEo#k*sx=mV_iC+slplFzVPc0Qb;J=i1#S|rQ@>yA*&Nz(a(BCK)Z^0%g-f5E;yy(! z-F4}p|4E}%_=jf9(;{({s7fy+3KkoF=h*Q z7sXCm+Q;jGY6f|cts|Oom!-RpUSwvE<-GW&81)V(pE~S?yUAGQF7KXc;Cn@@Dft&t ziEOf>k8zRvx3U_&%-UN7qJ;M!agy4({kcN9I%FJYnH;^jWuA{+A5O;FSvJ+`L)%W? ztVIz<30CPwy@G7N=w}mg!^+2CLldjN8Z&CUnO|0>#Ih9-|pRa)H+=%?W6!GDMhy9Hv&Xt7EtP0&Q zavUKa++dBt+@2SzA1wdyVNL3zsJ$K;`N|UzKD?;%RC3!w^z&J2C`rHEyTOa~fba#( z?W^?%(#7P=p%0P!R;lSvTtDxW*Bd{i6+K>t)P~}SJ{Io3Fk-cO11ao3PY3#bX`^0M zNm*^1n4@T-^Saw{zG2-ZndL&E0fbR;HJ32Q(G?)+koN-H-w$zlzeD^(K21puo%>0t zQ@z0RM*rZ^7kj?a7YeBThFtbh_IrFpQ40VLmoA_VKqIctdKiPqAFP-GzP?~B+5mlX z5nkeLi99qQg7x`mRFNV24P|BgSAj6^Z5d=TL?j957bM#jIY3n4Z$UTW_O6l7!d!sI z5BmS~OcF9iV^b+&BlkK1a|*1KGmSzzTnyi$Gd6R)q|ZPWs%n@bk~FLjatry{maI^8 z419yu1N9J+l3<}PB47Bw*ln}+drYC`D4bteK2;xZf?TrAT*?G_0JLLYSGmItR5u^dY2ultNtK>m(Mb82z!+d~` z$A$|6n=tp@8Fb%05yC6^e*105ZD!=*EhN8*TmSEMWejl5@ort&0!%gVP53zb9Dbe? zfE?>K%!WdMyQN7?RP=v1kpG8m0Ci+wLI)E*VV+0R>brBYkHR5y$IrwHFmuoVfOP=Z z7Z?zweEtvmil)HRUU`5~=02nrkB|shI9v%SPF&?09FuU(2 zu~PZR8d1lIS{vWqX>*T6ORUYS-CsT&SdAmzySxk!YU?q#-lOnoDh0PDMMuAkM#QLF zwmha5`my3;dfn zmW4rn`LkMwaF55^%&$9!xb8Q3g)rpOt1-Ev471NE1y= zJJL*FkGp&=u=LU4rO48@ZX+~dVrQkh!I+C6 z=^%5BQ^&A~HVD9{X{(P_<@LE%c}nxzV9)7g-r=@)`>+A2J|$OnW8JWSqKRH9hH*+O zggr9@Ws%_3B16@_&f8J4GeA{~60!#P&t6_a+fVY1tBxCj*QFiv*6 zG+jds&qM5R8&p8?!eqO4;Ub5i5&$&B?h5)Y{kx0cuB2fA00)1F?v}cx{td1>^}j@S zhgUaX0oBzF)&EIARZ%qngaKH3^vLRJEXbYy9~3+Rz#aB?FeOxo+yzquJ^;3k3@p1%rsQeM1o;4C{hz$Bg zZ7>nBTeb2|IIjD%%C5_}!eP0lAU_8U^}a<@jhyxBQC1FV4GM$7gtst9uBG+vLHb&T zs}*2FQe{9!b#KT?{baW&%!AGa`}`AIH}j`w+EaSkT{&t7x-R!8a_N{RGyA5SLd6m$ zEjXW1jd_xV;b8EOyVDJEV(?RWQ@!(e)yHNTp}qL$74f|OGWO!nP~TgyuSsZD)xwTX*I%_Ww-zt0#cEEOCU|b+%BF7B z2TPw2n3vvz_2u*RLiQF0c1!YJ@u343qE6ZWqX&g?!m${Pyl%s#s5WMyg@+; z3{t884Boq6Oe7ejn4m4#l>TP`|0m9YRb2fmhVBUUE*}(F%3Y%f2&+l|EBQOafo4zz znYjo2!-RtRuX$THn6sfzQE)2za1@c~1>}!{zc4+Gu}mUL)xqXY;E5lq`|OUgAv`nC zX0swij+D!Zsj``?qZrXj?P9R$E&~;Kbh4uHpm= zDxk0i3Iw3g4hqAd0KzpOTnobN|9@;nb(`8ndHsoaRf~nAJan;C$kUlonM6%@?4emP zDw>nh%6j5?>7(9@9yZ#6ggPb4R?VfY!WA0wpD*6*&ays>AQ_#tny=($E}OD!3~dnI?Q9bsKJ)u??E-iX;V>WUj1LheF&HDdn**mqAshyP?XLgftkU1k z{)d0ierx~&bqBNjm!NP!6M%ruNP#j0fYQO?02B#8q=0Qe3Jw8#FbMu#U5846Rv>}= z|8TZwc7Fcu$}w=;-5o;kHb!3X#RU9q}(wd*V=5uv6u77cFmlupzhPL^!X-2 z3KOngmNPxm2!B4egAR%@fU>#_RcHb`79%bfVrX|3sMOc%w$f#H;wV ze_bN}fDlV`?D86!>U;k|>*ZJ3=SGyf>plpWlWe{-cWt3928deY8DGb^+a+mqhUKUql`C8{S zDAyM-WqYwG`v9$Ue?J76+y6?szTdHdOx=+G75P{5u7?)zd)E_s9P;QI0Ng+S@9h3* zi_vh91Gl%gf3!*&2sm_gaU_dCQ|UCkX{lhaCIXP(V9DMwbYSA5VxB=uPh(C^&nS#v zVri4&Q`Y8nOD)z@m&g4b{c<)*azsF*%RJ3}<+YZF#zUpu=dF(%OLSi5oUSXE1znnP9(ZyTLFxr2%{L#NirNMvF$DA{A51KuKR!lAxANn5nb8DRuYCD$F6ZlrZ_djb(S-`wyTcB- zWuv3I`h|KDufp{MueV-g51-C%@W6+IZ6PwMSIP^>-kC0gcUNtv?kZN4A}Yb&-&#i~ z$n1K2XL3S&WQ`8aLoXJ7Mt@YK|2~b6S-aSA%Q5M39mC97NB)~h{}j$eawuAC_3*I- z^hsRY)U$}fTcH8pDU2J5y$;Yd;O~vAcOM^j8&@g7^#AeWV-37U0gS1K;H7ZzYpuK!q4w!tY&)b((lBnU*A@Ei> z2t_x?i_zWFWA~zdisQR(JIR3cp_3Xq>@>pX)W7Jt6RUKpiQE&%%fV*NKT9ba>}z(a z45OWpI9+^`6Uf4I36py=#<)^plg(a>-o}{)Ylt?{;!2uGr4jC~+*Qq+@CR!QyJ&Sy zYEo7l=<4T|j&g=rWOU3k>Jf@61Jbgc1uhB0tfX--^4ar7sH_j<wJN}0h+mZEI2qi{*UmRt$_?gQ(1=*2_KnIA9uuJS+z zd=dYCU^!s@M^ldle+htLe1n&ZXDuf_t%hd#z$DV2?v0}FOGi#+pd5@CNH#U_4|57$ z&TGE#Gj2F=@}PD+k8+xnmJJ49lt6hUP}P=Z@cf_yp?=8z%a_IFIpyk;(Z-WEd5AaN zSr;IbrZ_u3sV?A6?{s=M=Pos4eZQLLNauoE{e5j7mEzX)zKsbBiI{@(MA5*3PVTCDXX zQ>y`b@ZE+rGM=|b)+N8GRz;8B<0>pfvL^q?&3r!!+}ViYfQ#9FsB zx2RYgRZ%BWc7INtJDy7QScOSxzhPgu1h?)gwQXj*P`zQfsD9{l zT+bewINFEi8tb$gPhSB@?F9ro(6adc5?FJ|n_|8|AoRbG*UEzO#$_i(=DTIsP)9fj8XQ zR*!TyNmtL~x0Rb7F)Jm>wnPuX)5{hq*rU;p&RE8UjCi7~&o%gM z{Pa$_z7S53>N)P3HP_U#8*?wqQABV*FXywz8Ro8PTXfXzXh65JguEt*Q?i1f;foBH zNSa!Yy^dlTISgnFocGVnd@W#GJXDZ2U#3Iyj83vVV}UqKDo%vlCaZ~kUe{FZAwjL5LMuOO(=N(! ziyvD7^UVm<7_}IM3)m$hmILeWU4=ge>+g1WKJW|@Mj+q#Y9E4GjDz|z0tG5=-<+SH zBF**bh6*M#DqY(NV*$4DAHeARAJ8#Rvs%4`ZBNIMU=xjYm|^2Y@ImlK;ZYjk24=1WmqP)d$1>fk*c?zUL6FrK=yPGx%V-z{) z+I@97xgQcQ+#VOy)BtF_!&-!%Ky$m_KB9iaS|$Hum`GWM;@X%d7Js)#i-1P-_Y3E@ zEtb3Owrv}3{Dq8K9hNOEX)p*dOoxn!Y4Ke$sOzpNhtWYc3hf?aM^=~Vg!n0pxe6bo zzn5om1&rRZ^L$liV=?9EN1_~*0ssO*7XwaNoIYTIL<;3HUj`FB)qg07L>`~47kiH|$NEA;1Fwg#T`{dntaLa?Y0_wWD>z9@H0I4(A7Wncej=;WWrJcx26LWp z=#GxKgPG*4#;Nv#Uo|Z&F&m|3gD(_`?1M$XHN|2wKbgooTuVw(M(XYVDj66B`<*GkO!s$DdT-eCoQjYm z=d7$EyscI}#Kaz2(nIwSQyw%X`E8~vt_Ta)dGsULb7nDCPF)dx^SxR{Tv2XZx1le1 zURhiBgbnbieuD%2RAsLMN`QVqDGfpZGzw=40g5mrJp_d{A0<`Vo4(ZYp_znD3t6~< zODf&l1CMJw(?TG`9|=~Gf4>;eaT@#Y#+lTZe1%0l-o{k4inwqYunIx4?*qUc(*stK zxme8`n`e|_n7x+QW_g=#U&2u7$6|Wfepy(kZ@LG$J8AWX3+qfv#~piI*Z1%mg*D)oq;Pr4WftS(j0;mw`*|(Y#SYipqUHfsHxU4Loa@$)iW3 z-TQ22$@)1Amq?_jWm0w(&Ov4yAHg_dY_?df;5?=Y)7MJxb!rsFFeORGg5ycwwOvh< zMw!^m9d`%0(cM*zI-mFx`kG!QXAU@4Po-lAygP{abmHsJ3135nrX)q<;Eaay7?RZf zI(s_F8VU1l^&jXrM|V+zTgHoUnDHyB5rGjZo~5N zyp3}_W-&h8uQS$g*OvM$tJPJe7B&DAM4@pmv#cBGYL9d=&y!stXg49PwS}mkv&C$5 zwx17jTsr59;14!U8l1%luxmccIEx#3GxEx|NQ10qdA-Tczbgnq!M~`&P-iiG)~w%4bGtS-HmQ zZa5ue$5FienHs5Omk#UBdO_{&mg|%E%Ac=ScxQx@+s8spYWHn93y(b*dWdyjno|Y2 zkgKj*qQ#E$xyO=h4mG~MHo)={_IT{}Oq_UPyNsS3_kj0H5gDtof=!5(T5Tucn0h#G z`^$Is)M+6HH5s~X)OIvS$G8Kh1=ZOijESk4PAJ)%dPb${hag@)2tHxZ!8GSVZ@UhzE4!pPpo4 zNSSlXPWvz}*LbSZDd3))C^fl}^s)}O8X2m0sM)W3Pp!kTJ_!H%-a1mNrN=OfXBnyY zwr<2_+Eyh#2tx9_wvnhsZ!~_+NM)9SKU#h_B|2e?$O((-K2yTfXp)X`MTF6-(n6>x z(@UMmRh_sI?XFe5*om;<`&}YN@k4|T4Y5&HdN;`B+1kVzJtoPiT7IjSpRhzu1v(tw zNswJ9t|3C2_zN4E(i~H>LvtJ`~OAY~&-#r#vRZ2@!_thyX?F zb#ad}{suj41;+47r})$na^m)P6d&0NO@z?Yr48HPk?g@`x5ak(5id@0~0|6`-fo>s> zFO=hwM&j6J5((dG-!wuDcZgfVQ@Kd;#KUSvOqkQgP~gnvn*57$G356qB7Gg4O^hPR z!lZ-@@YUBj7TP3KuEl3%aWwJP{--I(oPHylVr zW6f|Gqb30wl5t+fRQR|xBT_|Nt=rBJ9Pif5FFgz3hy={+7#%v*;-2b+ytB#gb1l^F z#;1|Bn11M9M;Y{qwhIb!(x-vb4r-lVrRV`Dr@A(BWyhaL_hEa@nUGYmn7~$jvP?+q zh-1TG6bIAOPA1}qHF(jkBUyC7we|2SLhdpwmpOHi=(a zT;W||MMr&*vtJMt_|~bP4^F4w4Qms@t`gipg;_y>0cfaN+~1=MlP2#+i$Xq_5Mqbm z5n1Axi$Lkf!>Oo{kbXA^5fFm`Ea310KtHi2z6elFM-F@f03=ZR3=5HU{dt<-xOGzn z%yTn{_L?!8Z60r(l)lHKXzGvU){m$>3`p;aeyE$lcUM8WU=KpV-=s`Lvpe%4_z0xT z+-rc06`cPP)?{vH-2(WbKw(%-M*pMVJW~#A^he5wNuV+DrWimwqY=nM&M^V>daXV% zU;;x2K%y|EfolbV0xe>65ITfaKfO0be`-E8P`8T<>EA?&n)-3=3yIQ$5TQJj+tf4A z8$UBa>Cb;Ub&T60GmHXYq2YAVRhti+@r2?4 zNZfG(K*(Li?x@Rk34BE4vV$JcQRfE=0LW{~fdUX|OEkO+s7SvQzbt=dDwZ4yW|bxc z6OgiX2k80%R86U3_Xidv03ZP4wPw{BumGNhfw{bOm0>Bz9kZnbg5IzG=BC_j9KL~6 z1C5gVXaIr2aHu{~1kL0$zCA3x7i-oTaYa)J_`3{M;`-7Q^t%g|$M?hoUu-HMQlGGn z;3!Bbm5JJ`$Ic>MgVzi|8Z{XPAce>@L;+F&I!!Kcoe#C$xZ{NvV)T|{Wk^ulhKZ{^!F9;g~wu_k!zl^+)0GIne zbCEFkKf|lv*hSOO)_Xg$qvks%f=W0CGV`|x0~EYPpuh_X_Mm_R3KXCK!T<c@O#9Y77b}TR|8cd=v7*be|Fv=AIPFY+4lQDA57BCyj0)nhGj$tA6lpxCJ<2X0|&xJDenVu?J5dx1mT?-BpxtNA! zqlBHtM$4vhk)yhc1TRDKMlsSyHpcNbPw+CV?AP(zm0!-V*SNEjqz@9uknSj)I>+$$ z9^ao3BItV|=SSfdo1+Qq6}4Ql6V5B1kE3d|j@NZ!mq}yno?+3HsX>>mBePUa%A>BG zVq)ef{#p7O(J(&FaS;~>G7TWZaG`{dKqQZ2YDH>xZ3~ZdOxvE+je7B9rU&$yDhrjV znsMq~Bt} za|wv#2~=LmBm9DbUNPcf<#Bm{{|zR{B2xZJ@7$KHzbBKaYoKkUomA2?+&&Vu@kAYP zUQTI|*ZwqUraARSB&}vXXpnWk!MM-{lcm#?v$(TU#F*}VxTjlg2O3_7Re}kjnU6MB z+cqU5>H>T?yRvQFlq7+1#5pUi{n67;=Mn+OjS;f;i1DXS3+a{!lY76}m~ zlR`y*Z|zIEG7Mekx+o%kb1ZH!WkT(Zxhpz+muDYrji(5_0S_qHgF-wgtbqb}OBe|M z2I26#`=5-(7P;$0uG^dsKYO}jNj2I^vbq^}swQVI;IWTrcyAPY`lqfkYq!yXvobR1 zezeOFnE-6T=wj`;ida?SXgH=TYh&I#3#ZA*qM`I7h|aT^UM^8q7T8CjgG&o3KMPe> zD(%?u<1D6@O`?0TF~fNX_Krq2XRHb*M1}3pYga9+2gaT)On7!4Q7#h7tBilnn`!`5 zrG6yzgti|*;iZFIV@M*QI4mNJ2cp+!df80WTwOJ{RR1z7oz9iZN{vH%V3Hp4r2+s9lv0rax!>>3N(#==5kKGd!A=fX=D# zCyruv&M8Grab#UdBErmMlH7J>)sxIP-kBp8KShSj*hRv)R)}z5TEp16{W8+(F+rMm zALB~{R%H`{?7JyS50D`M877bcRl}f4`TvCBWDlLU`gRVg9}k9jDl=!6sXHv1tYrwh zs5n{7m$$O9xv1pjflMpNsDKQpasgHIe`8}HHd(!|_lgbdKOMc9LYeuOeMhclJOwZiZo>QNeynl-@A|{|jpL3pvV6UY}(b%0|OYpUw7LJ5!MKf82)NF6!&w z+??pcNM+y&?t3!#DNPA*Yu5ifRq87|l~d6q%fe(E+G%&`m-%@MC18ft)X>q@fTV)c zRCgBXS%G)7;pd;hoc0Fx?7Uwq-%A4%yN|7#mIf%b(!GH2 zGJumSIW|ypbKd!U3N&dc>2<&>&cz%FdBT7efjXNt2|-Hqx9PoPe1PXypMNf5<@_z< zgL(YiZI>JX41DP6da{{#s}GV(pSu1&^?L==ko&-`u&L?MWdCjYt;<_-pNUdRksU7* zBqq4%oP&WvY_)KPn4bA*k+Ohm`IeWy`V)OHS*hGNR2Y+MBqf_BqWO3Z+_*U^HwQ(% zr-c9O_TvD%3#HYJ==LA;*s65c5hD)S=|LpgSImJJoj>te8CXCOoQ{bfLXT8t8em1e zbbASg&_Da0lC`15w`;mz&M@8sJbomjEA)pWD5@ZxtFm{l6xBblDk_9{wbFN?*> zBCUu@0Q&?w)Vl!wXJ7=yX++-L)d*ta8l?$66oY`nAtrPHxMTQ1FYZI>KPYh2V^Y>} zHrF*aDa8u?Ka_n3TvN-_=t&3=stGkzLx)fdRj?5t^dc>EHS~@Y!L9)U1PE0?q$ouZ zLJ^Q^r6WZY1*s}kYkm!9`!w#k+k#z@{kDA01hdW9pypGTr!SSb*dbW$O`@k%tSTlQQa-`j9&d zRD13TSP$cB>)h+>nro3RE-lSga~tj3 z>lW=Ao58a}s0ARbXFYWah+IL9x9N6Iu-mI|_Y@gwk&5=XAv2xhvUt$q9%Ur&b3WNl zw)hIfHjKwYFfsvxg3{qHcz6-Wo?R-`!X$(G{0LzwIV>U=E~`yX;MCWTItX!-LAf?B z{V-uem_+!j>>Nsvi4F_~VFUOVLU?%b_7H*6UFeG^bxG=v8O(`@pcW7h3!r)m971Jh$0@@X zB$ak5($v-@Ws&$biUIa${BL%AbfyU2JEETJ+1)1mIERb{vc3#*(` zN73B$l^%(->Ba!t73vSmxY!~maoYUxHnSCoihlfDkpU;j1|3wF0fI97M9LwZ+Zo<+ z`|wo<_L;abgIlE>W8^u5H9G@7QkOw$j7+(`koBbElAy47&}Sb(@7sE4J(5(eZF88+ zz18MQ5&tWf-5My*dian9xR24Z;eV+vbb-$aLFFskq4%s7Q3+(KEW{2Z_SmSbip$xy z$b})58uYu7M$aMypSc<7$J%r}s^E(|IH`~pN^`3!#b%1Vu+ujyz%SAT%?mK}lE?%+ zPY;)poqi8ee~~H(D!Cnzr>ZHkCv~9*ZC1!9S`t}F*BUK}q~itc^kwY;k(mEPO%GDe zn8r%W#8*<${*wBO2=A}pT&mzZ=g1ze7d>2oC1}NxNV7r*DuRcoFQI~L9#-z9v995g z;G2-(OGP{Ma4ngM`RAd`@*Eba0(Ka0Dq0TQTn4^}rf)7O;p*&eYHcsCgR<#x&FUl+ zqRdL_NXuA&co_Z%K(u;ew0`rkeA-=Nu3MB?3RKbXR!3h0DP&DaFrp_#hbQ!Dlu{_WqfzIza^d(n-5^R}^RZ$+w zRm;V4dS%d9#Qea6 z7EYry!Hpm&0h|FDC(*!3Ff0mbY!8D}A#))DC?5ewlnW@pK(G1`l7+xS8L?||!J%#zs6Ydqm zvcxnMe=${hT@M}KgTaIP2@blW3>`0%q}cR#=x2$((^{fL zKtX@MxWLL-E?MLd=jq7@(R&8j!fM{(>=IH%@3;j>>e{wBS=gRlv`KD=G7W%=xt;k| zj+>$3dTl;z5ET?h#`r@RFplXp%1Q|?gL;sUT3>l(3pypiy4vy2Jvvj^F z5##iZF#4@K5nCbI(xBIc49>2}2o{8N_yS);(29(XdMxw_Mi`|;w!A6z14e}SmB>T` ztfeM~*T?~{RT^H@=%F2|j1Tw%?bDuO!xpdHXfVw+h%lJ+dXV6`6D7eWFiv=^6aJ0v z{z6od8u|!B(J~+~E*czbA@&r84oEK@+p`U#bR=>b5t|&-2B&AV*#wm>R!?|tI?68x zpA!&x`x|y)G$isOG;E`#ZDh=6_n;yEnM#&9(0YURNH-kYBkkxSBrgibEmWnZ-Z+BZ ziBN5pzhWL)WnpvG`VTfgD=4j|*bJs-*}k;vjXcVMA3E~m7$GxtQJ78M`N7gt=coOF z0Rem5s*Z3l60M>dG<^@8uTzJd!ZeK+_DUJQ<%H=paUD-()3|S=ZC_q>BgxNF3zn<| zAp^C*iF$7+ZfrJ%h z_t9ez{jco!s&zC;A~IYuT{^J5+9h$FkpTC*x)`X;xNF+Y$0ta*LSFikt)Up)Gq=(?VO>Sl*t91H^W}9mR452+lwEA8KlIW9e0I!U zX$%F9ltZLY&2K1-Pxw0i3NiMn8Z%Y4T@@^ZdQ=x}gs(Z%s$5#-$CcHdZ74lS*t>w< zzPrRL^~7ri{~nTp6Rb-ss`ck>2+7I8q0diK!s#$12_>s$e?S7C?{(y+6rPYP(tqDS!w!To#>eo`sITrDT0WEDdN1Q+1Eq+5d9TeAmGz%i%yNM}XCVNPV5 z!R0;DZ@9V((Z>`uppVoFcDRo=JvFtS-BZ2kw%ORIe7ZqArDh=S!UgT?7olC++S){R zcvS}*;apU~W6+sJ8TisM*b~~T_V=A(W%qdru}l{-QCwj}pQcy7{XU9B*$&Bgr2*h#oCAaX_F zJCPu`p4u+A$JtXApp!)Y)ouK_Hm1@r+R~>SZ0_&f=A{GiG#EcQ0NNXD0tU1}0m4$- zGei2WS~yhU9q9M~I;z4AWjw9blO~%k;*g#XCNIr&dpGBpeU5(e?&OCdxn4=bnv{(FX^ETL0 z>zXr3Mor8y{X$MzZoZRSmvQ#%&GYD;wCOGc4MP@R{pjl&kavw>{mLsuDc6yUJL#cv zu40x3{^vE)uou6;0RajXj!5c^8<1Ud5RjxmrcP6NsJwyU*3g?(RJ^83!+}{{1uDHM zdOf!+7wd;wl~G3LhYz?>urCdNXj8CtOrUD!hB8xuVSLpHm3h#BrU({k1^Lobi5Wv=`Y)A%$F zg^TpyAWrAQU-eDXD#~xp?X@f@`*mpj*}L_k(^Xv>7F@o1#y8j9lwoP(6=fTt2FB_N zA9$dV^8y5hfWB#4qs-N}d=}rqh~wr~`{Y7(vDo@1PKYwR!Q)>2%mss3)WB}%P268* z-{lHM6|e#u2x1}rfXDA1Ra}{%B?chgy2{jOUZ=F~aMrhM*)y(iV1&AjdfLI+5EbW9 zdg=Du|qhh4xzD&L}I zp4bgKRqO6Y2ou7aE_=CG;X^Og>jXvY4-53EyJkYUR~=;(_2sN(Xq%G z(hc0p{e7=yoK$gd@%!Q>_8litVqe&)W~bYD+}0i^J1X^~e5DCRs|x$wa^5$kyw1ff zZxaw~jN=PiKf3)Sr}z2#oXaKNH|r%d3I$}NAV0n%)TW&G$8HV|^r#S~IWT7(EcU^T zRD%M4>|C zry1f8JO+03Z#(Js^NM;y{b?fu%~ znx)>QJM4Wec z%!{!SL0>;QSJ<9}G;{u+EuIN2USHE?XfLMyuO&m%84#8G_etUXPX)$Z5gN$j&&Ayg z#edy6InXU;y~a7p_2u~y|=mD#=!!ed#tqJLQrR( zo3(1$?A6T|~l_%KDYz4eR6G_j=}jX}1?b&69zB@>v26w{Z0n`*sXD><--h<+-sw@(gd* z65k?&a4WHw*VUQF+Ca7NLmiRt3ZXwb#{qld@KKNEp~k+pNtAn>M?>??17I-ykXYP+zrZ)KY**U{VB}vVBcz^{YZzATp+TL#Zl6$9x zY;OR2_Z7QX3UN;J(1*ueGs?xV%y^qaseMgNfrsxXHseN79_g-JthHT_9}!DVQ?FYRxYH=H00U&zFMn@5ik`)mmWEJ5I-k*Tw& zu+ZGll>>KuKa~xVxVcGj7=`ejZ|t`%f`8fWSu+5?Rr)&u~q{1At z-|=KdwSOs;REbl4d2s#|>0;Y>Mbgc%P@~~cO*oufGjYWtNB9Wi>~%ABuh||bz2Jt! zHnLZob>6fxS6NDDQ%6G8c#GAD$b_?>r})PG?``T4{Wm`7WDaKhV?c z)wx6I8i(0Ob%xs_FTRwC(sF>R1!Ed(Y*&18*%!avM4CiHmkP1#c37PLsiYU&nGd}jo4^!-?PZ7VY zyN>t`h)gmZLdTxO2Z-KuT90mjz8JGx6TSA}d}L;wl#sNLRPcSAyVNCn8K}Ic_5;W7 zRIT6Srdv8ze8HY)QV(qx3e4bqdNWYJ0EObIe4TnF_*c`V+kT{x^ZMt04SE&0D2>Y} zieXf{?_e$I?$z@8C?44unOT@)pK6mnTkmXWe{mv~cAeyaAU6|bBAuk-@-|JLy{=xR zPV33+DRU%Jo@Tz`NPFqqpxo0Cu}p{GOP0;T^6xb%p|Oq07DOz6*})UNY|wLHN1kaM z{X!PvR-Mskp_U&x-rEt<9cthZ#0D+qZFykW;~~J*u9Pd-R%>T{}r~XI4U-G z%O7@sa5TX$zsm7Y#gal=lf*_O~{$|H|(SBU*F3h1kH;M zEwhPD{g#td)qLufy}iO*ujI5uxOB*Hzsb@{e6X)$))UJ>YfsL;sn}+j;fh-xHhE%K z6=HuDx*5neywSVgCXnjj9;+}L8SzKX^6W;Rk2&7S0x|MulSZHd*qdptM)T<}oO|B+jyA_w*s>wo z?%Cu_2|l73MnR*w5lr1yQzE}`ME`f8h4MP9Z=tRcJoDVaH%BzCT1|SJoP|x8#9BE& zxb$0iepoi8(roF;VP9PBu}1Sl#-}_@hV5V8AsOC_ZU_tw!_3|fMrZtZ2w~*zJTibF zOR(_r0L-PBXP2|VaE{5`0e`;*{|fL}j!}-$L<}YQI~IQyRp|h5BVNpOlL6yp3Z94j zdprNKIJYKQxeqeVl|}|@mZ8`P{GT{AopQH3`e-evXpw)5A6!7oLRTsjX|Ru-LW*pg zw!cy!;zD##qMDOJkfLuNU*k1f%o|oRxa@uVi0ZZL{WGtTVK)nq+1ohV2B3?FCHI2*ys@Z^JxmsY!J0-n`0m6~=PVOXncpPSK+1(JFkt4>~`dhTZF9okKt9|xNVF%mwx2JxSgyoYdzf}{x5Dc5knFYB@ocdd)xQBDnUm=JAYaNT8d(V z?^t2@2%k1xg>x25svwoqdkVnL-(R)Kg=y6u26>dY>EUK7ByfdE^ zRI|rFw1)Z>5vZeCXEL|JB9iy(jLJ-M`qfo;#2XRU?Ha!L7U&mbt&l!7<9@W?wlr{z<{Y zi7D{OkdCMY-|*8K%=>o{KhF=7q2dq&WS=$DLzti}qfm9F+d0y=i}L-25gpcb3dM#e z^ZUe)Vx-P0_fe#-`%KoTG4@~f4rI_?9ZJ~s@{!48xsZ>xJK-!-z=l+YIHJR3D#(0F z#NZcDpT-#>1yv9CPz$YA2xiel0-4br055#~zT_;54QnKI|C0|d1`>AS5aNC4s_S#wH7t&X|Pr>o`@{4tI()@(Z|V}!+Vr3?C;2V zqezT{k-3w7ys|!J7Nz@!WGGQ>FDx%?kKp33*rghkUKuYZBy2+>aca6-CLByqfJ{aT z83}AOSWI4cVI|)vCP`&@GzVf-$uLr3#SDgc2yF6FjRK6g&?qF2zt^3{FcCmRBKW!G z3C;o!8TVyA)>!a*kb26gT_|^YHA*<`6}vZ4xH|lEt&PIV^p&O>6^49R13Q7FEbg3e z`Gr$AUsPIJb#95RP_`ubrJW7?djh4}N<}Xp0WYp%#8*QkdG_j%crDMjvEmhR*Dw6g z4k@y-SKuU3G|EC?jh_p%aX~qd#Y!Dk3ww}6p|e3FuX1lKT+K?RCL&^6%n0=iJz*mn z168(4ASrQ;xRVs1RTG{HxHIJRiG;-xCS24n{!{P zd9Z#MiBeFeVB2a8&mOavq9C}M6GAK_EtSg-RSP>9S!(`LKoAhgoVbuf$_&)RY1pxi zPn?-9>OCthk?}hqC!)hPgi$<#215xI70WA&S1`t8su&eJslhmdPmK!I>O2HSr6HXL zD+cRIOhX`kWr8kN9M2sDCXGL~`)HA+Va5C4*qU85khPsP7l)}r(SVc&dO2nno5Su0 z(l5}lg>sZK9KjJJDY3SU8j{or1P5V;1N>pI&?K8P%!tG{jJJ%&D(FpeEBn9+2@r$C zR;%LdU`RBzG@KlFp0n_i#!ou^yud9~#%}5G5LdZfBw=0$V;|Y_`0YSz9`75Owq-?Fgz>$%xTuAi@#RULZ23Tfc zA*?;pAGT38&t0+h(h?sFpff1Wr7rk6ry8PGX@=tZxm0J|;w-uL0yk0f`b?yb+@4$d zL|MU_klq`S*Us0qO-J1Hh@M-|)}47XDN;P%;wO-*4+Jz#tP8U|SkZyNu4;DH*6M#c zR`S-3u_2?u(&TR#=tsUhg2NDTfmc+&)`s?l}00lyP7(oaC*g+7m5Y|U!SuI$dBpsX}9X#;QQh~tW zJq3u%KoQCmh#Si~fTR4Yz^hOfFGz$Lf`Emv-k}Ix^MwW%ea}A&0~zmGxn*0g6v;v< zfE0oyfHPQxtoQWV%3#f?suz#KQ84sf3j<~-zg=wnNbCjv-s)<|2l1LBVPcg13a^i) zU@q`i^8KA=2GRf>9-JHW_qmy9Kd>tr2q0N?c1u18AY;pZ&eru<*JM?d|C{^QdDUIn ztf~&P0s#wQy*A^|GGlHob!7lms{;Yc|5e~t#Rrf34~K{T^Y1~dX2|+`&^6%yht1Fk zysL0UzZGIbZ76Ad7>+w4H#HVox^c5!7&VtC z)g=?fGZ);7>yXq)iHv<4A|z4}h4S%aNe6-31kHJq)tvuA4Bt{O1L>?W7tkC3y9PCG zQEHeuQtkn*Uzj8gShE0#19-tcD9Wai0s*ss^UR!D9NSJl`@v1M^m9Ol{>O=)fT`@` zh8w1ei|>YHhPFT8j7ZA;!vZSKC`KIiDg8e33Lha*YWThPCQ3gwPG#nbxkS$MgUYX$ z4k_hFK`Z&Q?|gL|tP)NO*0II)Ul+B#O41z)zSTrl&f*z5+o`Di`88fy9EBLD8fZf4 zV8t`Uhx>=IBpvK7R035ik|(Z#FQpJYe5CDyWLDd3(c~%Zj+wBi&yOA*3)e1;FY!Gb z+t)9)$jJ2Gd-7GLf7WbdJ=7oArgSSbDq5hK(NskL*ciTTAhDAhl8!`;ESr#Zeo zW}vNjSeY-(wrPcOP4VKq6&VtQwT$~{sj8o`-h7q9L)#t`2*ZieuFvcmOu>i+uD|MR zjXhWP#BPYnzASL}-nHea_nHQ0_zw*}Sp0^>!}-!E#*H~$mI%1E*#aew=N4B$`~`P{ zNOtl4Gun4%v%aie{}pzoNur1And9SfZy)izl(Z;VYKNpe_wd^V@3MtYYIldYGJUcs zqsrXEO{yOl-ocF~K4PZQNz+0Rhz`Er@nHRD``v|W>zzp%XPqR(8Mr@K)+*Xura-oX3ZM)TTAvcNRR=ibBryr`)(Fv*@WTg6d?KsUVwRaqD~g zs6Di=KMnQU8;ak+V_lohxsJ_gs{@9wga*b!0j104V}pwpY8sQHG zG*fd984>0!42s-uJRRdcM-BMw5)(eO>!-r64%!S))qJu}68F;O8M+3~y?UvG%Gyy% zbpIbiJ+o4`I=&b`5}oCzAK^=WN=4%9s9MC;`JB!F?<{*!sTRqMUwHb z`hZuY$9nSa+VKFLeixCW&lOhZo7|Mugwok7oh+MVJ0)IyL0vK)uIwf&%L?&wWd643 z)WM`*Ci--fZ=F_lMafDIig5Q`P3R87lNylywKI`db!X{j=|45Yrshw)@`@ZYh#C;v z2`nu=-M)PFo14xVI?R-SR$e5cjgd0RTnf|2pmqym_ON(Q4=^&i{wu^#P3_PoRb$-5 zdRnL*PgOHAtFEuJ9xv{itLZD;*&9xYa+(&9(I3o6XrB&}t>d~hQ1k<($Ds8;dJ=if zEBwku+DMS+gtUZ+zBI7(ZrmLd>nz9<((Zba9&o+;wvXnF>&R;K$xbhgmJ4bkE{QW? z^q9FX?0z{NkvbS|0n3#Q^&A`dr3j<;;wNpxjA?odimPvs)YK{K2~5TL%v**Uua~(i zwBP9HT2J{YF^y=e397e`;+#_&^)^2s)a1D-kRmtSbM>2oMqK(ur;^O?xi%s>nOB;W zVvJF~HZ_+EQ`M+3yi)hgudf~Sglg)M*tjqOMk`a?-UkiwJM`jSy3Dg9t?$B* zDtUz-HskU|_BO@rK?-r)Bd|pUU4JW^!M;IHT!O{l(yB@|rV9E}L*KN%&~!J7dr`Yi zMre6O?AMd)=St#&@eO?HJy`N?Tqc-!_4*IJuNy3MZnd8oi=Mn0eH z)J4emWRDEF7;wV%jPRABXdeM@&x}Zms`W1dgEHbH6UXo!yw6Og2rCM4X;R+F>C^FC zW|-P`7q_Fm?2(V-=K0D{O7cbC$-6s*!`RJ)gk7bhsA#?NJXhKHZ!&hvGz99pessA@ z-wh!}jsQ+g$FNh}6^Z)j1&yDave0epX8iE+xN-$gPNn3g3)nf~qL4>*$zNh#*=4Xz zUYpFQ(u7J-sA>3Z0NU#vdu;* z_Z#tUNP&!xKY9jF%<_tZ-y5i%eSAo@B>a9!_SG3@?7gbXZOd$qD06K1&at9TDXC9U z+QARs;^gvSA=g?3h6ot)uvOplKy1PO}zw(NIO z;J&#KZN&Ly^=z0^Kdkw#4}3O$_wR+<98=Bh)2B}sv$dR5xfrOb&2D~scRD$yLj##6 zA$#lgOs9GqdgVn|>s#)CuhW*W^@GZ(&(ZSD7M1+LX~nHy`k%T?lh>aIUzPGDqEnlz zc5(GCPKT*sGR&|qitH_$zPug}X`8zcrdMoSYNc>R5O+oI+>lk6?rhjx;qbE!O}`Rx zdXntGVvvp-{=$7%8-K2w>=E((9xCYu{f zn{fOr)D6f;G539v)+vi}95Ik-lH@;nK7A-4qDGB>yHj81hCUY6fJ6+Qj1`KByn5Ni zMlXHwbhEx!RPmAiv2bhu_-|lkogXRd26iBrVzU`6I>~pJUC!P2(Vv~%CYl!&;c0^1 zH<+Ew_94bp9_0cEIwam*E>L8g)$}i!OWh9N*a*^>uA{NZdaO@6fO^qxojx*GtTh%6 zvwL}wXCAS$5z>hKQhXnN`_qK$TA`=id}Oz7%5$+;1UcY+I6YB{9KMqU+zi1iSiGF2AJD+H~H`v5h z)!uK5MM)STmM8>Or8?yK-o?lkeM$Z+XzU+hdXf>`rj?VeE({@Ot5%=d@gQXnNiegG z@pK|!^#XVYjLUsG$#}JVt}BW0-CkIsH1=3tU(>$hr%xvNXc6780z5blQu$24{oC`~ zT(2+Bg0IG%d4Rv|!K2q{yXQN4$95U{UY&N5M{wOtxwhC}<0fpnB48j}BY)1Nv7Q)t zr}(ftUT{KeE$#^wB2}Nc`(;5Czk}|ILL6KsR%Xsr(~={+yOV3_!NU z7Fe9&FYbb2v4_7Lj8)E`MQx!B20x&GUHkVr{|TWAMg@gJ0lQVPTjfF&MA?=dG9;?P z2rMe;AlVBwCn-iljSdGcc$=0*8D+=ILpgGIa6LG0S-1(jgEjw$p}?kD7Lp3mqjNJh zwR_F%UWTJ%Z`STKdH$~M03P<^L3`KRn*K=<2X$dr>!?;|FYEqEM=ua@1d+gr)yXJ} z5(kHrJ{d`x=|$WZMR-U}hesONMD2lVC|ClK0#?M+;u`umJ7>J@tHC#7u@*xzrzb(z zY8PkBbHCl}R5Nj2+MynO_@qCQ?cvZ9Oa5O<1ZK2b78IU!Y~!2;Zpm)ftIOax$1)Vl zG9U|%SJj5EF1z#d#rV4y_?Lj7^SeGt!ZJcWXT>ZRfKC7;0LTHr5`Yx|A^HP0NM2a7i~{#&c*FqQ|6JU<-N>%KIeFWNDhchfrvbadgY3xvDQ&}z+(;{iYR%;vxvv2L(brZ0Tn2yvzzqOY0m%KE27*c(7C?<>wX0y@ z13{e;*jd5Bt=S`g0&(j4%t4Vu?LO~TLf4m?m9KnTtB+DN?GXvPYp=f5a4L=@-MFCSt z3l*STs08>`q40S%*Q}|BuNR*``f6AHvTjT_HD;nMSW9qc-`euM0@D#9EgH`+^Xc>J zmPg$1FYm%5p2Lx3c_^Vc+v0&0s~khnU3lG*_*^HM@ug9(Q~SVXp3>z5{OwUzVk@sYlZ)fp#=O8aHcad7?;ypc9dELN= z^0ydxi;6Ta7{?mdUq2N{r|i&Q5m4G!>;YYbDCLjBKnE75dg3+o+=jLN0Agc(sI4`f zzc=r=b9o2E6QEKhkP90C0Sf^{P){;ysQ^XCNBVQQUdbmF*6BWRV{M`VS$KEwD6Eo9 zKqUCaLR&U$YcUDLAV?0<2ZLuIu%tuS6gWf%OQm4<3NJ(eN!P)$86Y7;jRrD>U+z^DL;Hr@nMnV^>_ocAtSw-UEFmBehE>yv>k980P*0`FNjHjTX zJTWBL%)ELTb`Orww&(n0M1iSFJmgg7ZA=A=HiU3{uX&-k8kllDmNIQLt)8?xIm(aui^Y6D~~H3r(;=Sjd+MW5=7JARRk!6Mz(s{Btnh zLcvlJKpb`w+#m~Ktu&hVV3z$md(g{;*f&V9Q=?#2{Dr2`gu4>Wcc299$gnpPQvjOr zG)*H(Ij{$#4-;jhkEScjCg^udq7`Yr-6hyeN%SZhn~CnGa4i-7i=jbWAHYo`U=%C@ z4$1!cVkqFEfGMyhQd{p9Yz6@L%;IDqx|ITQ{&ZLtAV4w@^&d=+_3(eWG61m@)~OH$ zcmS~gz@bwB0YLYothoXA7f^ zy*{zpL)SsLKE#1E$$|5jpHA7`zWaJlZ$uLaeDVRQlWRukIqCt8T z2?E4Mff1n~HY|>VpaYSdp@%LclBqChoWbxQk=SThS!beWcv};Uh?SL{_6!H2N}@AS zZn~|BWa@0{G3i2L;3{xsj5Y?ujwHhoczXszmO`pe!?}YQH}rVd%YU_(27)8S3RD1C zE9lNyhz59iS5EYDcbTu$Qj@-F#}IS-ApDBQG(3GC7ZZTKjO8 z`3D#mGKHNy4o)t-W(Q%{*iy62ECa z+T}q_nz+;d>as(N?P)Pp$#9wRyH%%eUQ&;ag5~W~79XM1*oaOvjvE$){nXG!JZv%A z_U4do|3Q4}lZ^~nF4cIgQ=bld@Ac-2XcXYu$ zxPM)a5ZXwUmy8%6d>$mlzh7Q00&Y%jXWooZ;Nd?&q;{?g56W(&@&ke(ojEB}S;c2< z4=g}86>P-wEmZ6~LNCl; zuO+<4Od&TVD*$evyCV_x@=`DF`>aF1-oI){0@gO}MyNKPg*=Bh5%-RWrQdgJ3^~b> z4_m1?d#zw(Cu-x|X%~BmhdzBR=o9Nfn-PU<+0}ja+L=>9^=4KB{HL=XN|?K~Dh24g zN$I~`g9kQ%M>2f|O9;s}DID$L)3H>!g_LdNF63hW$dS+|)aNCXd>;MZH0Vpt@27X; zE0vH~U&VsGxXM_^NIvu5{m>pChyb>C+yT8}3^xRw0N-_I^4D5wlj@@l+qR>LmQKQd z>~X1?(n#IK7ch7y%$OhJf&U~LJMmmqYZg&4bw=Ny|I=KA9>&#o!u_`8g{kFgRAraRTx(jLpVa)PxhOT>*uV1us!jBznD_hGEkm5V$ z`sk|bw$(Xqn;Sz*H@^rW1nykFU14yXw+OdCPto8b;Z2Ews&4Ov+vJo8E>4$cI~yFH za@c<`JCLSuJB4`ZW~j`brV)YV*Js|`8W=Zk()%npwHELNy{jsfxR{xLXWu#1_=dyY zj(%^^fyBXWxd#oGC%2vNIaz=B(c~*BXTRXnioqsfKLS+F;pY)VzK@!%J0=u1{@BO2*D^IXcKBXZ zc{|+nsq&Qv)I)f-qelKHR9Qv*PZ?GXZtwz zF;08hD@pe)tp@1_kR)}@EOkACJcSKEj0fHc4_buodVNHb;E;mPq3WR1U0UZP4w-2b zCo9BkJ6=urQ&b5-H$)FTe?En#KX~Mv>M0su> zc%chzQ)S$Wit|@}?9tf%b#}J{!RZg%;OEGWoyWb^l(vOgmmaor?w1pnd8Xj9JivIC ztR)yWAevp*iCqZBiO2?-Xub^(K4JMoOBLLNV9`QujeIv5)igA>+(7Wv2Cn{8ynbXJ$}ZMSlHH_9|t( zEX{QsYtXE+{Tg%lriF6}?A{mnSAOcT(t2-?bCt-!FstfYQ4%XufkO6Pq#iMRFlzTa zN=(0x56&fLG59Voz->iwH%i=hiX+F!N8}RDZ-)Q@F)bg@!5#jg_~c9ZyL_hmmr@qg z`ER8@t(dqh7FTg4Bu(+L%~W~~u+wX=V%K0vVI*rm(@hM7qc*gee2FFx&&OP2>#8y`-3oSZMX z+I;ia*zGU0bE+lv8z@;F4tlbY-vS10gAh1RF||L}O@?dT9o_NA(#W?@YWvbH4?n6b zKO(ai*JmlbzJ8-U!}OWIAmgo1pFqfo)P^5Zq~1M9us)>Lka_R27*WV!hp!R*G)jXu zlRw~eNmNaHwD2SC(e8HJJAOazAC}gPso1+;t6iihsIBKk^L1J&@6Zw1TM@hCxF=h^ zANC7}w1XueS2U-!SPEyW4Faar5~<4HAk8eV|iMe->?Q zFi)}E+1;u+Ln`W54xalbPcV06Vw$0jelG>k?)$e`)@)M&3A z7{gyW*fjIbsz0Rjf}OIn5O?gZ=vFtz^$4{uvGJ#c-$$w)gbTprUpSWAleNTnhuIT{ zC`9EvC9~EI@N1IYDwAoeM|PFkJ61m)n(z0haeHgK?AYX{QT3QBKWSH~otIhxE=wZ4 zoBDBH6?=EUR^oyGex56@vxdlcQH+gZrB=C{#^nriV&68i9aB7ZudHfZ)RZ2-qUIZy z+L1M$ST%jj=(k*1?C5mE2^G;56QjHRbYum<-S$V{zF>bfPjL8%mQTHN%{c?!7w@up zvfR`})m~ApYD(?E9a!fZd(l-6sHu1#C|hy$VKqvYmji7NOQGYD*8 znUOE}?GB=0c`$bINIWH{`PzKi3C^mdPOJJ z0#knz+3QIlN0YC&{9*uK{$yE^27R7SP^hAJn&@ROPJcovhTGoNoHj1ju>uR|^ii3_ zzK0^s$!zN=>s|NY&37*hJz;-SAJ85yZau##!q~wdzx$EpB2uv;a$YO(qx8AbQ^g&U zbaVGMqdwC>Eu+Q9*Z1%Cxi#u^bwYDC59kD1TRJ)Bmf;kul5r#CU4@)>X^*dAo<{^p zHSZ4$O?LhJ8At_=2a<%m^iBt$7P7C-;~m^5auIE&{=f`rF8C`BO<)ThJ!j1Yn9oxTG4^1R!fj5|uFyh^bvD(4-5jOTm zbj3ElcjI0)Ev}7{`nb1j=S^>=ED83jP2a#XFT@tze$T((bx6q4Hf64@5_;`BjAV!I$txFi7a03ZQ?0Eh&D696m#Kzr=Jr|AZnm6LuU?5_gv)U`#rY4b(o zYYiq?Hjy8OSWn!TzuH}&I~S*>@-T#_YIbR*V{+6feI8ef-P`+yJ^K+h!d^$1M_C8hfV!UyX0SP| zm29AA|9!G6-$U_!EOllhBzmkH)AcUg3>X~{o(1=d24CSTAd;)+qxe$8p6?zju? zY-uqk2ZD$|>yq-|(p~cJJLSCdn=UK!7mO_9bf#nZ70Div%<%h~G;B^lF!Y2kB=nHRZzP?%M*#BkVr&S$Ox0_9Dq-JFh3qK|y};)Wo3Rft$?)5m2Zboi&|p4g@zW{@ncXSd&PA<- zb;v}}ZsC^V9$Bg0ujYx+xwd!eSzKGGQ%iip6Y`s<93nDZY6(Tk3z@sRF!${$moy?2 z`fmqiF%o#YulA>|@n7FFuzbjIPYh*)n%G>%jpgQRdGl7G=^2@AnnOZaxn47&J`lw{ z3NJCfy7$Ni#)MsnK!ZkvImw@4YwT_1V*R06w=d^h?%=UaJ3(*kGOivyvCD&;U1t!) z$r&Gps+D+%xZZ9QPDqoR=Xi8aHZ2)T(H9HI)5LPn`h$J4F=i5B&FqVHuL5!~jG;!x?+#jkP+rV#4 z2f7I8be!I!K7qTRk2ZeGrig1f8PpUfX4yLpW7ADYem2T<@~BtEz<%#*3!+8|O4i zT8MFtx&Qik|4c*R)9)@f$Diqh`A2mK$&SS)!Q;Pu)5D~At^^sxB@|b#hi3Bji z)IBi5czScSQ#f!aI<{GD1c+=V0LYDvK*RwNj{ApM)x*QP{ugua0nk*>H2~j2!7^>x zODzSVASfUpI7=;M0>Nls2qa?8sEbUHBU-|sF37_!Z#_f|kg8M})s@68o z&|k(VwA)+etSGKbR)t_F*^b-2%jeF8UosvFzz%ktz+8lK z1g@6yRp#)AnF^A!5#i-%LsSIOb!LS88^2LgEhU8(qCRj#D6j%3+K1F1M+iT2geBu? zGo6lXvy}&1w%;ae=X$9vpn-XLhBC#ngL~t$LfH52$UlOQ;i7Qd3X@m zmCR-XLx;m)zH$P{fxzB+@Mpp|1ZoN{kBBZG6IG5ZLv$#}^;R@A1Z1G$WDzthg1gVA zz};Wy@-c!3Mv=n7K)7IKKLNqRqBA^+0|J0R9wDv=c5|>j{S;fLVE4@zlbJ?Md&Dwd&wC{ z4`TO@R1cHeBP52S@LO>ZFNnrKMdkb@8pA+3UJcO0)VKpXnN|~fuEN8m_tp#O%9{DW zC0Exjw%~*{yPN%JhI$FB{)oOFxo%9|P}V^#ZxxC1$dZ$kvxd-N~vH&fFvyC~n= z@Qfz?C_-nV(#6O-4K17O4(4(l7oI1Tf(A)H0IM(eD*vnpJG;}8|_CB7_Pi{NvJ_~TO~q>xkmGe}7Chbk-mtx^k;WMm)?j0emgPypr& z$YYw<1Brw6@fpEs>s~7d85pdX-pMmxlj(Q3rK4e|o>PCC%>kX)fn8!z!rQVzB3*xj zjbdt3t-IO{+Tng5={Pz1MHRF{>V~=HhN18y>|E=l&D0dn@(vp?#T@*#GZ(_edyMcX zPD8Ikdmdby+HoeNAl{`wK(P=$MhGZ6(QUhg3l4Ya9+;k-XEg^2>>>8>7V4C3ZTh2r zP39xBG&3?=xhp^aru z@CG-mmN$fK*Zgp4Cm|y4wEG{u4fO3JMalAjrnimTCy6R<*YS&cdu-@k2ZWwQ^VwSDjC_eJOs`ff2}RcF>2_NkXgcL5iekJcEf@?mDi zW~4&Ajpqz2Y+6D z@y%vu`277=>$L+C+qhakq5T-04x&IQ&?fF$Hr;h@Ype0Oou@UL-*weKaWk~w9=Ncn zTOzr0oWrMMF(JowrNAqVI7JM1-Lf)HgH>Szr&^q!8gN(!M&Ax`F*w3hzJ;hsxV2+b zxKwU0tMc+tC7VwzYm*luUr_G0K5}T5Be9I{&L}r$=#5j+g3(CjqW!7w@1}Qdv3tF0 z#WnY*wN4Rw3XL(^2*sIqTRbI!m@GGc!t+v5P(Bk9%jG||EBn;E znaHWK{Mf(a!oA=Ttg{H#-@U6+ir3DmIP!RQMAsFgeMYw&!;b7gxRhj%3G)^jXM{hC zR#o<(PrkS1QUDB7OQPqk7hl& zZ$_EE`C>~N?J*Q(&)5`^I5(~l`2Ka>j^_4P=T>kRk|mUHp4!))sObC!xydJwn^lgJ znOG5}vuUl=T@ZCX#hdtsH&ic_hIJZz&!ta0A#Zb2x~9vbbKg4i;jL?fN*d$upGJZM zD-6G$k5c=7yhU-SyxZU&a);4&!T64TdT`AK{VIurt)FgwKBVD^$#6frT>OIkGn1fO z%9Q-~lBDvSPD*$7hglz?f#HWz#oZ%wz7&&gFCO7*H92NdyR{Ilqg1}x`ZnC-Q%o%C?M5-W59{T83PO&8*NX8>?AYk-i9?*1Bt!!>+D&UDbK1=NYUx8widd z5c$HBRbl0JGLS!a;xS+G6w9e`&jFj0!J6MLZn%Aeb-nMRZr+l!zAx=?t3`M1jkOj%mV3#v zoOgr4#Gu#SD;LkdRZiU@cssjVIXP3xO!O5z|NbHYnS;-u&mP%eixIaqvb>~vwUkST z`(kbdt8uAS(CrdC(XzQ}4vAe|%2s?<;*}mw$at3s;&t;d29c4vB6GB{`jXoH{3 zDH{qOWg$zPd{Ch2#uTA$j$QSfn`!|$jv2=w!b3i`^p3E!vCt^4r+FBJy>^!(cj|Yb}-WBR1FUq<7h98Htv$VD!1y?SnxMmGf?9Z5(3d9sdS6 z1i5hyX2JiU)2$VPY+^7PRaR{6MY z<%xUm?RYC~&fhvBS`BE{;(xNZz8UhO2>Rb#T=#}};ByLm7w#2Woyco-14l;6zG2!)>UDv zA||${{&V;xM-{E{S;akBQ@-9g$G56>2g>pd3%sti9N4U7cS+4xWzj%vY1o)S4z+a=OM!nl%J=zhhf5qnuHt+qZP#+FEXN*}S-Li#%9Tgp_qE7It zaF|QCFRMTP%D(^Vj!93lSb_AkXL0%o`G!L-O3oT-;(Pa#`!{dQgQ1`zpW z`j;M_yWSFe6_<+8;uhb;j*PC?lfPu_NYhK?q2W?f!XvhGBp&otJDE!B=pgHDRzVJk ztXk@2(VEGDDHX6gHNHu#{OOJ<>?R-LvYk`G)*UA-V~>rP;wbQ)^cJ=%R3J3wp4s{w zro^%m9IN_ZUenUl=6HeW?TL70Ux>UzKY*gA*4&p+*g=k5*%<>zDCX=|jQod3C}I|l zP>@rvERrYX+o{PcvDN3yppQ8HJchxUpVH?9$R5GkhT8YB-xA0cSJH;B1Bi zoXv2Bvl&p2Q9TbHLp{n9(%@_c70zZHgR>d6mb-Q6D@>9lay5p+^9^+Q$G4HY`*_<3m_5b3!*T z=CR-eJPIiPZ4)GTBSh5r2gN}5PX1H?iZ1-o!+s05%z_OV0Dm0+`{4u=)Nc)1X<#NB zZP2s8BcMPPJg7GP@>hkkM2(K#SjUX)rx!BywE`xL-nr$Zw);_exlh_JH`<c?B+>9S}yLpkAFl^Hks zO1i&BZ%r`_V(M<_yD9skykJ|z4Sugpi@pjI*^9=nG2YUc8)KSo=C9e{I;3!erqrJ+ z6=b|3qu_>0-hheW1>;$M8LnBWH8$;aq{5Tgu^(%zKKpjYnitAH=_bbc@){euAkt!- z<&p$OCi1=Xn_K(%MltgzEe6=jz8^-P+nyh#25nZVAAM9^kV-nXaw#v*RZ{8ZOnP^~ zqH_^~tTlIHPf!y+*+rz>G3ml(#QP{eIok66Zo^?YmyQGN^+%QFUu~I-KyH1nrffxG z|KKep3t(b6#e5GZK3&5l*Hzec_GC7%!FN`bVS2n6Gx^+Eim>MQdYUh2+#RpSrO|_0&t#Zaq4h5`6mc z9m8JD&Kg-+Te;nay>w*$h~X-ZK4*Tm#`0-|cGjSK&6*wYl8?1Z@(qvhOT}}$ zdDaH#IID=8o^Y$}b=H&;NZ&)P?iw!(FB|hyocQc(pe)v(8cE79S6L}$0wu0}V`~-fn{{i;{ z0lffLaQ~a?&&?lUp>Upfla(r={~|64QQm?@UhswMs6oWU-bJ%{{EGm>Sb>28=(~|~ z>8r1w7KgOLE#LRiw-lO(D9}XkTk`PCR}j7&^m_GRfIK$ji?EHPNXOQ7VoMPB$j7+;s(sv10p#z?-8*@S*9&c51HM}w4Dtw64e2+)p ze;DUhkZ1CsaG#>e+VeaD%ZvC{N>SZK_MO9}yl^*ttmSefVqd8KW@dRlf>881(Zu{V zYBZ=Vfm<>W49r{*6aasucZp)}@*QcuHOqI@paD>g4sW1Pj^@9i$P5TXWN;v0qXsNT zEYuAz8W*}&K}3E2wDNw8lX-ua&^$5tGKFCFfPn%~vWRWG`Xip{QG1dIVE~e;`+H(> z^FZ<=UKIGD;K{!z;xF)DAdsMVNZ{Xy89xw!M!x(?T|y67wKdy%fQ; z`*>s{fS6@a6DR;I9dcHiG}I}zVc4G{%bhU8P~h|e^!>k1 ziT?$xUR`@-aA5L3V4?m0WTE3A0$Ioc|VVYY$ zMUzH6#myT@P0Y7ePD@}$z}Zc#jHp0_>zTv*?V9Z8^0eVKWmhS#vbTPLjT@0%VBuMUHISR&bdAw(+nfnW+GTdKnQ8zr~DG~`B zcIf&l&C!_ZIMN8!H%4}P;>0!z7Fk2vm6O~4om1YGaLU_GrJ-m~u|oySGQ=`f@Zm7a(CTj9x`$)f z-1nH_OOlv~i&$GRUIsIjHC(Y=?lxOCf32PJY^Y|$ z*}|3vXIqwNY;2cn1($2p>Ff%F+GC0M=F6Qk~;Nq15pas z751c~N;!K%n?;Q6^eAjICmWJZorrcPC5F)Cg?J4*UT4}-L@te&6=n(8w!o1x&6TI0 zi#`ne=IB~|x_{6hs~7edr~f*rD8qP*a?LDO3~Ge2N?8Rk>@3Vep=te1G5!Sq9H0Lg z`}~7l2CjL5aTfh&Jb?ahgPhXjgB%z>Fap3nqrf?MMp8!C*IuQaUwU;k7VV}ECCZxjqqW`OR&2aaC$USg5zH(G718TgrFZY11NIE4QT&6^swqc4kwbKE>y94l5=c z=dRmL`OZVj<(zwJeu$*LTYs`3C2MKCuCTZ1@WXB9p2evP;;M<@uu!+%MmJIuY$@Khr%k;*tlYlS6pV{>JF}NQe2PC{JFG~3%3YVs z`JE@>IOp7M${`XhSAX)UdDc?(-NN1lDG#^B>lUY)4ttB4doGvXxjdNrWLR)~ma>aO zqQ#aW{Udhbu41h!w_;-A+dKEYA{tCaZqvgeiX711DJ$#5Bkt35uL&2qKerlG)4qPJ zg?i2+bEN*z$~#iNd7rb2h0ZD;k+w0{D!W_Ze)IYat?A_Jm342}ln_pYhWLLJ;Znq1 z)3fHBARDL%q)^Lcoz$CmO7V&HQRcU(TOXQ@?T)lEG+R2dBXX~l6bRX@PW25wla1R; zyc>zUScbcgW#E{+xdmOgDP2o1XXzDD#i4GYTAhX)8%>O_Q6-yRk)!z3J%`17^vexg zT%AMka$W6biEA0cm={gQi5%m6?aKl_STr!?rf+QAAA`T11Ylqw4wcX5U{+mS{1xFxz{D8}{gpN;wIOp3%9l=)Ox zE0w1BblDaTo7d@N4@x|4C<&$b4oJjBQem~(`PUBgXSeuItUotE5TTKy3anpy$1k1f zC;G36=TF2>`&SluwUugq|HU4ZMb<=K>0SP{alEF9Xf-KPVV{iGYNF z=rGLq0fM=phzppo-(q5b&+nfnE8(gwKw>sPBq)G#$$h{fH_&$^-L>ojflLk0ljsv^ z7aFYtUL+>Z86~;5z!?R4nnbi~<&!$jVdZWh!&Tz^T+yR$eJD`l2F*vB-HWUNY-XE9|!y%m3fFXA9ay znCA73rXpyG1NWTi%=?3THi23|xo2U}HMz<|^dkv6#rSV7tF8vsbw&soAw0YxVA-+~wEXqA!kJ2{r2UZ%Q^1BbcTL_wv z4CrNm2;InLQzkbA^DbS#ai_i_A@(o{iPOTH9+r3!eRp}g#+uv80v2BKHYRhEv&}Qg ziLx6N9K5;@HOR#9dc=$zl?gX{KsRVqjy&+eehoV{?ASi%7sf}3tIlnyJ$$EjRcN4( zD({iq3g<5`>UOOEG;`+<2a$Jn$YJvycr=3hC*1QR;GFyZeeQV#qCtlNbiR-7?ruRe zKhd*ZM>_gx%pl^^(HOf^@JU_-jz__bIEVx)KJ706U5o}!Isr=Lw>Gl;sP1+seGCWm z02Dwu{Rc~)e>L$o&KO*-6VYlkE2B7fHS()n-Ei%meOACsyN7;}) zd4VxIhtnYvfH50(vGJ`8KlEaxBd4yuTC&0u+uDbxJ>KLKHNMFhyH)cngLllSF!V6 zYUX4SBjaM%bJ2$I+BbNB0r4{;M$4jXhHMLiyy;~sz;EL(CGdV1~j{cQT)dX4*DvBY782NV}bauQ~DQvIR6t9zEZP(Yvf9^^JmaT zZUoCWD1d&ox_P?m>&%R+MsM%ooF3q^s)xY+B4A!bMVXb3B`m~$!g?lC!d8>QMAw>)eedTo+CF=RXH4I?@Cxd$aNIkxU8J8w2P=1iEemd%!n z1dkImyq_8yw&iJ-$(Ph=tWZB*V6yy1_Bpdtfx^snD_n*1-|ic~7WxF+vx)QlY9gCZ za4q!3KJtcB>9r(Hi@vRAyR)N>OPp#Ow@{d17;d!-ZQ-CyJrMqWQSm$!wG&5iWff559RR7{a43tPm9hK@K4MqilnZ(1o<*7fdCo0FZYSijhb zjys#18qz>cjFJb{h)5#|@pdbOSHn8>Nm!>IUef6(yiV!1(`tvoV1?vCPxG@kOwld* zz3w&kQgrPsQ;)q35+GKjo0|C19~P=JwsuVE#_m!xRmd95H!IG#!INs4-0NNoQdP7; zs){d2Rha;(Dz8DRij+oHR0p?Pv$Kv&{8od)VgF_Y5x$?ZhC4$ebq}1}%8Dwj zFB~~_ogsSQ#{G!=+yG$=!9W2R8xX5A^It!0JJ<&xn+OR*^N|Kx77(zXi9LsZ>5coe zly{#CJ+1w9*Sk2`pl-Fo`I%Gq0@i(Dxob#vt~-6r=S8d2%oX#s%Ju75LbuEtgL^JI zPqMN_?ykMQ>&3w{f|9aR$M(-vn!F7;nyho|u#`=SSKgL`aXS?6s>?i-Eb(7wy1~QA zfsbE$|FX7?IdTmaOeqkTH4jNZIMD?CHj*^rm!XUV#v2S2KudFMxni&CSdwakdw&vw zFoOTCHvW4YIR!y4q~k&#fI-X`p7Z0VGho35rU*g-Pt*zipeSI13l4&#RsC}xUZcOH zBsekXK|&Nj6O1lit8E#zhx6|=#3fAU*90>V+$$jYQ!y6oALgH^zgd5eMxzEz0SnTB zrWRZ%@=pawz%6)E7RXR4l;XDpSKcou04zoynk5RLJmGZx|0!%$og0;Q{g|^^Nvk@h5*;r6OZm#uj znQi{jEq|?me=`aHt7N~eahuCaSgwq8EZ50{X8@O;ncl(O8isyo8v9X^7P1?8AbJa#3ay`ENpjws3zH@#O$}NV0ORqgCtvh$` z$lgPvAEX=w@6krBoUHfESzmB#Ym6X!8Mxa$y7A_ialh%V7d}Em5JVw>VhR?<9y$XGpq+uKp#6ZA z%UP`EWpF<-bz8VetSwO72Ap^Ujl%c(tHTA-IZP5w4ya6$ZSdQ;VEDZRVNDX8G zNQL3alEC;Xq(sp>M1UDfw|NpATxqmK8c-0~|Y*2P5%4eyCxElCzLRq$wT+%ic|+Il73Pe-q4 zyLhaOyG@#Oq5tkHQ?@Ll8?MQFu3S2I@3I}aPLwgni1s&b3wTDv;iGY!TU^ScZG=?u z>S5ZVTy?~m0*3I-S0ZOsBeW7{VtQl-JgN(2Z8b+4y0{MA?G6Z4&5d?FGq*YjXP$Y^X3m~$vLY2_ zo~|!#^u26N@FEP)jqw}mw=r%P^f>9~WDBhB!#1dvSQie@@^FkfWU0@!HV2X2ZQDmP z?!1q_KBm>U*woTkq)R+dzd$Vs%cE$WO|-eHwf4M0K@CwMo6@7goZh)2Ol~Zy!H?$; z`NMiGW41lUA@{dNg_ZJZ+V`LF zpV)i+jGgrpNr-iY}+)2$1}GNc-5FhPVm0K>11yf zr{-L&{uo)`OclMjPhnhNPC1NswWrR*4*NKt`bazNbNBpB4di-^c$qx)9Mx`Q?T1ZE zjOWx(Xdm3F9p1o`VsUJ@K;Mgy7ns-WGTqPC7v<1I zRZgTmxA96Iwoa^BvQ@io^YuWla>?l}-&xHmG4B-kC$=e0h1ku?@Ggp%n`TT>aEp7Q z<`TW^dSv*OBM0ZZ^LkyKc`~-DyeIYC@vM@Nj4xrDclihgpS7y=(%wFxqL4h?=Cn$} zo}c@0&9Ik-d$#JM<=tTu-hN6GR^2JO7DXCdMD^GJ3#I9T^S+(fPp7uolHTP}Jj|J~ z!jFn{^NW2?yT~+cH6P^WtZU5InNi=bD>D%GF^t;wEN_$;dnUOgmE_3W!gG*ITr$zK zj(k>ha?V$~Puc`iNboRC-n;Dbom*Q}n55TA)?^9(NYcYh^D*DXn^}VfWn0E)n?pZZ zi!K#})dP)0SUpJP?TxU^_kz`fXm0o6y84__FD>a8{amrV6?Q7Nw`1xpVN088)wr)h zM(jOLvF){YswqbdKLk^$Bs`NOBluz5`=p}vba2a^PGKJXBJ0Fku^m$iBl|k{1RBeV z<6=0bXNHMt)?{HL6PWR4nUh6ZddzC3LuQGUJlyV1-WAqyo)c5`j^{^Hjk{c(E3C^R z#X|LMLqr`NYV0s84i~nynM*w)E3dpdN4)m1`KTwzQsmGV3SAFJe|dQ$EzgBeNJ8kU%9>gwso%Ex#nxMX?b@~S z^r&ot4^OshSXzS85_hJgot|yNr4g^%6YDBceIs{ZIvSc}wfy6=((v~7G;jl__4x+8Nagv}1=;%;Mhh z8y?F_HcSX4_H9=5=jkfSUgBq2>EW@rks=ea3O8wWB6lVErXO;7 zU#~WFTMMr*#6T8AX244cJs0)xt4x*^XZXh z7!h@A9*G^;VjfQ(-KbXfG+WTW^?rDB++)NMYySOl8(RS}=jQ&H#lA*nGq+r1lR4Vj zxL&EHJ^a>9c1d4q$SWIX9wTXp<7vB=8+wKPs_jZ!Xzo|abi$0fd$$^X)y7^tFOPg0 zMk19h^l9;N9`^n9pPXN@x4LYUi9x=<-n2-N1KE_d?1*_DCxe{te8cqXDb7##=fj;} zZBo~JB}w0yo0iCqMqYh#Jox<;e!bqCYyPjqA3}Wu^6-D}`p&*so|b!jp1qi3klQ(> zd!m6p0aZn(Q6?H}vENU<*`yP-aumby!MPXuS;YHq^1JT6+K1nySIEb&g#(dL&{=?|Q>P%)|Iun^Iw%EysK^esec zRJv+(PA=)!?1-AWDZlk5dLM%srB_xNgFfGr6;CbU;vE;OlQLJLnN65+dS{(uL>m_- zJgIYAmq9H|%A@m%pCB3Ka`iqtxrm=Typ5NpCINXBfGjsqi{QDOLh&dJ*{;P zA(hH7Yv39bx%y|>SoGB8O_))N(Q;Y~2GP<>{?Bj=WfxB^%A-#N3!%6xP4o$Mc{aNW zD|YoGo89}Ux2iDJM{i=T4udI4d{U<)R!1+eOM6JrC&n0JK_n?oHs++8eVTWsi-e$2 zQW0JcYlL?-bcdp3M4C!R-Q>2GVl>>W9u$#E4I_xu(P4=*AzGwv9TZL~?JT_nJSG^% zHr5<#>0+8zUW5qxgfUJwVF<4mf!pmX-4mxCMZjwGNNwgG>&SEVBIsMFQ1}TEX9U&6 z^pZ++t!#Sn4XR%9P%IP5!rpofH)+LCy%S`&b@V6!=^>?jegaM*JyG&%gIsC43x^1a zi+&a(P-;y%OCJqu7$oz`g^_jA3YhAHYVu(?jUk?`$w(h2+xe63MK=9CaJz4`VAxPu zV7U4JWMcVEyDKxW?l$xwppQ20dt0?9;(rtmKS*VX2aVtR(~Peakjz_FIg`4jy@a zW^cVFd;!MUh`Y5c7dy58_6pmH2Hm4kaaTv4**QhfHK>WydE=gCGVvX*k-GTnm#4J* z+?6vnCzz~`()9J3hmt@s2E7f(j3es`Jh=2gY&QG45JZ?(GXB-L4uoI8f z0|?71au*$y26%Ao=K zGi^_7d!`n9XnKNaFyu02FXO5HPHS7tQ|j9D2mFeI^ohN4xBQd+Rk9A~70O@9$hTJH z+-YenTD_IqHI&hGXh!oy)do9ygO#WEIHR;eiY`sX0ZXe>k<+inmeQtBayzVVFMx20S%RM>h!xp zKKWzx;DQSZciv3~eSTOT*D~nYDAV`#Vn%prLv;JBTYj00{MIG(I;pc=&8m~t$(lkZ zJd5j{q?T3AaF*!GxB6&1GA1#btE8iii%VaNn5x_H4%UNUOMf`gUH&fffd^H$j9*IUp-o=p{+AEuN%F-Hy;+iEZM!5cUP}3%tP`_K&mWjvdY{sFP5=9Gbpch3QXpkWAjq zsh1@qG7-lqU{Dz9I^#$f_H44U6kC%;k6G4kD1TE)MJ&8)*i*w^R^@Ol+=cME&_zv5 zC5_J@ykppX{nEufT!mBqiUyTDwqnauRyYtH6lRig4i$?bhZvDiv z_9#Pgf^yTuT!eUZ2rY!*)cpVx-J&28cPFGtI5Ymh1IuFb(!v``_4*-~XigF0Q}EnN z?t8Vzg|M9`sCtHYyOk0HUliP`8?$m$LqEC)D2wWo+^Og6tjWoS5e1gj>vj^NS4_-B zfpi*MyNW-`j4xYOteT9Ld+1Z|=v*Z+P&N|S++NdkI+2E7hL3jFAcd|cl9GC96|0*` zy`Hs_x6WB7tEEr$z*SEJf~mfOqW}PG>T1I;!c8mS0>}J?G=r zd_&^?y^Yd>WAiFKQt<|5S;M~d)pjb{jIu#j*kLm53KvbRUmD-7|KN3q(&`(`*S*tb z`bUODW0b$_b93IP9%N>$@%pi{WL$xbDG-$~%PPnc^; zxz}b0#2X|J_IhbLO!z8fd6Drt0b4qT1zjX>QiS|-*Eo_A@2-B}+Zj=N28jcbhghP?K+`B)NW;@p@Jp6}Q#F>NY~`_gVx? zQ7G#v3-5$C-vZ`El2UH6*`_-jvu)$PvvY0P!v|A4_pz&KGBg^AG_B7`lqH>IGMTww zkM-roj;*8*Mz5CxkE8ROE`MilncH*aD?9KVdn7-+R!pVSei19~!aAH0naSyYma;<) ztAV@pNUN1`J+0G}+!nF#yjLmlOtr&vJ-tQg8DkQGJiMo`m&9wh1e>2+BD1>Q(Ywpt z&pyRMLCDY%pDd9SSU+HNF2a4a1WkiCMoh!pSXcQ1r*jm$xBmot@B3m;&)e(`!Ifbb zbZjf-!&AnJ<@)fXCDjHWhVEZiDvmQAVx$|i&gLbEZPk00rrT5rFVZV;!`ji{u1#CS zpEBz?vFn0_@6f8-7_;dcp5D3>|DC;WJ~PF|+csKaV{kIh_f?NAdp+x&-_>7NTKSm0 zujlEkM4-HR~f|y2e7cV$?u6(4os8%YcrjNbr^4pBE>?<1B zmwwDkPnj21i#@Fjs}%_pUmM)d?y6Rr``%l9xo{EPhF)R}{84jnHLF=8-TPe1nO?mZRG zWNMCRemkyv!J**sCwAb-#U@zdG6KyW27O#JU@M#5PgqY zy_*ivI)!>MAVbtyhm0j~afK2d@oC z60jW3&LFO|v#p(@U5~)S-HHdKg}=GEOBJ3A=&=+~5G29b8KDvM67o2l6Yy=A_VzAF z-$x)U@LQ^;FwY}kK{ zOlz6r352#PhQD4|SR0K-!;+lPHccW?WJ8G&TpevpW*8?lZ6^5Db?OmSz!m{b(*qMx zA$H0~gkc~Uq~M3i2&KxSW%N_wEbs(9vJ5}!jnbeBC0-~`0$1p>RpXl;u3#E{u$pJr z2p)?4x~Vyukmy{M2N`aI0?~s4>4E~$AJ*e@Xc^zX=kDQQ8yl@bjlNqJnI){G*bh!` zE{u;j2*ngUfwVGZ_@kh<4RuaLFo`;95`tWC18{g4nHdI3NH8iK2Z~6-3FQ%|^THy+ zr*L5rIQ|pZFgZ^ZL7q+r{5d%>Y;#bUdi!Au)x!bhc1|`ngSxJ) zh}Wbr88m;3__%p~=96y`3h{9FX%PsOIeLG{CzzZ*eZ6P-P==(5%nI?VG#XVVl**yE zh>sDvh>Ji#`1o)uf?qlDtL_sG)*miADTAqA>tVh5h?R}%jN%%T&vIt3P8EKo0ZTiy zu%3_QuR+DsGnOnzT%KY=?<4u+=rkF`DIbjFylw=WNHB+V;4WMMI$_-MxfL=Wv4$Xg zSa@nHoTXM=Pde4pBR9_+AO@U@)953W3AhOZW}-4eLI40t22h>yZ?SHJv$ZvH-BaxKVn zlLjjvtUj;?!14tv3M?{M8vh$KeJmf#OaK@FP+$Wz%G1w*wuJZQa*?T> z^b2@f)0?Em*K*Vi?^lJLl~w<6devFk)TL`m1k`1zr#OOcToF&Xd2suenV4Eua+CU} z&HX*Co8!f~K4jc-@IUQ5DJVEMbnA`o=v%+F_C=<(`A=lFl&s}{{`B;=Euz;04JrB- zPb-7xW_tL-{5Xray7!!qd_Nc68*@e88F^8cQy20iwt3O&qLO^^qnLE*+n1j$F8^?N zx1tep1o?37lftK%H|=!f0EDL%s`$knLNN5yPjDZDi@2*`@dDqL}= z#)W!OK>nHZ;@ZV0M0Q}VZh5}`!<^9u1&g=4thlPR4~X7ew)DkO<;buNyk4RD$4wJ1 zs~@?QY+CzC7@>SZze+DC!MlFQwVH%vMZ}qqio+aulA&>n=j0O2mUzkG0>10}5nRP2 zO33-whhr>%D|}*Rv+XWd?I-HC^5Ip$8*>i~6acma8RnLrPAAf&D1ek~G^#^DN7+nCLnQFdeRjNPP{f%_64wf>4x*2`;L`wx68ZxI5l9yy z4Mcwl1cL~0GY~cSE#W~p(Y+`XKH%UBNB4QeAR2mK2q{drV?b?71Wy|_KrNkkk|`Wl z6=a6L3Q1t1trFF~;Lz+fnddfKker)<5W_g3kf1dX5e5M~0fBB}p#ap1!9tzpQLtdQ zV(_RR5y60kB{IOIg8rd!C>F;+9SkBg5Cj9cM*#Xq1rPdl{}s?N&^m$ypz;5mLStFj znuS{EgF*dDMg$iTO)3W6lZK~YC}91ifD--~48lbL8FY{)3?75g0}2WPgs2Bf4SzIB zGs+e{@?ax^sJI|koi$y$vX0E#QT}Tp_RtN4Hfu^$Hfy`W zIVP5ZtFih4?h_ z74cJUO^-Tt)oFVS9E1xD_fVVmnDPIyxlTJOekxN~HhXnNM37sRe}RCunNC2qj$*d? zl$7_Nqg(}#@Mf~?VDwZb1jlO|1i5AkZ>~e*+YAUaETAh)_qa6)xAR;z(@~62pU%cB zMC>Vw@Q<(4=-|1EFgzv;g?=>WG|0EGIp=p|btu9-ti1u8NP1NaKM25MN)f6@oUKRT zm5Cq~+EOssFqDu15M&1KlXG{zUMqU<*K%j(<3j`bcFi?`Z|+s+OeF0)Jz+Y|YxL+v z!#5yneZ=NO4g~qWyn0-r9vPieK;uHyPB<3XI3r)5(K69=um5f{pcpV{AW1%MFK3JH znBArCK?Rsqg8t8y^Yp)@fvSZ!4wLz>2)w`}1|ggD32~5-7!pEaHN=;W$7mP$OfXp$ zUUCdb7xb?@Amc@NaY!B@=!F6Ze8?-CXsBplY8yz`sgI9Ncug1-(kFauq|4~zqdvZ^ zwr*vx0}Bho-$DzA#%~UBz?5GP3+LZ3Oaq!Z!Fq(ng|3%~+SrtlqzEjQtxY_$6hyt) z5a6;HCaaXCY(tYH;YchWj24s0Vj94GLHZ*$6bNK(u@FWNb|u=tDSL!zU_(YYSb4k* z96g+a=q5KVAwAp;*^6;TM|V&$6WVz=pB3X?UXW18Wrg;5Lj^$N;{ZVVQGo%z6;KHb zTP{5OD|~Ed;W64I7Nlt$O%sG=B5I@pG_s%hu3&T<{G{LQ-dbmEL_Vj?Q^y1n#&6xu zj=ze5tk+!P90FdL9UKUP0_fH^MXc;hgjYMkpQ<@U)sf8<&}KRtWD5^P_z&g4?J|l~ z+~aH>+3XMlBn!l**WqfMCBJ7BfK_F1t0?-gVlRVeHE-%{@%7UKseps zElcHE9Bd$xg@m&J=#D91>5p`GtJ_*zuvp^_($Xtp;36XFiEcX!@OB3M4+=;~Kq3t! z`Ye{ugs*QoS=7dwPM3DmM~`>>LxBGTL5K(Gt9k^2W*&)z)u++IVwlhbVK;dUoC<~h z5)YRnH54cp*x1k~7}^Wz|EDp^-(cXlX*6?U7zy3U_zwy#M?+2$304&oHDYK|s@^=# z##cI%vK6v*pgQ$9Q34!(;ijE$t1K6ZCTW;(cOP9>J&nVVE`uKjf9pPV%2A{XeF=Pn z!4L(~!J{d0V!B+Z{Fu9thnV}WQC3`zM+fOD)|u<1xM@?R-rLK^Y|dIE^r61-oPu7taj0m!DiN7#nE}rqvZUlP_N~m*3*wN{6bEZ6 zpDw;q1@KX=(sx@iL-_Ic54H=XQKZeB+inAmA|*6%N>XE$lU}V0r>6MIyda1`T(w9~ zHe9mSi4vORnz?!v8js{^@s${zYiI1VyP3_33h!p#PhIqjSWESj>Z$4Xmgpyjl(08V zXK<+dNOZ4=O&zl+JQrXjuw%?)*XAgnratShi$>BHx7P5hc&-+p?3U>F-5zyQpupgK zAKp{%R@LrT-%o8<*i6muM$Xn;`EH?w$0jIG9JROMhrp-U0(Zuke#_;~wH&y4av2KdqAT80^SU#`RWpb<+L^17W zQiFN!J-?Cj$*s9H_2bJ3gXpOF@yInb+ch$#R{TG^##;Zc>40zM)~%Yq%ny}!2;Tnw z>yQ7v>gP(k-fG3}b&uOMm*vGd-!}WC1I0?)%e^~4eX(kES8w06dx3>+jL^ZAoNJAz z{i%HZuFm1=e;+sRw#)NNk8$6NQ#7CW!{KRwx{SO${d-0tGm%AVO{CC;8jO&UH^D*v*V;53onkE%QaD^S8UXwlN!LFpPbDS?` zYd5^gW}ewq`hLT+P3j+4vYmJmH1#&`g&9wUG`!Y1n^XmH{oVENNagPhX$P-!HAy@( zDD`}#TK0b1RoB8v8h@U!)^s!&-%!zhI%)cex+{&pWn^AV?Ub;;zc|P+A#}CD;m>P4 z_PW)7oA5GI?e}8G{SIMu4;0O(BrTmj`C-qKT@kUf^DaP7acvP>Z9`f_`wsV(>5!)UXofq=fIN9 zocR%_R2jDZUMhNJ;RA~j&0m{-x;rx1>|y4xZebAdVLWhX#$%4Cj_s{1JuFE{Q{8Ta z?^;>=WB*tF$DthcU+ymo=Y8ptSn+6jm70-@c9~|gn0KwI^;EpRSK|ICb+#TRBz%#Xiyabdk!r0i1$dH0>pA!=JLrU>rp;lHQE@<;g8 zqS6g-RL=vm5`zHRI(=rKm^%=IDg$*8UBSqmnvn28$H?~k?MI6q1evw9ysYlGeUg&; pyWI>zu)#~5bn7{f#zV_2wT3;=;CVsiih literal 0 HcmV?d00001 diff --git a/test/models/PLY/cube_test.ply b/test/models/PLY/cube_test.ply index a86d022a5..b460215dc 100644 --- a/test/models/PLY/cube_test.ply +++ b/test/models/PLY/cube_test.ply @@ -1,6 +1,6 @@ ply format ascii 1.0 -comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.3297435427) +comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.732158294) element vertex 8 property float x property float y From 6ed4f5d936dbd341bc12a19303ec11d7472b568e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 6 Jun 2019 08:29:12 +0200 Subject: [PATCH 02/13] JT: Inital commit. --- code/FBXConverter.h | 1 + code/JTImporter.cpp | 186 +++++++++++++++++++++++++++++++++++++++ code/JTImporter.h | 207 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 394 insertions(+) create mode 100644 code/JTImporter.cpp create mode 100644 code/JTImporter.h diff --git a/code/FBXConverter.h b/code/FBXConverter.h index f75222d5b..1aa7e13ea 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXUtil.h" #include "FBXProperties.h" #include "FBXImporter.h" + #include #include #include diff --git a/code/JTImporter.cpp b/code/JTImporter.cpp new file mode 100644 index 000000000..efee4bcb3 --- /dev/null +++ b/code/JTImporter.cpp @@ -0,0 +1,186 @@ +#include "JTImporter.h" + +#include +#include +#include +#include +#include + +#ifndef ASSIMP_BUILD_NO_JT_IMPORTER + +namespace Assimp { + +namespace { + + static const aiImporterDesc desc = { + "Siemens JF File format importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "jt" + }; +} + +enum SegmentType { + SceneGraph = 1, + JT_B_Rep, + PMI_Data, + MetaData, + Shape, + Shape_LOD0, + Shape_LOD1, + Shape_LOD2, + Shape_LOD3, + Shape_LOD4, + Shape_LOD5, + Shape_LOD6, + Shape_LOD7, + Shape_LOD8, + Shape_LOD9, + XT_B_Rep, + Wireframe_Rep, + ULP, + LWPA +}; + +JTImporter::JTImporter() +: BaseImporter() +, mJTModel() +, mDataSegments() +, mCurrentDataSegment( nullptr ){ + // empty +} + +JTImporter::~JTImporter() { + // empty +} + +bool JTImporter::CanRead(const std::string &file, IOSystem* pIOHandler, bool checkSig) const { + const std::string& extension = GetExtension(file); + if (extension == std::string(desc.mFileExtensions)) { + return true; + } + + return false; +} + +const aiImporterDesc* JTImporter::GetInfo() const { + return &desc; +} + +void JTImporter::InternReadFile(const std::string &file, aiScene* pScene, IOSystem* pIOHandler) { + + std::unique_ptr stream(pIOHandler->Open(file, "rb")); + if (!stream) { + throw DeadlyImportError("JT: Cannot open file " + file ); + } + + std::vector contents; + contents.resize(stream->FileSize() + 1); + stream->Read(&*contents.begin(), 1, contents.size() - 1); + contents[contents.size() - 1] = 0; + BinReader reader(contents); + ReadHeader(reader); +} + +void JTImporter::ReadHeader(BinReader& reader) { + reader.readChars(VersionLen, mJTModel.mJTHeader.mVersion); + reader.readUChar(mJTModel.mJTHeader.mByteOrder); + reader.readI32(mJTModel.mJTHeader.mReserved); + + int toc_offset; + reader.readI32(toc_offset); +} + +void JTImporter::ReadTokenSegment(BinReader& reader) { + int toc_count; + reader.readI32(toc_count); + if (0 == toc_count) { + return; + } + + for (int i = 0; i < toc_count; ++i) { + ReadTokenEntry(reader); + } +} + +void JTImporter::ReadTokenEntry(BinReader& reader) { + TokenEntry* entry = new TokenEntry; + reader.readGUID(entry->guid); + reader.readI32(entry->offset); + reader.readI32(entry->length); + reader.readU32(entry->attributes); + + mJTModel.mTokenEntryMap[entry->guid] = entry; +} + +TokenEntry* JTImporter::FindTokenEntryByGuid(GUID& guid) { + if (mJTModel.mTokenEntryMap.empty()) { + return nullptr; + } + + JTModel::TokenEntryMap::const_iterator it(mJTModel.mTokenEntryMap.find(guid)); + if (it == mJTModel.mTokenEntryMap.end()) { + return nullptr; + } + + return it->second; +} + +static bool SupportsZlibCompression(SegmentType type) { + switch (type) { + case SceneGraph: + case JT_B_Rep: + case PMI_Data: + case MetaData: + return true; + case Shape: + case Shape_LOD0: + case Shape_LOD1: + case Shape_LOD2: + case Shape_LOD3: + case Shape_LOD4: + case Shape_LOD5: + case Shape_LOD6: + case Shape_LOD7: + case Shape_LOD8: + case Shape_LOD9: + return false; + case XT_B_Rep: + case Wireframe_Rep: + case ULP: + case LWPA: + return true; + default: + ai_assert_entry(); + break; + } + + return false; +} + +void JTImporter::ReadDataSegment(BinReader& reader) { + mCurrentDataSegment = new DataSegment; + mDataSegments.push_back(mCurrentDataSegment); + ReadDataSegmentHeader(reader); + +} + +void JTImporter::ReadDataSegmentHeader(BinReader& reader) { + if (nullptr == mCurrentDataSegment) { + return; + } + + reader.readGUID(mCurrentDataSegment->m_DataSegmentHeader.guid); + reader.readI32(mCurrentDataSegment->m_DataSegmentHeader.type); + reader.readI32(mCurrentDataSegment->m_DataSegmentHeader.length); +} + +} // end namespace Assimp + +#endif // ASSIMP_BUILD_NO_JT_IMPORTER diff --git a/code/JTImporter.h b/code/JTImporter.h new file mode 100644 index 000000000..821a31032 --- /dev/null +++ b/code/JTImporter.h @@ -0,0 +1,207 @@ +#pragma once +#ifndef AI_JTIMPORTER_H_INC +#define AI_JTIMPORTER_H_INC + +#include + +#include +#include + +#ifndef ASSIMP_BUILD_NO_JT_IMPORTER + +namespace Assimp { + +using i32 = int; +using u32 = unsigned int; +using i16 = short; +using u16 = unsigned short; +using c8 = char; +using u8 = unsigned char; +using f32 = float; +using f64 = double; + +static const size_t VersionLen = 80u; + +struct JTHeader { + char mVersion[VersionLen]; + unsigned char mByteOrder; + int mReserved; + int toc_offset; +}; + +template +struct TJTCoord { + T x, y, z; +}; + +using F32Coord = TJTCoord; +using F64Coord = TJTCoord; + +struct BBox32 { + F32Coord min; + F32Coord max; +}; + +template +struct THJTCoord { + T x, y, z, w; +}; + +using F32HCoord = THJTCoord; +using F64HCoord = THJTCoord; + +struct MbString { + int count; + char *data; +}; + +struct String { + int count; + char* data; +}; + +template +struct TVec { + int count; + T *data; +}; + +using VecF32 = TVec; +using VecF64 = TVec; +using VecI32 = TVec; + +struct RGB { + f32 data[3]; +}; + +struct RGBA { + f32 data[4]; + }; + +struct GUID { + unsigned int id1; + u16 id2[2]; + u8 id3[8]; +}; + +struct GUIDComparer { + bool operator() (const GUID& lhs, const GUID& rhs) const { + if (lhs.id1 < rhs.id1) { + return true; + } + + for (size_t i = 0; i < 2; ++i) { + if (lhs.id2[i] < rhs.id2[i]) { + return true; + } + } + + for (size_t i = 0; i < 8; ++i) { + if (lhs.id3[i] < rhs.id3[i]) { + return true; + } + } + + return false; + } +}; + +struct TokenEntry { + GUID guid; + i32 offset; + i32 length; + u32 attributes; +}; + +struct DataSegmentHeader { + GUID guid; + i32 type; + i32 length; +}; + +struct DataSegment { + DataSegmentHeader m_DataSegmentHeader; +}; + +struct ElementHeader { + GUID obj_type_id; + u8 obj_base_type; + i32 obj_id; +}; + +struct JTModel { + JTHeader mJTHeader; + GUID mGUID; + using TokenEntryMap = std::map; + TokenEntryMap mTokenEntryMap; +}; + +class BinReader { + std::vector& mData; + size_t mOffset; + +public: + BinReader(std::vector& data) + : mData(data) + , mOffset(0u){ + // empty + } + + void readChars(size_t numChars, char* buffer) { + ::memcpy(buffer, &mData[mOffset], numChars); + mOffset += numChars; + } + + void readUChar(unsigned char &c) { + ::memcpy(&c, &mData[mOffset], sizeof(unsigned char)); + mOffset += sizeof(unsigned char); + } + + void readI32(int32_t& v) { + ::memcpy(&v, &mData[mOffset], sizeof(int32_t)); + mOffset += sizeof(int32_t); + } + + void readU32(uint32_t& v) { + ::memcpy(&v, &mData[mOffset], sizeof(uint32_t)); + mOffset += sizeof(uint32_t); + } + + void readGUID(GUID &guid) { + ::memcpy(&guid.id1, &mData[mOffset], sizeof(i32)); + mOffset += sizeof(i32); + ::memcpy(guid.id2, &mData[mOffset], sizeof(u16) * 2); + mOffset += sizeof(u16)*2; + ::memcpy(guid.id3, &mData[mOffset], sizeof(u8) * 8); + mOffset += sizeof(u8) * 8; + } +}; + +class JTImporter : public BaseImporter { +public: + JTImporter(); + ~JTImporter(); + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const override; + const aiImporterDesc* GetInfo() const override; + +protected: + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) override; + void ReadHeader(BinReader &reader); + void ReadTokenSegment(BinReader& reader); + void ReadTokenEntry(BinReader& reader); + TokenEntry* FindTokenEntryByGuid(GUID& id); + void ReadDataSegment(BinReader& reader); + void ReadDataSegmentHeader(BinReader& reader); + +private: + JTModel mJTModel; + std::vector mDataSegments; + DataSegment *mCurrentDataSegment; + +}; + +} // end namespace Assimp + +#endif // ASSIMP_BUILD_NO_JT_IMPORTER + +#endif // AI_JTIMPORTER_H_INC From 6b3d6c3b8f53a493e44ed60dafde22a243dc169b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 18 Jun 2019 20:35:02 +0200 Subject: [PATCH 03/13] JT: next steps. --- code/JTImporter.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++-- code/JTImporter.h | 3 +++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/code/JTImporter.cpp b/code/JTImporter.cpp index efee4bcb3..0348770bc 100644 --- a/code/JTImporter.cpp +++ b/code/JTImporter.cpp @@ -27,7 +27,7 @@ namespace { } enum SegmentType { - SceneGraph = 1, + LogicalSceneGraph = 1, JT_B_Rep, PMI_Data, MetaData, @@ -134,7 +134,7 @@ TokenEntry* JTImporter::FindTokenEntryByGuid(GUID& guid) { static bool SupportsZlibCompression(SegmentType type) { switch (type) { - case SceneGraph: + case LogicalSceneGraph: case JT_B_Rep: case PMI_Data: case MetaData: @@ -179,6 +179,67 @@ void JTImporter::ReadDataSegmentHeader(BinReader& reader) { reader.readGUID(mCurrentDataSegment->m_DataSegmentHeader.guid); reader.readI32(mCurrentDataSegment->m_DataSegmentHeader.type); reader.readI32(mCurrentDataSegment->m_DataSegmentHeader.length); + +} + +void JTImporter::ReadLogicalElementHeaderZLib(BinReader& reader) { + i32 compressionFlag; + reader.readI32(compressionFlag); + i32 compressedLen; + reader.readI32(compressedLen); + u8 compressionAlgo; + reader.readUChar(compressionAlgo); +} + +void JTImporter::ReadSegmentType(BinReader& reader) { + SegmentType type = static_cast(mCurrentDataSegment->m_DataSegmentHeader.type); + switch (type) { + case LogicalSceneGraph: + ReadLSG(reader); + break; + case JT_B_Rep: + break; + case PMI_Data: + break; + case MetaData: + break; + case Shape: + break; + case Shape_LOD0: + break; + case Shape_LOD1: + break; + case Shape_LOD2: + break; + case Shape_LOD3: + break; + case Shape_LOD4: + break; + case Shape_LOD5: + break; + case Shape_LOD6: + break; + case Shape_LOD7: + break; + case Shape_LOD8: + break; + case Shape_LOD9: + break; + case XT_B_Rep: + break; + case Wireframe_Rep: + break; + case ULP: + break; + case LWPA: + break; + default: + break; + } +} + +void JTImporter::ReadLSG(BinReader& reader) { + ReadLogicalElementHeaderZLib(reader); } } // end namespace Assimp diff --git a/code/JTImporter.h b/code/JTImporter.h index 821a31032..9466900bb 100644 --- a/code/JTImporter.h +++ b/code/JTImporter.h @@ -192,6 +192,9 @@ protected: TokenEntry* FindTokenEntryByGuid(GUID& id); void ReadDataSegment(BinReader& reader); void ReadDataSegmentHeader(BinReader& reader); + void ReadLogicalElementHeaderZLib(BinReader& reader); + void ReadSegmentType(BinReader& reader); + void ReadLSG(BinReader& reader); private: JTModel mJTModel; From 418c07a9bb0c74babf74eac16660f1b1d273e20d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 21 Jun 2019 18:00:53 +0200 Subject: [PATCH 04/13] closes https://github.com/assimp/assimp/issues/2368: add missign anim mesh for multimaterial meshes in fbx,. --- code/CreateAnimMesh.cpp | 4 ++-- code/FBXConverter.cpp | 44 +++++++++++++++++++++++++++++++++++++++++ code/FBXParser.cpp | 4 ---- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/code/CreateAnimMesh.cpp b/code/CreateAnimMesh.cpp index 1a052849b..ab7dac5ca 100644 --- a/code/CreateAnimMesh.cpp +++ b/code/CreateAnimMesh.cpp @@ -47,10 +47,10 @@ namespace Assimp { aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh) { aiAnimMesh *animesh = new aiAnimMesh; - animesh->mVertices = NULL; + /*animesh->mVertices = NULL; animesh->mNormals = NULL; animesh->mTangents = NULL; - animesh->mBitangents = NULL; + animesh->mBitangents = NULL;*/ animesh->mNumVertices = mesh->mNumVertices; if (mesh->mVertices) { animesh->mVertices = new aiVector3D[animesh->mNumVertices]; diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 1701b2966..5f7835461 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -1385,6 +1385,50 @@ namespace Assimp { ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); } + std::vector animMeshes; + size_t bsIdx(0), bsChannelIdx(0); + for (const BlendShape* blendShape : mesh.GetBlendShapes()) { + for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) { + const std::vector& shapeGeometries = blendShapeChannel->GetShapeGeometries(); + for (size_t i = 0; i < shapeGeometries.size(); i++) { + aiAnimMesh* animMesh = aiCreateAnimMesh(out_mesh); + const ShapeGeometry* shapeGeometry = shapeGeometries.at(i); + const std::vector& vertices = shapeGeometry->GetVertices(); + const std::vector& normals = shapeGeometry->GetNormals(); + const std::vector& indices = shapeGeometry->GetIndices(); + animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name())); + for (size_t j = 0; j < indices.size(); j++) { + unsigned int index = indices.at(j); + aiVector3D vertex = vertices.at(j); + aiVector3D normal = normals.at(j); + unsigned int count = 0; + const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count); + for (unsigned int k = 0; k < count; k++) { + unsigned int index = outIndices[k]; + animMesh->mVertices[index] += vertex; + if (animMesh->mNormals != nullptr) { + animMesh->mNormals[index] += normal; + animMesh->mNormals[index].NormalizeSafe(); + } + } + } + animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f; + animMeshes.push_back(animMesh); + } + bsChannelIdx++; + } + bsIdx++; + } + + const size_t numAnimMeshes = animMeshes.size(); + if (numAnimMeshes > 0) { + out_mesh->mNumAnimMeshes = static_cast(numAnimMeshes); + out_mesh->mAnimMeshes = new aiAnimMesh * [numAnimMeshes]; + for (size_t i = 0; i < numAnimMeshes; i++) { + out_mesh->mAnimMeshes[i] = animMeshes.at(i); + } + } + return static_cast(meshes.size() - 1); } diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index fe63866a0..d1f1689c5 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -963,7 +963,6 @@ void ParseVectorDataArray(std::vector& out, const Element& el) } } - // ------------------------------------------------------------------------------------------------ // read an array of uints void ParseVectorDataArray(std::vector& out, const Element& el) @@ -1280,7 +1279,6 @@ float ParseTokenAsFloat(const Token& t) return i; } - // ------------------------------------------------------------------------------------------------ // wrapper around ParseTokenAsInt() with ParseError handling int ParseTokenAsInt(const Token& t) @@ -1293,8 +1291,6 @@ int ParseTokenAsInt(const Token& t) return i; } - - // ------------------------------------------------------------------------------------------------ // wrapper around ParseTokenAsInt64() with ParseError handling int64_t ParseTokenAsInt64(const Token& t) From b87f4c03bab4df875d5ec05be15809bbec73d342 Mon Sep 17 00:00:00 2001 From: kkulling Date: Tue, 9 Jul 2019 11:49:59 +0200 Subject: [PATCH 05/13] Remove not needed init. --- code/CreateAnimMesh.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/CreateAnimMesh.cpp b/code/CreateAnimMesh.cpp index ab7dac5ca..98b60e531 100644 --- a/code/CreateAnimMesh.cpp +++ b/code/CreateAnimMesh.cpp @@ -47,10 +47,6 @@ namespace Assimp { aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh) { aiAnimMesh *animesh = new aiAnimMesh; - /*animesh->mVertices = NULL; - animesh->mNormals = NULL; - animesh->mTangents = NULL; - animesh->mBitangents = NULL;*/ animesh->mNumVertices = mesh->mNumVertices; if (mesh->mVertices) { animesh->mVertices = new aiVector3D[animesh->mNumVertices]; From eb5e829436a0497e84325609040d65aabfe30be6 Mon Sep 17 00:00:00 2001 From: kkulling Date: Thu, 11 Jul 2019 17:43:03 +0200 Subject: [PATCH 06/13] Assjson: add json export to list of exporters. --- code/Assjson/json_exporter.cpp | 8 +++++++- code/Common/Exporter.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/code/Assjson/json_exporter.cpp b/code/Assjson/json_exporter.cpp index 2696e779d..848a08b4d 100644 --- a/code/Assjson/json_exporter.cpp +++ b/code/Assjson/json_exporter.cpp @@ -6,6 +6,9 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information */ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER + #include #include #include @@ -821,7 +824,7 @@ void Write(JSONWriter& out, const aiScene& ai) } -void Assimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) +void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) { std::unique_ptr str(io->Open(file,"wt")); if(!str) { @@ -851,3 +854,6 @@ void Assimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, c } } // + +#endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index b1caa4f96..58cad3be8 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -102,6 +102,7 @@ void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperti void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); +void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*); // ------------------------------------------------------------------------------------------------ // global array of all export formats which Assimp supports in its current build @@ -179,7 +180,11 @@ Exporter::ExportFormatEntry gExporters[] = #endif #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER - Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) + Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER + Exporter::ExportFormatEntry("json", "Plain JSON representation of the Assimp scene data structure", "json", &ExportAssimp2Json, 0) #endif }; From da33262bcc02e3870df8da5a84c2b9d03aaed11a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 11 Jul 2019 19:17:41 +0200 Subject: [PATCH 07/13] Update FBXConverter.cpp Fix merge conflicts --- code/FBX/FBXConverter.cpp | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index fc9c8b299..a4c1015bf 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -420,11 +420,6 @@ namespace Assimp { out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); - //cameras are defined along positive x direction - /*out_camera->mPosition = cam.Position(); - out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize(); - out_camera->mUp = cam.UpVector();*/ - out_camera->mPosition = aiVector3D(0.0f); out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f); out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f); @@ -667,8 +662,7 @@ namespace Assimp { if ((v - all_ones).SquareLength() > zero_epsilon) { return true; } - } - else if (ok) { + } else if (ok) { if (v.SquareLength() > zero_epsilon) { return true; } @@ -1396,10 +1390,6 @@ namespace Assimp { } std::vector animMeshes; -<<<<<<< HEAD:code/FBXConverter.cpp - size_t bsIdx(0), bsChannelIdx(0); -======= ->>>>>>> master:code/FBX/FBXConverter.cpp for (const BlendShape* blendShape : mesh.GetBlendShapes()) { for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) { const std::vector& shapeGeometries = blendShapeChannel->GetShapeGeometries(); @@ -1417,12 +1407,7 @@ namespace Assimp { unsigned int count = 0; const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count); for (unsigned int k = 0; k < count; k++) { -<<<<<<< HEAD:code/FBXConverter.cpp - unsigned int index = outIndices[k]; -======= unsigned int index = translateIndexMap[outIndices[k]]; - ->>>>>>> master:code/FBX/FBXConverter.cpp animMesh->mVertices[index] += vertex; if (animMesh->mNormals != nullptr) { animMesh->mNormals[index] += normal; @@ -1433,20 +1418,6 @@ namespace Assimp { animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f; animMeshes.push_back(animMesh); } -<<<<<<< HEAD:code/FBXConverter.cpp - bsChannelIdx++; - } - bsIdx++; - } - - const size_t numAnimMeshes = animMeshes.size(); - if (numAnimMeshes > 0) { - out_mesh->mNumAnimMeshes = static_cast(numAnimMeshes); - out_mesh->mAnimMeshes = new aiAnimMesh * [numAnimMeshes]; - for (size_t i = 0; i < numAnimMeshes; i++) { - out_mesh->mAnimMeshes[i] = animMeshes.at(i); -======= ->>>>>>> master:code/FBX/FBXConverter.cpp } } @@ -1755,9 +1726,8 @@ namespace Assimp { } void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh) - { + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh) { TextureMap::const_iterator it = textures.find(propName); if (it == textures.end()) { return; From 82e7775d6db02368bbc469b014ef2d9b2e1600f4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 11 Jul 2019 19:23:00 +0200 Subject: [PATCH 08/13] Update JTImporter.cpp disable unused method,. --- code/JTImporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/JTImporter.cpp b/code/JTImporter.cpp index 0348770bc..12b17e141 100644 --- a/code/JTImporter.cpp +++ b/code/JTImporter.cpp @@ -131,7 +131,7 @@ TokenEntry* JTImporter::FindTokenEntryByGuid(GUID& guid) { return it->second; } - +/* static bool SupportsZlibCompression(SegmentType type) { switch (type) { case LogicalSceneGraph: @@ -162,7 +162,7 @@ static bool SupportsZlibCompression(SegmentType type) { } return false; -} +}*/ void JTImporter::ReadDataSegment(BinReader& reader) { mCurrentDataSegment = new DataSegment; From 8bf1b9ac5ecde9d60c6821cee3604a41f616a9eb Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 11 Jul 2019 22:29:30 +0200 Subject: [PATCH 09/13] Update CMakeLists.txt remove jtimporter --- code/CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index bdb589bc0..a8a514f73 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -297,11 +297,6 @@ ADD_ASSIMP_IMPORTER( AMF AMF/AMFImporter_Postprocess.cpp ) -ADD_ASSIMP_IMPORTER( JT - JTImporter.h - JTImporter.cpp -) - ADD_ASSIMP_IMPORTER( 3DS 3DS/3DSConverter.cpp 3DS/3DSHelper.h From b1d8398243ef94ea050becc17a9d49f63381c4c8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 11 Jul 2019 22:31:24 +0200 Subject: [PATCH 10/13] Delete JTImporter.cpp Remove JTImorter.cpp from pull request --- code/JTImporter.cpp | 247 -------------------------------------------- 1 file changed, 247 deletions(-) delete mode 100644 code/JTImporter.cpp diff --git a/code/JTImporter.cpp b/code/JTImporter.cpp deleted file mode 100644 index 12b17e141..000000000 --- a/code/JTImporter.cpp +++ /dev/null @@ -1,247 +0,0 @@ -#include "JTImporter.h" - -#include -#include -#include -#include -#include - -#ifndef ASSIMP_BUILD_NO_JT_IMPORTER - -namespace Assimp { - -namespace { - - static const aiImporterDesc desc = { - "Siemens JF File format importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "jt" - }; -} - -enum SegmentType { - LogicalSceneGraph = 1, - JT_B_Rep, - PMI_Data, - MetaData, - Shape, - Shape_LOD0, - Shape_LOD1, - Shape_LOD2, - Shape_LOD3, - Shape_LOD4, - Shape_LOD5, - Shape_LOD6, - Shape_LOD7, - Shape_LOD8, - Shape_LOD9, - XT_B_Rep, - Wireframe_Rep, - ULP, - LWPA -}; - -JTImporter::JTImporter() -: BaseImporter() -, mJTModel() -, mDataSegments() -, mCurrentDataSegment( nullptr ){ - // empty -} - -JTImporter::~JTImporter() { - // empty -} - -bool JTImporter::CanRead(const std::string &file, IOSystem* pIOHandler, bool checkSig) const { - const std::string& extension = GetExtension(file); - if (extension == std::string(desc.mFileExtensions)) { - return true; - } - - return false; -} - -const aiImporterDesc* JTImporter::GetInfo() const { - return &desc; -} - -void JTImporter::InternReadFile(const std::string &file, aiScene* pScene, IOSystem* pIOHandler) { - - std::unique_ptr stream(pIOHandler->Open(file, "rb")); - if (!stream) { - throw DeadlyImportError("JT: Cannot open file " + file ); - } - - std::vector contents; - contents.resize(stream->FileSize() + 1); - stream->Read(&*contents.begin(), 1, contents.size() - 1); - contents[contents.size() - 1] = 0; - BinReader reader(contents); - ReadHeader(reader); -} - -void JTImporter::ReadHeader(BinReader& reader) { - reader.readChars(VersionLen, mJTModel.mJTHeader.mVersion); - reader.readUChar(mJTModel.mJTHeader.mByteOrder); - reader.readI32(mJTModel.mJTHeader.mReserved); - - int toc_offset; - reader.readI32(toc_offset); -} - -void JTImporter::ReadTokenSegment(BinReader& reader) { - int toc_count; - reader.readI32(toc_count); - if (0 == toc_count) { - return; - } - - for (int i = 0; i < toc_count; ++i) { - ReadTokenEntry(reader); - } -} - -void JTImporter::ReadTokenEntry(BinReader& reader) { - TokenEntry* entry = new TokenEntry; - reader.readGUID(entry->guid); - reader.readI32(entry->offset); - reader.readI32(entry->length); - reader.readU32(entry->attributes); - - mJTModel.mTokenEntryMap[entry->guid] = entry; -} - -TokenEntry* JTImporter::FindTokenEntryByGuid(GUID& guid) { - if (mJTModel.mTokenEntryMap.empty()) { - return nullptr; - } - - JTModel::TokenEntryMap::const_iterator it(mJTModel.mTokenEntryMap.find(guid)); - if (it == mJTModel.mTokenEntryMap.end()) { - return nullptr; - } - - return it->second; -} -/* -static bool SupportsZlibCompression(SegmentType type) { - switch (type) { - case LogicalSceneGraph: - case JT_B_Rep: - case PMI_Data: - case MetaData: - return true; - case Shape: - case Shape_LOD0: - case Shape_LOD1: - case Shape_LOD2: - case Shape_LOD3: - case Shape_LOD4: - case Shape_LOD5: - case Shape_LOD6: - case Shape_LOD7: - case Shape_LOD8: - case Shape_LOD9: - return false; - case XT_B_Rep: - case Wireframe_Rep: - case ULP: - case LWPA: - return true; - default: - ai_assert_entry(); - break; - } - - return false; -}*/ - -void JTImporter::ReadDataSegment(BinReader& reader) { - mCurrentDataSegment = new DataSegment; - mDataSegments.push_back(mCurrentDataSegment); - ReadDataSegmentHeader(reader); - -} - -void JTImporter::ReadDataSegmentHeader(BinReader& reader) { - if (nullptr == mCurrentDataSegment) { - return; - } - - reader.readGUID(mCurrentDataSegment->m_DataSegmentHeader.guid); - reader.readI32(mCurrentDataSegment->m_DataSegmentHeader.type); - reader.readI32(mCurrentDataSegment->m_DataSegmentHeader.length); - -} - -void JTImporter::ReadLogicalElementHeaderZLib(BinReader& reader) { - i32 compressionFlag; - reader.readI32(compressionFlag); - i32 compressedLen; - reader.readI32(compressedLen); - u8 compressionAlgo; - reader.readUChar(compressionAlgo); -} - -void JTImporter::ReadSegmentType(BinReader& reader) { - SegmentType type = static_cast(mCurrentDataSegment->m_DataSegmentHeader.type); - switch (type) { - case LogicalSceneGraph: - ReadLSG(reader); - break; - case JT_B_Rep: - break; - case PMI_Data: - break; - case MetaData: - break; - case Shape: - break; - case Shape_LOD0: - break; - case Shape_LOD1: - break; - case Shape_LOD2: - break; - case Shape_LOD3: - break; - case Shape_LOD4: - break; - case Shape_LOD5: - break; - case Shape_LOD6: - break; - case Shape_LOD7: - break; - case Shape_LOD8: - break; - case Shape_LOD9: - break; - case XT_B_Rep: - break; - case Wireframe_Rep: - break; - case ULP: - break; - case LWPA: - break; - default: - break; - } -} - -void JTImporter::ReadLSG(BinReader& reader) { - ReadLogicalElementHeaderZLib(reader); -} - -} // end namespace Assimp - -#endif // ASSIMP_BUILD_NO_JT_IMPORTER From 3b3a965d2b1b4aa404d8fb5956a919ee9b85e851 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 11 Jul 2019 22:31:40 +0200 Subject: [PATCH 11/13] Delete JTImporter.h --- code/JTImporter.h | 210 ---------------------------------------------- 1 file changed, 210 deletions(-) delete mode 100644 code/JTImporter.h diff --git a/code/JTImporter.h b/code/JTImporter.h deleted file mode 100644 index 9466900bb..000000000 --- a/code/JTImporter.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once -#ifndef AI_JTIMPORTER_H_INC -#define AI_JTIMPORTER_H_INC - -#include - -#include -#include - -#ifndef ASSIMP_BUILD_NO_JT_IMPORTER - -namespace Assimp { - -using i32 = int; -using u32 = unsigned int; -using i16 = short; -using u16 = unsigned short; -using c8 = char; -using u8 = unsigned char; -using f32 = float; -using f64 = double; - -static const size_t VersionLen = 80u; - -struct JTHeader { - char mVersion[VersionLen]; - unsigned char mByteOrder; - int mReserved; - int toc_offset; -}; - -template -struct TJTCoord { - T x, y, z; -}; - -using F32Coord = TJTCoord; -using F64Coord = TJTCoord; - -struct BBox32 { - F32Coord min; - F32Coord max; -}; - -template -struct THJTCoord { - T x, y, z, w; -}; - -using F32HCoord = THJTCoord; -using F64HCoord = THJTCoord; - -struct MbString { - int count; - char *data; -}; - -struct String { - int count; - char* data; -}; - -template -struct TVec { - int count; - T *data; -}; - -using VecF32 = TVec; -using VecF64 = TVec; -using VecI32 = TVec; - -struct RGB { - f32 data[3]; -}; - -struct RGBA { - f32 data[4]; - }; - -struct GUID { - unsigned int id1; - u16 id2[2]; - u8 id3[8]; -}; - -struct GUIDComparer { - bool operator() (const GUID& lhs, const GUID& rhs) const { - if (lhs.id1 < rhs.id1) { - return true; - } - - for (size_t i = 0; i < 2; ++i) { - if (lhs.id2[i] < rhs.id2[i]) { - return true; - } - } - - for (size_t i = 0; i < 8; ++i) { - if (lhs.id3[i] < rhs.id3[i]) { - return true; - } - } - - return false; - } -}; - -struct TokenEntry { - GUID guid; - i32 offset; - i32 length; - u32 attributes; -}; - -struct DataSegmentHeader { - GUID guid; - i32 type; - i32 length; -}; - -struct DataSegment { - DataSegmentHeader m_DataSegmentHeader; -}; - -struct ElementHeader { - GUID obj_type_id; - u8 obj_base_type; - i32 obj_id; -}; - -struct JTModel { - JTHeader mJTHeader; - GUID mGUID; - using TokenEntryMap = std::map; - TokenEntryMap mTokenEntryMap; -}; - -class BinReader { - std::vector& mData; - size_t mOffset; - -public: - BinReader(std::vector& data) - : mData(data) - , mOffset(0u){ - // empty - } - - void readChars(size_t numChars, char* buffer) { - ::memcpy(buffer, &mData[mOffset], numChars); - mOffset += numChars; - } - - void readUChar(unsigned char &c) { - ::memcpy(&c, &mData[mOffset], sizeof(unsigned char)); - mOffset += sizeof(unsigned char); - } - - void readI32(int32_t& v) { - ::memcpy(&v, &mData[mOffset], sizeof(int32_t)); - mOffset += sizeof(int32_t); - } - - void readU32(uint32_t& v) { - ::memcpy(&v, &mData[mOffset], sizeof(uint32_t)); - mOffset += sizeof(uint32_t); - } - - void readGUID(GUID &guid) { - ::memcpy(&guid.id1, &mData[mOffset], sizeof(i32)); - mOffset += sizeof(i32); - ::memcpy(guid.id2, &mData[mOffset], sizeof(u16) * 2); - mOffset += sizeof(u16)*2; - ::memcpy(guid.id3, &mData[mOffset], sizeof(u8) * 8); - mOffset += sizeof(u8) * 8; - } -}; - -class JTImporter : public BaseImporter { -public: - JTImporter(); - ~JTImporter(); - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const override; - const aiImporterDesc* GetInfo() const override; - -protected: - void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) override; - void ReadHeader(BinReader &reader); - void ReadTokenSegment(BinReader& reader); - void ReadTokenEntry(BinReader& reader); - TokenEntry* FindTokenEntryByGuid(GUID& id); - void ReadDataSegment(BinReader& reader); - void ReadDataSegmentHeader(BinReader& reader); - void ReadLogicalElementHeaderZLib(BinReader& reader); - void ReadSegmentType(BinReader& reader); - void ReadLSG(BinReader& reader); - -private: - JTModel mJTModel; - std::vector mDataSegments; - DataSegment *mCurrentDataSegment; - -}; - -} // end namespace Assimp - -#endif // ASSIMP_BUILD_NO_JT_IMPORTER - -#endif // AI_JTIMPORTER_H_INC From 53c0f913059160b270d5f6ce6ba1956acf366c30 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Fri, 12 Jul 2019 16:08:51 +0200 Subject: [PATCH 12/13] Add unittest for json. --- code/Assjson/json_exporter.cpp | 1501 ++++++++--------- code/Assjson/mesh_splitter.cpp | 19 +- code/COB/COBLoader.cpp | 11 +- code/Common/ImporterRegistry.cpp | 7 - code/FBX/FBXDocument.h | 2 +- include/assimp/camera.h | 4 +- test/CMakeLists.txt | 12 +- test/models/PLY/cube_test.ply | 6 +- test/unit/AbstractImportExportBase.h | 24 +- .../ImportExport/utAssjsonImportExport.cpp | 69 + test/unit/utAMFImportExport.cpp | 2 - test/unit/utASEImportExport.cpp | 2 - test/unit/utAssbinImportExport.cpp | 8 +- test/unit/utB3DImportExport.cpp | 2 - test/unit/utFindDegenerates.cpp | 71 +- test/unit/utFindInvalidData.cpp | 117 +- test/unit/utGenBoundingBoxesProcess.cpp | 10 +- test/unit/utJoinVertices.cpp | 64 +- test/unit/utLimitBoneWeights.cpp | 74 +- test/unit/utPretransformVertices.cpp | 73 +- test/unit/utScenePreprocessor.cpp | 67 +- test/unit/utSortByPType.cpp | 108 +- 22 files changed, 1137 insertions(+), 1116 deletions(-) create mode 100644 test/unit/ImportExport/utAssjsonImportExport.cpp diff --git a/code/Assjson/json_exporter.cpp b/code/Assjson/json_exporter.cpp index 848a08b4d..cd542b29e 100644 --- a/code/Assjson/json_exporter.cpp +++ b/code/Assjson/json_exporter.cpp @@ -13,7 +13,6 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information #include #include #include - #include #include @@ -28,832 +27,792 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information #include "mesh_splitter.h" - extern "C" { -#include "cencode.h" + #include "cencode.h" } +namespace Assimp { -namespace { -void Assimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*); -} +void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*); -Assimp::Exporter::ExportFormatEntry Assimp2Json_desc = Assimp::Exporter::ExportFormatEntry( - "assimp.json", - "Plain JSON representation of the Assimp scene data structure", - "assimp.json", - Assimp2Json, - 0u); - -namespace { +Exporter::ExportFormatEntry Assimp2Json_desc = Assimp::Exporter::ExportFormatEntry( + "json", + "Plain JSON representation of the Assimp scene data structure", + "json", + &ExportAssimp2Json, + 0u +); - // small utility class to simplify serializing the aiScene to Json -class JSONWriter -{ - +// small utility class to simplify serializing the aiScene to Json +class JSONWriter { public: + enum { + Flag_DoNotIndent = 0x1, + Flag_WriteSpecialFloats = 0x2, + }; - enum { - Flag_DoNotIndent = 0x1, - Flag_WriteSpecialFloats = 0x2, - }; + JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u) + : out(out) + , first() + , flags(flags) { + // make sure that all formatting happens using the standard, C locale and not the user's current locale + buff.imbue(std::locale("C")); + } -public: + ~JSONWriter() { + Flush(); + } - JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u) - : out(out) - , first() - , flags(flags) - { - // make sure that all formatting happens using the standard, C locale and not the user's current locale - buff.imbue( std::locale("C") ); - } + void Flush() { + const std::string s = buff.str(); + out.Write(s.c_str(), s.length(), 1); + buff.clear(); + } - ~JSONWriter() - { - Flush(); - } + void PushIndent() { + indent += '\t'; + } -public: + void PopIndent() { + indent.erase(indent.end() - 1); + } - void Flush() { - const std::string s = buff.str(); - out.Write(s.c_str(),s.length(),1); - buff.clear(); - } + void Key(const std::string& name) { + AddIndentation(); + Delimit(); + buff << '\"' + name + "\": "; + } - void PushIndent() { - indent += '\t'; - } + template + void Element(const Literal& name) { + AddIndentation(); + Delimit(); - void PopIndent() { - indent.erase(indent.end()-1); - } + LiteralToString(buff, name) << '\n'; + } - void Key(const std::string& name) { - AddIndentation(); - Delimit(); - buff << '\"'+name+"\": "; - } + template + void SimpleValue(const Literal& s) { + LiteralToString(buff, s) << '\n'; + } - template - void Element(const Literal& name) { - AddIndentation(); - Delimit(); + void SimpleValue(const void* buffer, size_t len) { + base64_encodestate s; + base64_init_encodestate(&s); - LiteralToString(buff, name) << '\n'; - } + char* const out = new char[std::max(len * 2, static_cast(16u))]; + const int n = base64_encode_block(reinterpret_cast(buffer), static_cast(len), out, &s); + out[n + base64_encode_blockend(out + n, &s)] = '\0'; - template - void SimpleValue(const Literal& s) { - LiteralToString(buff, s) << '\n'; - } + // base64 encoding may add newlines, but JSON strings may not contain 'real' newlines + // (only escaped ones). Remove any newlines in out. + for (char* cur = out; *cur; ++cur) { + if (*cur == '\n') { + *cur = ' '; + } + } + buff << '\"' << out << "\"\n"; + delete[] out; + } - void SimpleValue(const void* buffer, size_t len) { - base64_encodestate s; - base64_init_encodestate(&s); + void StartObj(bool is_element = false) { + // if this appears as a plain array element, we need to insert a delimiter and we should also indent it + if (is_element) { + AddIndentation(); + if (!first) { + buff << ','; + } + } + first = true; + buff << "{\n"; + PushIndent(); + } - char* const out = new char[std::max(len*2, static_cast(16u))]; - const int n = base64_encode_block(reinterpret_cast( buffer ), static_cast( len ),out,&s); - out[n+base64_encode_blockend(out + n,&s)] = '\0'; + void EndObj() { + PopIndent(); + AddIndentation(); + first = false; + buff << "}\n"; + } - // base64 encoding may add newlines, but JSON strings may not contain 'real' newlines - // (only escaped ones). Remove any newlines in out. - for(char* cur = out; *cur; ++cur) { - if(*cur == '\n') { - *cur = ' '; - } - } + void StartArray(bool is_element = false) { + // if this appears as a plain array element, we need to insert a delimiter and we should also indent it + if (is_element) { + AddIndentation(); + if (!first) { + buff << ','; + } + } + first = true; + buff << "[\n"; + PushIndent(); + } + void EndArray() { + PopIndent(); + AddIndentation(); + buff << "]\n"; + first = false; + } - buff << '\"' << out << "\"\n"; - delete[] out; - } + void AddIndentation() { + if (!(flags & Flag_DoNotIndent)) { + buff << indent; + } + } - void StartObj(bool is_element = false) { - // if this appears as a plain array element, we need to insert a delimiter and we should also indent it - if(is_element) { - AddIndentation(); - if(!first) { - buff << ','; - } - } - first = true; - buff << "{\n"; - PushIndent(); - } - - void EndObj() { - PopIndent(); - AddIndentation(); - first = false; - buff << "}\n"; - } - - void StartArray(bool is_element = false) { - // if this appears as a plain array element, we need to insert a delimiter and we should also indent it - if(is_element) { - AddIndentation(); - if(!first) { - buff << ','; - } - } - first = true; - buff << "[\n"; - PushIndent(); - } - - void EndArray() { - PopIndent(); - AddIndentation(); - buff << "]\n"; - first = false; - } - - void AddIndentation() { - if(!(flags & Flag_DoNotIndent)) { - buff << indent; - } - } - - void Delimit() { - if(!first) { - buff << ','; - } - else { - buff << ' '; - first = false; - } - } + void Delimit() { + if (!first) { + buff << ','; + } + else { + buff << ' '; + first = false; + } + } private: + template + std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) { + stream << s; + return stream; + } - template - std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) { - stream << s; - return stream; - } + std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) { + std::string t; - std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) { - std::string t; + // escape backslashes and single quotes, both would render the JSON invalid if left as is + t.reserve(s.length); + for (size_t i = 0; i < s.length; ++i) { - // escape backslashes and single quotes, both would render the JSON invalid if left as is - t.reserve(s.length); - for(size_t i = 0; i < s.length; ++i) { - - if (s.data[i] == '\\' || s.data[i] == '\'' || s.data[i] == '\"') { - t.push_back('\\'); - } + if (s.data[i] == '\\' || s.data[i] == '\'' || s.data[i] == '\"') { + t.push_back('\\'); + } - t.push_back(s.data[i]); - } - stream << "\""; - stream << t; - stream << "\""; - return stream; - } + t.push_back(s.data[i]); + } + stream << "\""; + stream << t; + stream << "\""; + return stream; + } - std::stringstream& LiteralToString(std::stringstream& stream, float f) { - if (!std::numeric_limits::is_iec559) { - // on a non IEEE-754 platform, we make no assumptions about the representation or existence - // of special floating-point numbers. - stream << f; - return stream; - } + std::stringstream& LiteralToString(std::stringstream& stream, float f) { + if (!std::numeric_limits::is_iec559) { + // on a non IEEE-754 platform, we make no assumptions about the representation or existence + // of special floating-point numbers. + stream << f; + return stream; + } - // JSON does not support writing Inf/Nan - // [RFC 4672: "Numeric values that cannot be represented as sequences of digits - // (such as Infinity and NaN) are not permitted."] - // Nevertheless, many parsers will accept the special keywords Infinity, -Infinity and NaN - if (std::numeric_limits::infinity() == fabs(f)) { - if (flags & Flag_WriteSpecialFloats) { - stream << (f < 0 ? "\"-" : "\"") + std::string( "Infinity\"" ); - return stream; - } - // we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr - // std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl; - stream << "0.0"; - return stream; - } - // f!=f is the most reliable test for NaNs that I know of - else if (f != f) { - if (flags & Flag_WriteSpecialFloats) { - stream << "\"NaN\""; - return stream; - } - // we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr - // std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl; - stream << "0.0"; - return stream; - } + // JSON does not support writing Inf/Nan + // [RFC 4672: "Numeric values that cannot be represented as sequences of digits + // (such as Infinity and NaN) are not permitted."] + // Nevertheless, many parsers will accept the special keywords Infinity, -Infinity and NaN + if (std::numeric_limits::infinity() == fabs(f)) { + if (flags & Flag_WriteSpecialFloats) { + stream << (f < 0 ? "\"-" : "\"") + std::string("Infinity\""); + return stream; + } + // we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr + // std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl; + stream << "0.0"; + return stream; + } + // f!=f is the most reliable test for NaNs that I know of + else if (f != f) { + if (flags & Flag_WriteSpecialFloats) { + stream << "\"NaN\""; + return stream; + } + // we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr + // std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl; + stream << "0.0"; + return stream; + } - stream << f; - return stream; - } + stream << f; + return stream; + } -private: - Assimp::IOStream& out; - std::string indent, newline; - std::stringstream buff; - bool first; +private: + Assimp::IOStream& out; + std::string indent, newline; + std::stringstream buff; + bool first; - unsigned int flags; + unsigned int flags; }; - -void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) -{ - out.StartArray(is_elem); - out.Element(ai.x); - out.Element(ai.y); - out.Element(ai.z); - out.EndArray(); +void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) { + out.StartArray(is_elem); + out.Element(ai.x); + out.Element(ai.y); + out.Element(ai.z); + out.EndArray(); } -void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) -{ - out.StartArray(is_elem); - out.Element(ai.w); - out.Element(ai.x); - out.Element(ai.y); - out.Element(ai.z); - out.EndArray(); +void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) { + out.StartArray(is_elem); + out.Element(ai.w); + out.Element(ai.x); + out.Element(ai.y); + out.Element(ai.z); + out.EndArray(); } -void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) -{ - out.StartArray(is_elem); - out.Element(ai.r); - out.Element(ai.g); - out.Element(ai.b); - out.EndArray(); +void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) { + out.StartArray(is_elem); + out.Element(ai.r); + out.Element(ai.g); + out.Element(ai.b); + out.EndArray(); } -void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) -{ - out.StartArray(is_elem); - for(unsigned int x = 0; x < 4; ++x) { - for(unsigned int y = 0; y < 4; ++y) { - out.Element(ai[x][y]); - } - } - out.EndArray(); +void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) { + out.StartArray(is_elem); + for (unsigned int x = 0; x < 4; ++x) { + for (unsigned int y = 0; y < 4; ++y) { + out.Element(ai[x][y]); + } + } + out.EndArray(); } -void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) -{ - out.StartObj(is_elem); +void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) { + out.StartObj(is_elem); - out.Key("name"); - out.SimpleValue(ai.mName); + out.Key("name"); + out.SimpleValue(ai.mName); - out.Key("offsetmatrix"); - Write(out,ai.mOffsetMatrix,false); + out.Key("offsetmatrix"); + Write(out, ai.mOffsetMatrix, false); - out.Key("weights"); - out.StartArray(); - for(unsigned int i = 0; i < ai.mNumWeights; ++i) { - out.StartArray(true); - out.Element(ai.mWeights[i].mVertexId); - out.Element(ai.mWeights[i].mWeight); - out.EndArray(); - } - out.EndArray(); - out.EndObj(); + out.Key("weights"); + out.StartArray(); + for (unsigned int i = 0; i < ai.mNumWeights; ++i) { + out.StartArray(true); + out.Element(ai.mWeights[i].mVertexId); + out.Element(ai.mWeights[i].mWeight); + out.EndArray(); + } + out.EndArray(); + out.EndObj(); +} + +void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) { + out.StartArray(is_elem); + for (unsigned int i = 0; i < ai.mNumIndices; ++i) { + out.Element(ai.mIndices[i]); + } + out.EndArray(); +} + +void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("name"); + out.SimpleValue(ai.mName); + + out.Key("materialindex"); + out.SimpleValue(ai.mMaterialIndex); + + out.Key("primitivetypes"); + out.SimpleValue(ai.mPrimitiveTypes); + + out.Key("vertices"); + out.StartArray(); + for (unsigned int i = 0; i < ai.mNumVertices; ++i) { + out.Element(ai.mVertices[i].x); + out.Element(ai.mVertices[i].y); + out.Element(ai.mVertices[i].z); + } + out.EndArray(); + + if (ai.HasNormals()) { + out.Key("normals"); + out.StartArray(); + for (unsigned int i = 0; i < ai.mNumVertices; ++i) { + out.Element(ai.mNormals[i].x); + out.Element(ai.mNormals[i].y); + out.Element(ai.mNormals[i].z); + } + out.EndArray(); + } + + if (ai.HasTangentsAndBitangents()) { + out.Key("tangents"); + out.StartArray(); + for (unsigned int i = 0; i < ai.mNumVertices; ++i) { + out.Element(ai.mTangents[i].x); + out.Element(ai.mTangents[i].y); + out.Element(ai.mTangents[i].z); + } + out.EndArray(); + + out.Key("bitangents"); + out.StartArray(); + for (unsigned int i = 0; i < ai.mNumVertices; ++i) { + out.Element(ai.mBitangents[i].x); + out.Element(ai.mBitangents[i].y); + out.Element(ai.mBitangents[i].z); + } + out.EndArray(); + } + + if (ai.GetNumUVChannels()) { + out.Key("numuvcomponents"); + out.StartArray(); + for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) { + out.Element(ai.mNumUVComponents[n]); + } + out.EndArray(); + + out.Key("texturecoords"); + out.StartArray(); + for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) { + const unsigned int numc = ai.mNumUVComponents[n] ? ai.mNumUVComponents[n] : 2; + + out.StartArray(true); + for (unsigned int i = 0; i < ai.mNumVertices; ++i) { + for (unsigned int c = 0; c < numc; ++c) { + out.Element(ai.mTextureCoords[n][i][c]); + } + } + out.EndArray(); + } + out.EndArray(); + } + + if (ai.GetNumColorChannels()) { + out.Key("colors"); + out.StartArray(); + for (unsigned int n = 0; n < ai.GetNumColorChannels(); ++n) { + out.StartArray(true); + for (unsigned int i = 0; i < ai.mNumVertices; ++i) { + out.Element(ai.mColors[n][i].r); + out.Element(ai.mColors[n][i].g); + out.Element(ai.mColors[n][i].b); + out.Element(ai.mColors[n][i].a); + } + out.EndArray(); + } + out.EndArray(); + } + + if (ai.mNumBones) { + out.Key("bones"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumBones; ++n) { + Write(out, *ai.mBones[n]); + } + out.EndArray(); + } + + out.Key("faces"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumFaces; ++n) { + Write(out, ai.mFaces[n]); + } + out.EndArray(); + + out.EndObj(); +} + +void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("name"); + out.SimpleValue(ai.mName); + + out.Key("transformation"); + Write(out, ai.mTransformation, false); + + if (ai.mNumMeshes) { + out.Key("meshes"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumMeshes; ++n) { + out.Element(ai.mMeshes[n]); + } + out.EndArray(); + } + + if (ai.mNumChildren) { + out.Key("children"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumChildren; ++n) { + Write(out, *ai.mChildren[n]); + } + out.EndArray(); + } + + out.EndObj(); +} + +void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("properties"); + out.StartArray(); + for (unsigned int i = 0; i < ai.mNumProperties; ++i) { + const aiMaterialProperty* const prop = ai.mProperties[i]; + out.StartObj(true); + out.Key("key"); + out.SimpleValue(prop->mKey); + out.Key("semantic"); + out.SimpleValue(prop->mSemantic); + out.Key("index"); + out.SimpleValue(prop->mIndex); + + out.Key("type"); + out.SimpleValue(prop->mType); + + out.Key("value"); + switch (prop->mType) { + case aiPTI_Float: + if (prop->mDataLength / sizeof(float) > 1) { + out.StartArray(); + for (unsigned int i = 0; i < prop->mDataLength / sizeof(float); ++i) { + out.Element(reinterpret_cast(prop->mData)[i]); + } + out.EndArray(); + } + else { + out.SimpleValue(*reinterpret_cast(prop->mData)); + } + break; + + case aiPTI_Integer: + if (prop->mDataLength / sizeof(int) > 1) { + out.StartArray(); + for (unsigned int i = 0; i < prop->mDataLength / sizeof(int); ++i) { + out.Element(reinterpret_cast(prop->mData)[i]); + } + out.EndArray(); + } else { + out.SimpleValue(*reinterpret_cast(prop->mData)); + } + break; + + case aiPTI_String: + { + aiString s; + aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s); + out.SimpleValue(s); + } + break; + case aiPTI_Buffer: + { + // binary data is written as series of hex-encoded octets + out.SimpleValue(prop->mData, prop->mDataLength); + } + break; + default: + assert(false); + } + + out.EndObj(); + } + + out.EndArray(); + out.EndObj(); +} + +void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("width"); + out.SimpleValue(ai.mWidth); + + out.Key("height"); + out.SimpleValue(ai.mHeight); + + out.Key("formathint"); + out.SimpleValue(aiString(ai.achFormatHint)); + + out.Key("data"); + if (!ai.mHeight) { + out.SimpleValue(ai.pcData, ai.mWidth); + } + else { + out.StartArray(); + for (unsigned int y = 0; y < ai.mHeight; ++y) { + out.StartArray(true); + for (unsigned int x = 0; x < ai.mWidth; ++x) { + const aiTexel& tx = ai.pcData[y*ai.mWidth + x]; + out.StartArray(true); + out.Element(static_cast(tx.r)); + out.Element(static_cast(tx.g)); + out.Element(static_cast(tx.b)); + out.Element(static_cast(tx.a)); + out.EndArray(); + } + out.EndArray(); + } + out.EndArray(); + } + + out.EndObj(); +} + +void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("name"); + out.SimpleValue(ai.mName); + + out.Key("type"); + out.SimpleValue(ai.mType); + + if (ai.mType == aiLightSource_SPOT || ai.mType == aiLightSource_UNDEFINED) { + out.Key("angleinnercone"); + out.SimpleValue(ai.mAngleInnerCone); + + out.Key("angleoutercone"); + out.SimpleValue(ai.mAngleOuterCone); + } + + out.Key("attenuationconstant"); + out.SimpleValue(ai.mAttenuationConstant); + + out.Key("attenuationlinear"); + out.SimpleValue(ai.mAttenuationLinear); + + out.Key("attenuationquadratic"); + out.SimpleValue(ai.mAttenuationQuadratic); + + out.Key("diffusecolor"); + Write(out, ai.mColorDiffuse, false); + + out.Key("specularcolor"); + Write(out, ai.mColorSpecular, false); + + out.Key("ambientcolor"); + Write(out, ai.mColorAmbient, false); + + if (ai.mType != aiLightSource_POINT) { + out.Key("direction"); + Write(out, ai.mDirection, false); + + } + + if (ai.mType != aiLightSource_DIRECTIONAL) { + out.Key("position"); + Write(out, ai.mPosition, false); + } + + out.EndObj(); +} + +void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("name"); + out.SimpleValue(ai.mNodeName); + + out.Key("prestate"); + out.SimpleValue(ai.mPreState); + + out.Key("poststate"); + out.SimpleValue(ai.mPostState); + + if (ai.mNumPositionKeys) { + out.Key("positionkeys"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) { + const aiVectorKey& pos = ai.mPositionKeys[n]; + out.StartArray(true); + out.Element(pos.mTime); + Write(out, pos.mValue); + out.EndArray(); + } + out.EndArray(); + } + + if (ai.mNumRotationKeys) { + out.Key("rotationkeys"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) { + const aiQuatKey& rot = ai.mRotationKeys[n]; + out.StartArray(true); + out.Element(rot.mTime); + Write(out, rot.mValue); + out.EndArray(); + } + out.EndArray(); + } + + if (ai.mNumScalingKeys) { + out.Key("scalingkeys"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) { + const aiVectorKey& scl = ai.mScalingKeys[n]; + out.StartArray(true); + out.Element(scl.mTime); + Write(out, scl.mValue); + out.EndArray(); + } + out.EndArray(); + } + out.EndObj(); +} + +void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("name"); + out.SimpleValue(ai.mName); + + out.Key("tickspersecond"); + out.SimpleValue(ai.mTicksPerSecond); + + out.Key("duration"); + out.SimpleValue(ai.mDuration); + + out.Key("channels"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumChannels; ++n) { + Write(out, *ai.mChannels[n]); + } + out.EndArray(); + out.EndObj(); +} + +void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) { + out.StartObj(is_elem); + + out.Key("name"); + out.SimpleValue(ai.mName); + + out.Key("aspect"); + out.SimpleValue(ai.mAspect); + + out.Key("clipplanefar"); + out.SimpleValue(ai.mClipPlaneFar); + + out.Key("clipplanenear"); + out.SimpleValue(ai.mClipPlaneNear); + + out.Key("horizontalfov"); + out.SimpleValue(ai.mHorizontalFOV); + + out.Key("up"); + Write(out, ai.mUp, false); + + out.Key("lookat"); + Write(out, ai.mLookAt, false); + + out.EndObj(); +} + +void WriteFormatInfo(JSONWriter& out) { + out.StartObj(); + out.Key("format"); + out.SimpleValue("\"assimp2json\""); + out.Key("version"); + out.SimpleValue(CURRENT_FORMAT_VERSION); + out.EndObj(); +} + +void Write(JSONWriter& out, const aiScene& ai) { + out.StartObj(); + + out.Key("__metadata__"); + WriteFormatInfo(out); + + out.Key("rootnode"); + Write(out, *ai.mRootNode, false); + + out.Key("flags"); + out.SimpleValue(ai.mFlags); + + if (ai.HasMeshes()) { + out.Key("meshes"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumMeshes; ++n) { + Write(out, *ai.mMeshes[n]); + } + out.EndArray(); + } + + if (ai.HasMaterials()) { + out.Key("materials"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumMaterials; ++n) { + Write(out, *ai.mMaterials[n]); + } + out.EndArray(); + } + + if (ai.HasAnimations()) { + out.Key("animations"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumAnimations; ++n) { + Write(out, *ai.mAnimations[n]); + } + out.EndArray(); + } + + if (ai.HasLights()) { + out.Key("lights"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumLights; ++n) { + Write(out, *ai.mLights[n]); + } + out.EndArray(); + } + + if (ai.HasCameras()) { + out.Key("cameras"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumCameras; ++n) { + Write(out, *ai.mCameras[n]); + } + out.EndArray(); + } + + if (ai.HasTextures()) { + out.Key("textures"); + out.StartArray(); + for (unsigned int n = 0; n < ai.mNumTextures; ++n) { + Write(out, *ai.mTextures[n]); + } + out.EndArray(); + } + out.EndObj(); } -void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) -{ - out.StartArray(is_elem); - for(unsigned int i = 0; i < ai.mNumIndices; ++i) { - out.Element(ai.mIndices[i]); - } - out.EndArray(); +void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) { + std::unique_ptr str(io->Open(file, "wt")); + if (!str) { + //throw Assimp::DeadlyExportError("could not open output file"); + } + + // get a copy of the scene so we can modify it + aiScene* scenecopy_tmp; + aiCopyScene(scene, &scenecopy_tmp); + + try { + // split meshes so they fit into a 16 bit index buffer + MeshSplitter splitter; + splitter.SetLimit(1 << 16); + splitter.Execute(scenecopy_tmp); + + // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters + JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats); + Write(s, *scenecopy_tmp); + + } + catch (...) { + aiFreeScene(scenecopy_tmp); + throw; + } + aiFreeScene(scenecopy_tmp); } - -void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("name"); - out.SimpleValue(ai.mName); - - out.Key("materialindex"); - out.SimpleValue(ai.mMaterialIndex); - - out.Key("primitivetypes"); - out.SimpleValue(ai.mPrimitiveTypes); - - out.Key("vertices"); - out.StartArray(); - for(unsigned int i = 0; i < ai.mNumVertices; ++i) { - out.Element(ai.mVertices[i].x); - out.Element(ai.mVertices[i].y); - out.Element(ai.mVertices[i].z); - } - out.EndArray(); - - if(ai.HasNormals()) { - out.Key("normals"); - out.StartArray(); - for(unsigned int i = 0; i < ai.mNumVertices; ++i) { - out.Element(ai.mNormals[i].x); - out.Element(ai.mNormals[i].y); - out.Element(ai.mNormals[i].z); - } - out.EndArray(); - } - - if(ai.HasTangentsAndBitangents()) { - out.Key("tangents"); - out.StartArray(); - for(unsigned int i = 0; i < ai.mNumVertices; ++i) { - out.Element(ai.mTangents[i].x); - out.Element(ai.mTangents[i].y); - out.Element(ai.mTangents[i].z); - } - out.EndArray(); - - out.Key("bitangents"); - out.StartArray(); - for(unsigned int i = 0; i < ai.mNumVertices; ++i) { - out.Element(ai.mBitangents[i].x); - out.Element(ai.mBitangents[i].y); - out.Element(ai.mBitangents[i].z); - } - out.EndArray(); - } - - if(ai.GetNumUVChannels()) { - out.Key("numuvcomponents"); - out.StartArray(); - for(unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) { - out.Element(ai.mNumUVComponents[n]); - } - out.EndArray(); - - out.Key("texturecoords"); - out.StartArray(); - for(unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) { - - const unsigned int numc = ai.mNumUVComponents[n] ? ai.mNumUVComponents[n] : 2; - - out.StartArray(true); - for(unsigned int i = 0; i < ai.mNumVertices; ++i) { - for(unsigned int c = 0; c < numc; ++c) { - out.Element(ai.mTextureCoords[n][i][c]); - } - } - out.EndArray(); - } - out.EndArray(); - } - - if(ai.GetNumColorChannels()) { - out.Key("colors"); - out.StartArray(); - for(unsigned int n = 0; n < ai.GetNumColorChannels(); ++n) { - - out.StartArray(true); - for(unsigned int i = 0; i < ai.mNumVertices; ++i) { - out.Element(ai.mColors[n][i].r); - out.Element(ai.mColors[n][i].g); - out.Element(ai.mColors[n][i].b); - out.Element(ai.mColors[n][i].a); - } - out.EndArray(); - } - out.EndArray(); - } - - if(ai.mNumBones) { - out.Key("bones"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumBones; ++n) { - Write(out, *ai.mBones[n]); - } - out.EndArray(); - } - - - out.Key("faces"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumFaces; ++n) { - Write(out, ai.mFaces[n]); - } - out.EndArray(); - - out.EndObj(); } - -void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("name"); - out.SimpleValue(ai.mName); - - out.Key("transformation"); - Write(out,ai.mTransformation,false); - - if(ai.mNumMeshes) { - out.Key("meshes"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumMeshes; ++n) { - out.Element(ai.mMeshes[n]); - } - out.EndArray(); - } - - if(ai.mNumChildren) { - out.Key("children"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumChildren; ++n) { - Write(out,*ai.mChildren[n]); - } - out.EndArray(); - } - - out.EndObj(); -} - -void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("properties"); - out.StartArray(); - for(unsigned int i = 0; i < ai.mNumProperties; ++i) { - const aiMaterialProperty* const prop = ai.mProperties[i]; - out.StartObj(true); - out.Key("key"); - out.SimpleValue(prop->mKey); - out.Key("semantic"); - out.SimpleValue(prop->mSemantic); - out.Key("index"); - out.SimpleValue(prop->mIndex); - - out.Key("type"); - out.SimpleValue(prop->mType); - - out.Key("value"); - switch(prop->mType) - { - case aiPTI_Float: - if(prop->mDataLength/sizeof(float) > 1) { - out.StartArray(); - for(unsigned int i = 0; i < prop->mDataLength/sizeof(float); ++i) { - out.Element(reinterpret_cast(prop->mData)[i]); - } - out.EndArray(); - } - else { - out.SimpleValue(*reinterpret_cast(prop->mData)); - } - break; - - case aiPTI_Integer: - if(prop->mDataLength/sizeof(int) > 1) { - out.StartArray(); - for(unsigned int i = 0; i < prop->mDataLength/sizeof(int); ++i) { - out.Element(reinterpret_cast(prop->mData)[i]); - } - out.EndArray(); - } - else { - out.SimpleValue(*reinterpret_cast(prop->mData)); - } - break; - case aiPTI_String: - { - aiString s; - aiGetMaterialString(&ai,prop->mKey.data,prop->mSemantic,prop->mIndex,&s); - out.SimpleValue(s); - } - break; - case aiPTI_Buffer: - { - // binary data is written as series of hex-encoded octets - out.SimpleValue(prop->mData,prop->mDataLength); - } - break; - default: - assert(false); - } - - out.EndObj(); - } - - out.EndArray(); - out.EndObj(); -} - -void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("width"); - out.SimpleValue(ai.mWidth); - - out.Key("height"); - out.SimpleValue(ai.mHeight); - - out.Key("formathint"); - out.SimpleValue(aiString(ai.achFormatHint)); - - out.Key("data"); - if(!ai.mHeight) { - out.SimpleValue(ai.pcData,ai.mWidth); - } - else { - out.StartArray(); - for(unsigned int y = 0; y < ai.mHeight; ++y) { - out.StartArray(true); - for(unsigned int x = 0; x < ai.mWidth; ++x) { - const aiTexel& tx = ai.pcData[y*ai.mWidth+x]; - out.StartArray(true); - out.Element(static_cast(tx.r)); - out.Element(static_cast(tx.g)); - out.Element(static_cast(tx.b)); - out.Element(static_cast(tx.a)); - out.EndArray(); - } - out.EndArray(); - } - out.EndArray(); - } - - out.EndObj(); -} - -void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("name"); - out.SimpleValue(ai.mName); - - out.Key("type"); - out.SimpleValue(ai.mType); - - if(ai.mType == aiLightSource_SPOT || ai.mType == aiLightSource_UNDEFINED) { - out.Key("angleinnercone"); - out.SimpleValue(ai.mAngleInnerCone); - - out.Key("angleoutercone"); - out.SimpleValue(ai.mAngleOuterCone); - } - - out.Key("attenuationconstant"); - out.SimpleValue(ai.mAttenuationConstant); - - out.Key("attenuationlinear"); - out.SimpleValue(ai.mAttenuationLinear); - - out.Key("attenuationquadratic"); - out.SimpleValue(ai.mAttenuationQuadratic); - - out.Key("diffusecolor"); - Write(out,ai.mColorDiffuse,false); - - out.Key("specularcolor"); - Write(out,ai.mColorSpecular,false); - - out.Key("ambientcolor"); - Write(out,ai.mColorAmbient,false); - - if(ai.mType != aiLightSource_POINT) { - out.Key("direction"); - Write(out,ai.mDirection,false); - - } - - if(ai.mType != aiLightSource_DIRECTIONAL) { - out.Key("position"); - Write(out,ai.mPosition,false); - } - - out.EndObj(); -} - -void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("name"); - out.SimpleValue(ai.mNodeName); - - out.Key("prestate"); - out.SimpleValue(ai.mPreState); - - out.Key("poststate"); - out.SimpleValue(ai.mPostState); - - if(ai.mNumPositionKeys) { - out.Key("positionkeys"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumPositionKeys; ++n) { - const aiVectorKey& pos = ai.mPositionKeys[n]; - out.StartArray(true); - out.Element(pos.mTime); - Write(out,pos.mValue); - out.EndArray(); - } - out.EndArray(); - } - - if(ai.mNumRotationKeys) { - out.Key("rotationkeys"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumRotationKeys; ++n) { - const aiQuatKey& rot = ai.mRotationKeys[n]; - out.StartArray(true); - out.Element(rot.mTime); - Write(out,rot.mValue); - out.EndArray(); - } - out.EndArray(); - } - - if(ai.mNumScalingKeys) { - out.Key("scalingkeys"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumScalingKeys; ++n) { - const aiVectorKey& scl = ai.mScalingKeys[n]; - out.StartArray(true); - out.Element(scl.mTime); - Write(out,scl.mValue); - out.EndArray(); - } - out.EndArray(); - } - out.EndObj(); -} - -void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("name"); - out.SimpleValue(ai.mName); - - out.Key("tickspersecond"); - out.SimpleValue(ai.mTicksPerSecond); - - out.Key("duration"); - out.SimpleValue(ai.mDuration); - - out.Key("channels"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumChannels; ++n) { - Write(out,*ai.mChannels[n]); - } - out.EndArray(); - out.EndObj(); -} - -void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) -{ - out.StartObj(is_elem); - - out.Key("name"); - out.SimpleValue(ai.mName); - - out.Key("aspect"); - out.SimpleValue(ai.mAspect); - - out.Key("clipplanefar"); - out.SimpleValue(ai.mClipPlaneFar); - - out.Key("clipplanenear"); - out.SimpleValue(ai.mClipPlaneNear); - - out.Key("horizontalfov"); - out.SimpleValue(ai.mHorizontalFOV); - - out.Key("up"); - Write(out,ai.mUp,false); - - out.Key("lookat"); - Write(out,ai.mLookAt,false); - - out.EndObj(); -} - -void WriteFormatInfo(JSONWriter& out) -{ - out.StartObj(); - out.Key("format"); - out.SimpleValue("\"assimp2json\""); - out.Key("version"); - out.SimpleValue(CURRENT_FORMAT_VERSION); - out.EndObj(); -} - -void Write(JSONWriter& out, const aiScene& ai) -{ - out.StartObj(); - - out.Key("__metadata__"); - WriteFormatInfo(out); - - out.Key("rootnode"); - Write(out,*ai.mRootNode, false); - - out.Key("flags"); - out.SimpleValue(ai.mFlags); - - if(ai.HasMeshes()) { - out.Key("meshes"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumMeshes; ++n) { - Write(out,*ai.mMeshes[n]); - } - out.EndArray(); - } - - if(ai.HasMaterials()) { - out.Key("materials"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumMaterials; ++n) { - Write(out,*ai.mMaterials[n]); - } - out.EndArray(); - } - - if(ai.HasAnimations()) { - out.Key("animations"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumAnimations; ++n) { - Write(out,*ai.mAnimations[n]); - } - out.EndArray(); - } - - if(ai.HasLights()) { - out.Key("lights"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumLights; ++n) { - Write(out,*ai.mLights[n]); - } - out.EndArray(); - } - - if(ai.HasCameras()) { - out.Key("cameras"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumCameras; ++n) { - Write(out,*ai.mCameras[n]); - } - out.EndArray(); - } - - if(ai.HasTextures()) { - out.Key("textures"); - out.StartArray(); - for(unsigned int n = 0; n < ai.mNumTextures; ++n) { - Write(out,*ai.mTextures[n]); - } - out.EndArray(); - } - out.EndObj(); -} - - -void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) -{ - std::unique_ptr str(io->Open(file,"wt")); - if(!str) { - //throw Assimp::DeadlyExportError("could not open output file"); - } - - // get a copy of the scene so we can modify it - aiScene* scenecopy_tmp; - aiCopyScene(scene, &scenecopy_tmp); - - try { - // split meshes so they fit into a 16 bit index buffer - MeshSplitter splitter; - splitter.SetLimit(1 << 16); - splitter.Execute(scenecopy_tmp); - - // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters - JSONWriter s(*str,JSONWriter::Flag_WriteSpecialFloats); - Write(s,*scenecopy_tmp); - - } - catch(...) { - aiFreeScene(scenecopy_tmp); - throw; - } - aiFreeScene(scenecopy_tmp); -} - -} // - #endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/Assjson/mesh_splitter.cpp b/code/Assjson/mesh_splitter.cpp index 07ae0bd86..24385f9a0 100644 --- a/code/Assjson/mesh_splitter.cpp +++ b/code/Assjson/mesh_splitter.cpp @@ -6,7 +6,6 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information */ - #include "mesh_splitter.h" #include @@ -18,8 +17,7 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. -void MeshSplitter :: Execute( aiScene* pScene) -{ +void MeshSplitter::Execute( aiScene* pScene) { std::vector > source_mesh_map; for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { @@ -27,8 +25,7 @@ void MeshSplitter :: Execute( aiScene* pScene) } const unsigned int size = static_cast(source_mesh_map.size()); - if (size != pScene->mNumMeshes) - { + if (size != pScene->mNumMeshes) { // it seems something has been split. rebuild the mesh list delete[] pScene->mMeshes; pScene->mNumMeshes = size; @@ -45,8 +42,7 @@ void MeshSplitter :: Execute( aiScene* pScene) // ------------------------------------------------------------------------------------------------ -void MeshSplitter :: UpdateNode(aiNode* pcNode, const std::vector >& source_mesh_map) -{ +void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector >& source_mesh_map) { // TODO: should better use std::(multi)set for source_mesh_map. // for every index in out list build a new entry @@ -82,10 +78,9 @@ typedef std::pair PerVertexWeight; typedef std::vector VertexWeightTable; // ------------------------------------------------------------------------------------------------ -VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) -{ +VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) { if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) { - return NULL; + return nullptr; } VertexWeightTable* const avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices]; @@ -101,9 +96,7 @@ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) } // ------------------------------------------------------------------------------------------------ -void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, - std::vector >& source_mesh_map) -{ +void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector >& source_mesh_map) { // TODO: should better use std::(multi)set for source_mesh_map. if (in_mesh->mNumVertices <= LIMIT) { diff --git a/code/COB/COBLoader.cpp b/code/COB/COBLoader.cpp index b9eb5b7ea..19e3cd59e 100644 --- a/code/COB/COBLoader.cpp +++ b/code/COB/COBLoader.cpp @@ -898,6 +898,7 @@ public: : nfo(nfo) , reader(reader) , cur(reader.GetCurrentPos()) { + // empty } ~chunk_guard() { @@ -905,7 +906,7 @@ public: if(nfo.size != static_cast(-1)) { try { reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur ); - } catch (const DeadlyImportError& e ) { + } catch (const DeadlyImportError& ) { // out of limit so correct the value reader.IncPtr( reader.GetReadLimit() ); } @@ -913,15 +914,17 @@ public: } private: - const COB::ChunkInfo& nfo; StreamReaderLE& reader; long cur; }; // ------------------------------------------------------------------------------------------------ -void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader) -{ +void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader) { + if (nullptr == reader) { + return; + } + while(1) { std::string type; type += reader -> GetI1() diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index b99f145ab..32ac3b416 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -200,9 +200,6 @@ corresponding preprocessor flag to selectively disable formats. #ifndef ASSIMP_BUILD_NO_STEP_IMPORTER # include "Importer/StepFile/StepFileImporter.h" #endif -#ifndef ASSIMP_BUILD_NO_JT_IMPORTER -# include "JTImporter.h" -#endif namespace Assimp { @@ -361,10 +358,6 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) #ifndef ASSIMP_BUILD_NO_STEP_IMPORTER out.push_back(new StepFile::StepFileImporter()); #endif -#ifndef ASSIMP_BUILD_NO_JT_IMPORTER - out.push_back(new JTImporter()); -#endif - } /** will delete all registered importers. */ diff --git a/code/FBX/FBXDocument.h b/code/FBX/FBXDocument.h index 53bd65878..18e5c38f1 100644 --- a/code/FBX/FBXDocument.h +++ b/code/FBX/FBXDocument.h @@ -627,7 +627,7 @@ public: return content; } - uint32_t ContentLength() const { + uint64_t ContentLength() const { return contentLength; } diff --git a/include/assimp/camera.h b/include/assimp/camera.h index dd45d90d1..e573eea5d 100644 --- a/include/assimp/camera.h +++ b/include/assimp/camera.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -60,7 +58,7 @@ extern "C" { * * Cameras have a representation in the node graph and can be animated. * An important aspect is that the camera itself is also part of the - * scenegraph. This means, any values such as the look-at vector are not + * scene-graph. This means, any values such as the look-at vector are not * *absolute*, they're relative to the coordinate system defined * by the node which corresponds to the camera. This allows for camera * animations. For static cameras parameters like the 'look-at' or 'up' vectors diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 28aeac65a..913813c3b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -120,13 +120,13 @@ SET( IMPORTERS unit/utB3DImportExport.cpp unit/utMDCImportExport.cpp unit/utAssbinImportExport.cpp + unit/ImportExport/utAssjsonImportExport.cpp unit/ImportExport/utCOBImportExport.cpp unit/ImportExport/utOgreImportExport.cpp unit/ImportExport/utQ3BSPFileImportExport.cpp unit/ImportExport/utOFFImportExport.cpp unit/ImportExport/utNFFImportExport.cpp unit/ImportExport/utXGLImportExport.cpp -# unit/ImportExport/utJTImporter.cpp ) SET( MATERIAL @@ -166,11 +166,11 @@ SET( POST_PROCESSES ) SOURCE_GROUP( UnitTests\\Compiler FILES unit/CCompilerTest.c ) -SOURCE_GROUP( UnitTests\\Common FILES ${COMMON} ) -SOURCE_GROUP( UnitTests\\Importers FILES ${IMPORTERS} ) -SOURCE_GROUP( UnitTests\\Material FILES ${MATERIAL} ) -SOURCE_GROUP( UnitTests\\Math FILES ${MATH} ) -SOURCE_GROUP( UnitTests\\PostProcess FILES ${POST_PROCESSES}) +SOURCE_GROUP( UnitTests\\Common FILES ${COMMON} ) +SOURCE_GROUP( UnitTests\\ImportExport FILES ${IMPORTERS} ) +SOURCE_GROUP( UnitTests\\Material FILES ${MATERIAL} ) +SOURCE_GROUP( UnitTests\\Math FILES ${MATH} ) +SOURCE_GROUP( UnitTests\\PostProcess FILES ${POST_PROCESSES}) add_executable( unit ../contrib/gtest/src/gtest-all.cc diff --git a/test/models/PLY/cube_test.ply b/test/models/PLY/cube_test.ply index 45fae27fb..f2ac612e8 100644 --- a/test/models/PLY/cube_test.ply +++ b/test/models/PLY/cube_test.ply @@ -1,10 +1,6 @@ ply format ascii 1.0 -<<<<<<< HEAD -comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.732158294) -======= -comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.649942190) ->>>>>>> master +comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.993695325) element vertex 8 property float x property float y diff --git a/test/unit/AbstractImportExportBase.h b/test/unit/AbstractImportExportBase.h index 529b9cf7d..d6ae37d60 100644 --- a/test/unit/AbstractImportExportBase.h +++ b/test/unit/AbstractImportExportBase.h @@ -3,7 +3,7 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2016, assimp team +Copyright (c) 2006-2019, assimp team All rights reserved. @@ -39,17 +39,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ #pragma once +#ifndef AI_ABSTRACTIMPORTEXPORTBASE_H_INC +#define AI_ABSTRACTIMPORTEXPORTBASE_H_INC #include "UnitTestPCH.h" +// --------------------------------------------------------------------------- +/** Abstract base class to test import and export + */ + // --------------------------------------------------------------------------- class AbstractImportExportBase : public ::testing::Test { public: + /// @brief The class destructor. virtual ~AbstractImportExportBase(); - virtual bool importerTest() = 0; + + /// @brief The importer-test, will return true for successful import. + /// @return true for success, false for failure. + virtual bool importerTest(); + + /// @brief The exporter-test, will return true for successful import. + /// @return true for success, false for failure. virtual bool exporterTest(); }; +inline +bool AbstractImportExportBase::importerTest() { + return true; +} + inline bool AbstractImportExportBase::exporterTest() { return true; } + +#endif // AI_ABSTRACTIMPORTEXPORTBASE_H_INC diff --git a/test/unit/ImportExport/utAssjsonImportExport.cpp b/test/unit/ImportExport/utAssjsonImportExport.cpp new file mode 100644 index 000000000..20a2a9e24 --- /dev/null +++ b/test/unit/ImportExport/utAssjsonImportExport.cpp @@ -0,0 +1,69 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" +#include "AbstractImportExportBase.h" + +#include +#include +#include +#include + +using namespace Assimp; + +#ifndef ASSIMP_BUILD_NO_EXPORT + +class utAssjsonImportExport : public AbstractImportExportBase { +public: + bool exporterTest() override { + Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure); + + Exporter exporter; + aiReturn res = exporter.Export(scene, "json", "./spider_test.json"); + return aiReturn_SUCCESS == res; + } +}; + +TEST_F(utAssjsonImportExport, exportTest) { + EXPECT_TRUE(exporterTest()); +} + +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/test/unit/utAMFImportExport.cpp b/test/unit/utAMFImportExport.cpp index dc2c9b99b..6eb0b4418 100644 --- a/test/unit/utAMFImportExport.cpp +++ b/test/unit/utAMFImportExport.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/test/unit/utASEImportExport.cpp b/test/unit/utASEImportExport.cpp index 28168de7d..313f0f83a 100644 --- a/test/unit/utASEImportExport.cpp +++ b/test/unit/utASEImportExport.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/test/unit/utAssbinImportExport.cpp b/test/unit/utAssbinImportExport.cpp index 6def04796..011aaa649 100644 --- a/test/unit/utAssbinImportExport.cpp +++ b/test/unit/utAssbinImportExport.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -53,8 +51,8 @@ using namespace Assimp; class utAssbinImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { - Assimp::Importer importer; + bool importerTest() override { + Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure ); Exporter exporter; @@ -66,7 +64,7 @@ public: }; TEST_F( utAssbinImportExport, exportAssbin3DFromFileTest ) { - Assimp::Importer importer; + Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); } diff --git a/test/unit/utB3DImportExport.cpp b/test/unit/utB3DImportExport.cpp index d51ca51a6..ea75b1939 100644 --- a/test/unit/utB3DImportExport.cpp +++ b/test/unit/utB3DImportExport.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index ea539df27..064031f34 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -49,35 +47,42 @@ using namespace Assimp; class FindDegeneratesProcessTest : public ::testing::Test { public: + FindDegeneratesProcessTest() + : Test() + , mMesh( nullptr ) + , mProcess( nullptr ) { + // empty + } + +protected: virtual void SetUp(); virtual void TearDown(); protected: - aiMesh* mesh; - FindDegeneratesProcess* process; + aiMesh* mMesh; + FindDegeneratesProcess* mProcess; }; -// ------------------------------------------------------------------------------------------------ void FindDegeneratesProcessTest::SetUp() { - mesh = new aiMesh(); - process = new FindDegeneratesProcess(); + mMesh = new aiMesh(); + mProcess = new FindDegeneratesProcess(); - mesh->mNumFaces = 1000; - mesh->mFaces = new aiFace[1000]; + mMesh->mNumFaces = 1000; + mMesh->mFaces = new aiFace[1000]; - mesh->mNumVertices = 5000*2; - mesh->mVertices = new aiVector3D[5000*2]; + mMesh->mNumVertices = 5000*2; + mMesh->mVertices = new aiVector3D[5000*2]; for (unsigned int i = 0; i < 5000; ++i) { - mesh->mVertices[i] = mesh->mVertices[i+5000] = aiVector3D((float)i); + mMesh->mVertices[i] = mMesh->mVertices[i+5000] = aiVector3D((float)i); } - mesh->mPrimitiveTypes = aiPrimitiveType_LINE | aiPrimitiveType_POINT | + mMesh->mPrimitiveTypes = aiPrimitiveType_LINE | aiPrimitiveType_POINT | aiPrimitiveType_POLYGON | aiPrimitiveType_TRIANGLE; unsigned int numOut = 0, numFaces = 0; for (unsigned int i = 0; i < 1000; ++i) { - aiFace& f = mesh->mFaces[i]; + aiFace& f = mMesh->mFaces[i]; f.mNumIndices = (i % 5)+1; // between 1 and 5 f.mIndices = new unsigned int[f.mNumIndices]; bool had = false; @@ -102,46 +107,46 @@ void FindDegeneratesProcessTest::SetUp() { if (!had) ++numFaces; } - mesh->mNumUVComponents[0] = numOut; - mesh->mNumUVComponents[1] = numFaces; + mMesh->mNumUVComponents[0] = numOut; + mMesh->mNumUVComponents[1] = numFaces; } void FindDegeneratesProcessTest::TearDown() { - delete mesh; - delete process; + delete mMesh; + delete mProcess; } TEST_F(FindDegeneratesProcessTest, testDegeneratesDetection) { - process->EnableInstantRemoval(false); - process->ExecuteOnMesh(mesh); + mProcess->EnableInstantRemoval(false); + mProcess->ExecuteOnMesh(mMesh); unsigned int out = 0; for (unsigned int i = 0; i < 1000; ++i) { - aiFace& f = mesh->mFaces[i]; + aiFace& f = mMesh->mFaces[i]; out += f.mNumIndices; } - EXPECT_EQ(1000U, mesh->mNumFaces); - EXPECT_EQ(10000U, mesh->mNumVertices); - EXPECT_EQ(out, mesh->mNumUVComponents[0]); + EXPECT_EQ(1000U, mMesh->mNumFaces); + EXPECT_EQ(10000U, mMesh->mNumVertices); + EXPECT_EQ(out, mMesh->mNumUVComponents[0]); EXPECT_EQ(static_cast( aiPrimitiveType_LINE | aiPrimitiveType_POINT | aiPrimitiveType_POLYGON | aiPrimitiveType_TRIANGLE), - mesh->mPrimitiveTypes); + mMesh->mPrimitiveTypes); } TEST_F(FindDegeneratesProcessTest, testDegeneratesRemoval) { - process->EnableAreaCheck(false); - process->EnableInstantRemoval(true); - process->ExecuteOnMesh(mesh); + mProcess->EnableAreaCheck(false); + mProcess->EnableInstantRemoval(true); + mProcess->ExecuteOnMesh(mMesh); - EXPECT_EQ(mesh->mNumUVComponents[1], mesh->mNumFaces); + EXPECT_EQ(mMesh->mNumUVComponents[1], mMesh->mNumFaces); } TEST_F(FindDegeneratesProcessTest, testDegeneratesRemovalWithAreaCheck) { - process->EnableAreaCheck(true); - process->EnableInstantRemoval(true); - process->ExecuteOnMesh(mesh); + mProcess->EnableAreaCheck(true); + mProcess->EnableInstantRemoval(true); + mProcess->ExecuteOnMesh(mMesh); - EXPECT_EQ(mesh->mNumUVComponents[1]-100, mesh->mNumFaces); + EXPECT_EQ(mMesh->mNumUVComponents[1]-100, mMesh->mNumFaces); } diff --git a/test/unit/utFindInvalidData.cpp b/test/unit/utFindInvalidData.cpp index 5b874a2a4..7c70a71a9 100644 --- a/test/unit/utFindInvalidData.cpp +++ b/test/unit/utFindInvalidData.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -48,89 +46,100 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace std; using namespace Assimp; -class FindInvalidDataProcessTest : public ::testing::Test -{ +class utFindInvalidDataProcess : public ::testing::Test { public: + utFindInvalidDataProcess() + : Test() + , mMesh(nullptr) + , mProcess(nullptr) { + // empty + } + +protected: virtual void SetUp(); virtual void TearDown(); protected: - aiMesh* pcMesh; - FindInvalidDataProcess* piProcess; + aiMesh* mMesh; + FindInvalidDataProcess* mProcess; }; // ------------------------------------------------------------------------------------------------ -void FindInvalidDataProcessTest::SetUp() { +void utFindInvalidDataProcess::SetUp() { ASSERT_TRUE( AI_MAX_NUMBER_OF_TEXTURECOORDS >= 3); - piProcess = new FindInvalidDataProcess(); - pcMesh = new aiMesh(); + mProcess = new FindInvalidDataProcess(); + mMesh = new aiMesh(); - pcMesh->mNumVertices = 1000; - pcMesh->mVertices = new aiVector3D[1000]; - for (unsigned int i = 0; i < 1000;++i) - pcMesh->mVertices[i] = aiVector3D((float)i); + mMesh->mNumVertices = 1000; + mMesh->mVertices = new aiVector3D[1000]; + for (unsigned int i = 0; i < 1000; ++i) { + mMesh->mVertices[i] = aiVector3D((float)i); + } - pcMesh->mNormals = new aiVector3D[1000]; - for (unsigned int i = 0; i < 1000;++i) - pcMesh->mNormals[i] = aiVector3D((float)i+1); + mMesh->mNormals = new aiVector3D[1000]; + for (unsigned int i = 0; i < 1000; ++i) { + mMesh->mNormals[i] = aiVector3D((float)i + 1); + } - pcMesh->mTangents = new aiVector3D[1000]; - for (unsigned int i = 0; i < 1000;++i) - pcMesh->mTangents[i] = aiVector3D((float)i); + mMesh->mTangents = new aiVector3D[1000]; + for (unsigned int i = 0; i < 1000; ++i) { + mMesh->mTangents[i] = aiVector3D((float)i); + } - pcMesh->mBitangents = new aiVector3D[1000]; - for (unsigned int i = 0; i < 1000;++i) - pcMesh->mBitangents[i] = aiVector3D((float)i); + mMesh->mBitangents = new aiVector3D[1000]; + for (unsigned int i = 0; i < 1000; ++i) { + mMesh->mBitangents[i] = aiVector3D((float)i); + } - for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) - { - pcMesh->mTextureCoords[a] = new aiVector3D[1000]; - for (unsigned int i = 0; i < 1000;++i) - pcMesh->mTextureCoords[a][i] = aiVector3D((float)i); + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { + mMesh->mTextureCoords[a] = new aiVector3D[1000]; + for (unsigned int i = 0; i < 1000; ++i) { + mMesh->mTextureCoords[a][i] = aiVector3D((float)i); + } } } // ------------------------------------------------------------------------------------------------ -void FindInvalidDataProcessTest::TearDown() -{ - delete piProcess; - delete pcMesh; +void utFindInvalidDataProcess::TearDown() { + delete mProcess; + delete mMesh; } // ------------------------------------------------------------------------------------------------ -TEST_F(FindInvalidDataProcessTest, testStepNegativeResult) -{ - ::memset(pcMesh->mNormals,0,pcMesh->mNumVertices*sizeof(aiVector3D)); - ::memset(pcMesh->mBitangents,0,pcMesh->mNumVertices*sizeof(aiVector3D)); +TEST_F(utFindInvalidDataProcess, testStepNegativeResult) { + ::memset(mMesh->mNormals, 0, mMesh->mNumVertices*sizeof(aiVector3D) ); + ::memset(mMesh->mBitangents, 0, mMesh->mNumVertices*sizeof(aiVector3D) ); - pcMesh->mTextureCoords[2][455] = aiVector3D( std::numeric_limits::quiet_NaN() ); + mMesh->mTextureCoords[2][455] = aiVector3D( std::numeric_limits::quiet_NaN() ); - piProcess->ProcessMesh(pcMesh); + mProcess->ProcessMesh(mMesh); - EXPECT_TRUE(NULL != pcMesh->mVertices); - EXPECT_TRUE(NULL == pcMesh->mNormals); - EXPECT_TRUE(NULL == pcMesh->mTangents); - EXPECT_TRUE(NULL == pcMesh->mBitangents); + EXPECT_TRUE(NULL != mMesh->mVertices); + EXPECT_EQ(NULL, mMesh->mNormals); + EXPECT_EQ(NULL, mMesh->mTangents); + EXPECT_EQ(NULL, mMesh->mBitangents); - for (unsigned int i = 0; i < 2;++i) - EXPECT_TRUE(NULL != pcMesh->mTextureCoords[i]); + for (unsigned int i = 0; i < 2; ++i) { + EXPECT_TRUE(NULL != mMesh->mTextureCoords[i]); + } - for (unsigned int i = 2; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - EXPECT_TRUE(NULL == pcMesh->mTextureCoords[i]); + for (unsigned int i = 2; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + EXPECT_EQ(NULL, mMesh->mTextureCoords[i]); + } } // ------------------------------------------------------------------------------------------------ -TEST_F(FindInvalidDataProcessTest, testStepPositiveResult) -{ - piProcess->ProcessMesh(pcMesh); +TEST_F(utFindInvalidDataProcess, testStepPositiveResult) { + mProcess->ProcessMesh(mMesh); - EXPECT_TRUE(NULL != pcMesh->mVertices); + EXPECT_NE(nullptr, mMesh->mVertices); - EXPECT_TRUE(NULL != pcMesh->mNormals); - EXPECT_TRUE(NULL != pcMesh->mTangents); - EXPECT_TRUE(NULL != pcMesh->mBitangents); + EXPECT_NE(nullptr, mMesh->mNormals); + EXPECT_NE(nullptr, mMesh->mTangents); + EXPECT_NE(nullptr, mMesh->mBitangents); - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - EXPECT_TRUE(NULL != pcMesh->mTextureCoords[i]); + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + EXPECT_NE(nullptr, mMesh->mTextureCoords[i]); + } } diff --git a/test/unit/utGenBoundingBoxesProcess.cpp b/test/unit/utGenBoundingBoxesProcess.cpp index a26edb7e3..2c2f831bc 100644 --- a/test/unit/utGenBoundingBoxesProcess.cpp +++ b/test/unit/utGenBoundingBoxesProcess.cpp @@ -48,13 +48,21 @@ using namespace Assimp; class utGenBoundingBoxesProcess : public ::testing::Test { public: + utGenBoundingBoxesProcess() + : Test() + , mProcess(nullptr) + , mMesh(nullptr) + , mScene(nullptr) { + // empty + } + void SetUp() override { mProcess = new GenBoundingBoxesProcess; mMesh = new aiMesh(); mMesh->mNumVertices = 100; mMesh->mVertices = new aiVector3D[100]; for (unsigned int i = 0; i < 100; ++i) { - mMesh->mVertices[i] = aiVector3D(i, i, i); + mMesh->mVertices[i] = aiVector3D((ai_real)i, (ai_real)i, (ai_real)i); } mScene = new aiScene(); mScene->mNumMeshes = 1; diff --git a/test/unit/utJoinVertices.cpp b/test/unit/utJoinVertices.cpp index 6b832b73d..215b0dd90 100644 --- a/test/unit/utJoinVertices.cpp +++ b/test/unit/utJoinVertices.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -49,8 +47,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace std; using namespace Assimp; -class JoinVerticesTest : public ::testing::Test { +class utJoinVertices : public ::testing::Test { public: + utJoinVertices() + : Test() + , piProcess(nullptr) + , pcMesh(nullptr) { + // empty + } + +protected: virtual void SetUp(); virtual void TearDown(); @@ -60,8 +66,7 @@ protected: }; // ------------------------------------------------------------------------------------------------ -void JoinVerticesTest::SetUp() -{ +void utJoinVertices::SetUp() { // construct the process piProcess = new JoinVerticesProcess(); @@ -71,11 +76,9 @@ void JoinVerticesTest::SetUp() pcMesh->mNumVertices = 900; aiVector3D*& pv = pcMesh->mVertices = new aiVector3D[900]; - for (unsigned int i = 0; i < 3;++i) - { + for (unsigned int i = 0; i < 3;++i) { const unsigned int base = i*300; - for (unsigned int a = 0; a < 300;++a) - { + for (unsigned int a = 0; a < 300;++a) { pv[base+a].x = pv[base+a].y = pv[base+a].z = (float)a; } } @@ -83,38 +86,37 @@ void JoinVerticesTest::SetUp() // generate faces - each vertex is referenced once pcMesh->mNumFaces = 300; pcMesh->mFaces = new aiFace[300]; - for (unsigned int i = 0,p = 0; i < 300;++i) - { + for (unsigned int i = 0,p = 0; i < 300;++i) { aiFace& face = pcMesh->mFaces[i]; face.mIndices = new unsigned int[ face.mNumIndices = 3 ]; - for (unsigned int a = 0; a < 3;++a) + for (unsigned int a = 0; a < 3; ++a) { face.mIndices[a] = p++; + } } // generate extra members - set them to zero to make sure they're identical pcMesh->mTextureCoords[0] = new aiVector3D[900]; - for (unsigned int i = 0; i < 900;++i)pcMesh->mTextureCoords[0][i] = aiVector3D( 0.f ); - - pcMesh->mNormals = new aiVector3D[900]; - for (unsigned int i = 0; i < 900;++i)pcMesh->mNormals[i] = aiVector3D( 0.f ); - - pcMesh->mTangents = new aiVector3D[900]; - for (unsigned int i = 0; i < 900;++i)pcMesh->mTangents[i] = aiVector3D( 0.f ); - pcMesh->mBitangents = new aiVector3D[900]; - for (unsigned int i = 0; i < 900;++i)pcMesh->mBitangents[i] = aiVector3D( 0.f ); + pcMesh->mNormals = new aiVector3D[900]; + pcMesh->mTangents = new aiVector3D[900]; + for (unsigned int i = 0; i < 900; ++i) { + pcMesh->mTextureCoords[0][i] = aiVector3D(0.f); + pcMesh->mNormals[i] = aiVector3D(0.f); + pcMesh->mTangents[i] = aiVector3D(0.f); + pcMesh->mBitangents[i] = aiVector3D(0.f); + } } // ------------------------------------------------------------------------------------------------ -void JoinVerticesTest::TearDown() -{ +void utJoinVertices::TearDown() { delete this->pcMesh; + pcMesh = nullptr; delete this->piProcess; + piProcess = nullptr; } // ------------------------------------------------------------------------------------------------ -TEST_F(JoinVerticesTest, testProcess) -{ +TEST_F(utJoinVertices, testProcess) { // execute the step on the given data piProcess->ProcessMesh(pcMesh,0); @@ -122,15 +124,14 @@ TEST_F(JoinVerticesTest, testProcess) ASSERT_EQ(300U, pcMesh->mNumFaces); ASSERT_EQ(300U, pcMesh->mNumVertices); - ASSERT_TRUE(NULL != pcMesh->mNormals); - ASSERT_TRUE(NULL != pcMesh->mTangents); - ASSERT_TRUE(NULL != pcMesh->mBitangents); - ASSERT_TRUE(NULL != pcMesh->mTextureCoords[0]); + ASSERT_TRUE( nullptr != pcMesh->mNormals); + ASSERT_TRUE( nullptr != pcMesh->mTangents); + ASSERT_TRUE( nullptr != pcMesh->mBitangents); + ASSERT_TRUE( nullptr != pcMesh->mTextureCoords[0]); // the order doesn't care float fSum = 0.f; - for (unsigned int i = 0; i < 300;++i) - { + for (unsigned int i = 0; i < 300; ++i) { aiVector3D& v = pcMesh->mVertices[i]; fSum += v.x + v.y + v.z; @@ -141,4 +142,3 @@ TEST_F(JoinVerticesTest, testProcess) } EXPECT_EQ(150.f*299.f*3.f, fSum); // gaussian sum equation } - diff --git a/test/unit/utLimitBoneWeights.cpp b/test/unit/utLimitBoneWeights.cpp index 5172976b4..927a9e37c 100644 --- a/test/unit/utLimitBoneWeights.cpp +++ b/test/unit/utLimitBoneWeights.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -50,83 +48,83 @@ using namespace Assimp; class LimitBoneWeightsTest : public ::testing::Test { public: + LimitBoneWeightsTest() + : Test() + , mProcess(nullptr) + , mMesh(nullptr) { + // empty + } + +protected: virtual void SetUp(); virtual void TearDown(); protected: - LimitBoneWeightsProcess* piProcess; - aiMesh* pcMesh; + LimitBoneWeightsProcess *mProcess; + aiMesh *mMesh; }; // ------------------------------------------------------------------------------------------------ -void LimitBoneWeightsTest::SetUp() -{ +void LimitBoneWeightsTest::SetUp() { // construct the process - this->piProcess = new LimitBoneWeightsProcess(); + this->mProcess = new LimitBoneWeightsProcess(); // now need to create a nice mesh for testing purposes - this->pcMesh = new aiMesh(); + this->mMesh = new aiMesh(); - pcMesh->mNumVertices = 500; - pcMesh->mVertices = new aiVector3D[500]; // uninit. - pcMesh->mNumBones = 30; - pcMesh->mBones = new aiBone*[30]; + mMesh->mNumVertices = 500; + mMesh->mVertices = new aiVector3D[500]; // uninit. + mMesh->mNumBones = 30; + mMesh->mBones = new aiBone*[30]; unsigned int iCur = 0; - for (unsigned int i = 0; i < 30;++i) - { - aiBone* pc = pcMesh->mBones[i] = new aiBone(); + for (unsigned int i = 0; i < 30;++i) { + aiBone* pc = mMesh->mBones[i] = new aiBone(); pc->mNumWeights = 250; pc->mWeights = new aiVertexWeight[pc->mNumWeights]; - for (unsigned int qq = 0; qq < pc->mNumWeights;++qq) - { + for (unsigned int qq = 0; qq < pc->mNumWeights;++qq) { aiVertexWeight& v = pc->mWeights[qq]; v.mVertexId = iCur++; - if (500 == iCur)iCur = 0; + if (500 == iCur) { + iCur = 0; + } v.mWeight = 1.0f / 15; // each vertex should occur once in two bones } } } // ------------------------------------------------------------------------------------------------ -void LimitBoneWeightsTest::TearDown() -{ - delete pcMesh; - delete piProcess; +void LimitBoneWeightsTest::TearDown() { + delete mMesh; + delete mProcess; } // ------------------------------------------------------------------------------------------------ -TEST_F(LimitBoneWeightsTest, testProcess) -{ +TEST_F(LimitBoneWeightsTest, testProcess) { // execute the step on the given data - piProcess->ProcessMesh(pcMesh); + mProcess->ProcessMesh(mMesh); // check whether everything is ok ... typedef std::vector VertexWeightList; - VertexWeightList* asWeights = new VertexWeightList[pcMesh->mNumVertices]; + VertexWeightList* asWeights = new VertexWeightList[mMesh->mNumVertices]; - for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + for (unsigned int i = 0; i < mMesh->mNumVertices; ++i) { asWeights[i].reserve(4); + } // sort back as per-vertex lists - for (unsigned int i = 0; i < pcMesh->mNumBones;++i) - { - aiBone& pcBone = **(pcMesh->mBones+i); - for (unsigned int q = 0; q < pcBone.mNumWeights;++q) - { + for (unsigned int i = 0; i < mMesh->mNumBones;++i) { + aiBone& pcBone = **(mMesh->mBones+i); + for (unsigned int q = 0; q < pcBone.mNumWeights;++q) { aiVertexWeight weight = pcBone.mWeights[q]; asWeights[weight.mVertexId].push_back(LimitBoneWeightsProcess::Weight (i,weight.mWeight)); } } // now validate the size of the lists and check whether all weights sum to 1.0f - for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) - { + for (unsigned int i = 0; i < mMesh->mNumVertices;++i) { EXPECT_LE(asWeights[i].size(), 4U); float fSum = 0.0f; - for (VertexWeightList::const_iterator - iter = asWeights[i].begin(); - iter != asWeights[i].end();++iter) - { + for (VertexWeightList::const_iterator iter = asWeights[i].begin(); iter != asWeights[i].end();++iter) { fSum += (*iter).mWeight; } EXPECT_GE(fSum, 0.95F); diff --git a/test/unit/utPretransformVertices.cpp b/test/unit/utPretransformVertices.cpp index 22d1b28b4..0839740ac 100644 --- a/test/unit/utPretransformVertices.cpp +++ b/test/unit/utPretransformVertices.cpp @@ -51,18 +51,24 @@ using namespace Assimp; class PretransformVerticesTest : public ::testing::Test { public: + PretransformVerticesTest() + : Test() + , mScene(nullptr) + , mProcess(nullptr) { + // empty + } + +protected: virtual void SetUp(); virtual void TearDown(); protected: - - aiScene* scene; - PretransformVertices* process; + aiScene *mScene; + PretransformVertices *mProcess; }; // ------------------------------------------------------------------------------------------------ -void AddNodes(unsigned int num, aiNode* father, unsigned int depth) -{ +void AddNodes(unsigned int num, aiNode* father, unsigned int depth) { father->mChildren = new aiNode*[father->mNumChildren = 5]; for (unsigned int i = 0; i < 5; ++i) { aiNode* nd = father->mChildren[i] = new aiNode(); @@ -79,26 +85,26 @@ void AddNodes(unsigned int num, aiNode* father, unsigned int depth) } if (depth > 1) { - for (unsigned int i = 0; i < 5; ++i) - AddNodes(i, father->mChildren[i],depth-1); + for (unsigned int i = 0; i < 5; ++i) { + AddNodes(i, father->mChildren[i], depth - 1); + } } } // ------------------------------------------------------------------------------------------------ -void PretransformVerticesTest::SetUp() -{ - scene = new aiScene(); +void PretransformVerticesTest::SetUp() { + mScene = new aiScene(); // add 5 empty materials - scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 5]; + mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials = 5]; for (unsigned int i = 0; i < 5;++i) { - scene->mMaterials[i] = new aiMaterial(); + mScene->mMaterials[i] = new aiMaterial(); } // add 25 test meshes - scene->mMeshes = new aiMesh*[scene->mNumMeshes = 25]; - for ( unsigned int i = 0; i < 25; ++i) { - aiMesh* mesh = scene->mMeshes[ i ] = new aiMesh(); + mScene->mMeshes = new aiMesh*[mScene->mNumMeshes = 25]; + for ( unsigned int i = 0; i < 25; ++i) { + aiMesh* mesh = mScene->mMeshes[ i ] = new aiMesh(); mesh->mPrimitiveTypes = aiPrimitiveType_POINT; mesh->mFaces = new aiFace[ mesh->mNumFaces = 10+i ]; @@ -124,36 +130,33 @@ void PretransformVerticesTest::SetUp() } // construct some nodes (1+25) - scene->mRootNode = new aiNode(); - scene->mRootNode->mName.Set("Root"); - AddNodes(0,scene->mRootNode,2); + mScene->mRootNode = new aiNode(); + mScene->mRootNode->mName.Set("Root"); + AddNodes(0, mScene->mRootNode, 2); - process = new PretransformVertices(); + mProcess = new PretransformVertices(); } // ------------------------------------------------------------------------------------------------ -void PretransformVerticesTest::TearDown() -{ - delete scene; - delete process; +void PretransformVerticesTest::TearDown() { + delete mScene; + delete mProcess; } // ------------------------------------------------------------------------------------------------ -TEST_F(PretransformVerticesTest, testProcessCollapseHierarchy) -{ - process->KeepHierarchy(false); - process->Execute(scene); +TEST_F(PretransformVerticesTest, testProcessCollapseHierarchy) { + mProcess->KeepHierarchy(false); + mProcess->Execute(mScene); - EXPECT_EQ(5U, scene->mNumMaterials); - EXPECT_EQ(10U, scene->mNumMeshes); // every second mesh has normals + EXPECT_EQ(5U, mScene->mNumMaterials); + EXPECT_EQ(10U, mScene->mNumMeshes); // every second mesh has normals } // ------------------------------------------------------------------------------------------------ -TEST_F(PretransformVerticesTest, testProcessKeepHierarchy) -{ - process->KeepHierarchy(true); - process->Execute(scene); +TEST_F(PretransformVerticesTest, testProcessKeepHierarchy) { + mProcess->KeepHierarchy(true); + mProcess->Execute(mScene); - EXPECT_EQ(5U, scene->mNumMaterials); - EXPECT_EQ(49U, scene->mNumMeshes); // see note on mesh 12 above + EXPECT_EQ(5U, mScene->mNumMaterials); + EXPECT_EQ(49U, mScene->mNumMeshes); // see note on mesh 12 above } diff --git a/test/unit/utScenePreprocessor.cpp b/test/unit/utScenePreprocessor.cpp index 2c181a90f..7233e1379 100644 --- a/test/unit/utScenePreprocessor.cpp +++ b/test/unit/utScenePreprocessor.cpp @@ -50,67 +50,67 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace std; using namespace Assimp; - -class ScenePreprocessorTest : public ::testing::Test -{ +class ScenePreprocessorTest : public ::testing::Test { public: + ScenePreprocessorTest() + : Test() + , mScenePreprocessor(nullptr) + , mScene(nullptr) { + // empty + } +protected: virtual void SetUp(); virtual void TearDown(); protected: - void CheckIfOnly(aiMesh* p, unsigned int num, unsigned flag); + void ProcessAnimation(aiAnimation* anim) { mScenePreprocessor->ProcessAnimation(anim); } + void ProcessMesh(aiMesh* mesh) { mScenePreprocessor->ProcessMesh(mesh); } - void ProcessAnimation(aiAnimation* anim) { pp->ProcessAnimation(anim); } - void ProcessMesh(aiMesh* mesh) { pp->ProcessMesh(mesh); } - - ScenePreprocessor* pp; - aiScene* scene; +private: + ScenePreprocessor *mScenePreprocessor; + aiScene *mScene; }; // ------------------------------------------------------------------------------------------------ -void ScenePreprocessorTest::SetUp() -{ +void ScenePreprocessorTest::SetUp() { // setup a dummy scene with a single node - scene = new aiScene(); - scene->mRootNode = new aiNode(); - scene->mRootNode->mName.Set(""); + mScene = new aiScene(); + mScene->mRootNode = new aiNode(); + mScene->mRootNode->mName.Set(""); // add some translation - scene->mRootNode->mTransformation.a4 = 1.f; - scene->mRootNode->mTransformation.b4 = 2.f; - scene->mRootNode->mTransformation.c4 = 3.f; + mScene->mRootNode->mTransformation.a4 = 1.f; + mScene->mRootNode->mTransformation.b4 = 2.f; + mScene->mRootNode->mTransformation.c4 = 3.f; // and allocate a ScenePreprocessor to operate on the scene - pp = new ScenePreprocessor(scene); + mScenePreprocessor = new ScenePreprocessor(mScene); } // ------------------------------------------------------------------------------------------------ -void ScenePreprocessorTest::TearDown() -{ - delete pp; - delete scene; +void ScenePreprocessorTest::TearDown() { + delete mScenePreprocessor; + delete mScene; } // ------------------------------------------------------------------------------------------------ // Check whether ProcessMesh() returns flag for a mesh that consist of primitives with num indices -void ScenePreprocessorTest::CheckIfOnly(aiMesh* p, unsigned int num, unsigned int flag) -{ +void ScenePreprocessorTest::CheckIfOnly(aiMesh* p, unsigned int num, unsigned int flag) { // Triangles only for (unsigned i = 0; i < p->mNumFaces;++i) { p->mFaces[i].mNumIndices = num; } - pp->ProcessMesh(p); + mScenePreprocessor->ProcessMesh(p); EXPECT_EQ(flag, p->mPrimitiveTypes); p->mPrimitiveTypes = 0; } // ------------------------------------------------------------------------------------------------ // Check whether a mesh is preprocessed correctly. Case 1: The mesh needs preprocessing -TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos) -{ - aiMesh* p = new aiMesh(); +TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos) { + aiMesh* p = new aiMesh; p->mNumFaces = 100; p->mFaces = new aiFace[p->mNumFaces]; @@ -145,9 +145,8 @@ TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos) // ------------------------------------------------------------------------------------------------ // Check whether a mesh is preprocessed correctly. Case 1: The mesh doesn't need preprocessing -TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg) -{ - aiMesh* p = new aiMesh(); +TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg) { + aiMesh* p = new aiMesh; p->mPrimitiveTypes = aiPrimitiveType_TRIANGLE|aiPrimitiveType_POLYGON; ProcessMesh(p); @@ -160,8 +159,7 @@ TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg) // ------------------------------------------------------------------------------------------------ // Make a dummy animation with a single channel, '' -aiAnimation* MakeDummyAnimation() -{ +aiAnimation* MakeDummyAnimation() { aiAnimation* p = new aiAnimation(); p->mNumChannels = 1; p->mChannels = new aiNodeAnim*[1]; @@ -172,8 +170,7 @@ aiAnimation* MakeDummyAnimation() // ------------------------------------------------------------------------------------------------ // Check whether an anim is preprocessed correctly. Case 1: The anim needs preprocessing -TEST_F(ScenePreprocessorTest, testAnimationPreprocessingPos) -{ +TEST_F(ScenePreprocessorTest, testAnimationPreprocessingPos) { aiAnimation* p = MakeDummyAnimation(); aiNodeAnim* anim = p->mChannels[0]; diff --git a/test/unit/utSortByPType.cpp b/test/unit/utSortByPType.cpp index 1aa9dad0a..749e618da 100644 --- a/test/unit/utSortByPType.cpp +++ b/test/unit/utSortByPType.cpp @@ -52,12 +52,20 @@ using namespace Assimp; class SortByPTypeProcessTest : public ::testing::Test { public: + SortByPTypeProcessTest() + : Test() + , mProcess1(nullptr) + , mScene(nullptr) { + // empty + } + +protected: virtual void SetUp(); virtual void TearDown(); protected: - SortByPTypeProcess* process1; - aiScene* scene; + SortByPTypeProcess* mProcess1; + aiScene* mScene; }; // ------------------------------------------------------------------------------------------------ @@ -75,8 +83,7 @@ static unsigned int num[10][4] = { }; // ------------------------------------------------------------------------------------------------ -static unsigned int result[10] = -{ +static unsigned int result[10] = { aiPrimitiveType_POLYGON, aiPrimitiveType_TRIANGLE, aiPrimitiveType_LINE, @@ -90,19 +97,16 @@ static unsigned int result[10] = }; // ------------------------------------------------------------------------------------------------ -void SortByPTypeProcessTest::SetUp() -{ -// process0 = new DeterminePTypeHelperProcess(); - process1 = new SortByPTypeProcess(); - scene = new aiScene(); +void SortByPTypeProcessTest::SetUp() { + mProcess1 = new SortByPTypeProcess(); + mScene = new aiScene(); - scene->mNumMeshes = 10; - scene->mMeshes = new aiMesh*[10]; + mScene->mNumMeshes = 10; + mScene->mMeshes = new aiMesh*[10]; bool five = false; - for (unsigned int i = 0; i < 10; ++i) - { - aiMesh* mesh = scene->mMeshes[i] = new aiMesh(); + for (unsigned int i = 0; i < 10; ++i) { + aiMesh* mesh = mScene->mMeshes[i] = new aiMesh(); mesh->mNumFaces = 1000; aiFace* faces = mesh->mFaces = new aiFace[1000]; aiVector3D* pv = mesh->mVertices = new aiVector3D[mesh->mNumFaces*5]; @@ -115,27 +119,24 @@ void SortByPTypeProcessTest::SetUp() unsigned int remaining[4] = {num[i][0],num[i][1],num[i][2],num[i][3]}; unsigned int n = 0; - for (unsigned int m = 0; m < 1000; ++m) - { + for (unsigned int m = 0; m < 1000; ++m) { unsigned int idx = m % 4; - while (true) - { - if (!remaining[idx]) - { - if (4 == ++idx)idx = 0; + while (true) { + if (!remaining[idx]) { + if (4 == ++idx) { + idx = 0; + } continue; } break; } faces->mNumIndices = idx+1; - if (4 == faces->mNumIndices) - { + if (4 == faces->mNumIndices) { if(five)++faces->mNumIndices; five = !five; } faces->mIndices = new unsigned int[faces->mNumIndices]; - for (unsigned int q = 0; q mNumIndices;++q,++n) - { + for (unsigned int q = 0; q mNumIndices;++q,++n) { faces->mIndices[q] = n; float f = (float)remaining[idx]; @@ -152,12 +153,11 @@ void SortByPTypeProcessTest::SetUp() mesh->mNumVertices = n; } - scene->mRootNode = new aiNode(); - scene->mRootNode->mNumChildren = 5; - scene->mRootNode->mChildren = new aiNode*[5]; - for (unsigned int i = 0; i< 5;++i ) - { - aiNode* node = scene->mRootNode->mChildren[i] = new aiNode(); + mScene->mRootNode = new aiNode(); + mScene->mRootNode->mNumChildren = 5; + mScene->mRootNode->mChildren = new aiNode*[5]; + for (unsigned int i = 0; i< 5;++i ) { + aiNode* node = mScene->mRootNode->mChildren[i] = new aiNode(); node->mNumMeshes = 2; node->mMeshes = new unsigned int[2]; node->mMeshes[0] = (i<<1u); @@ -166,48 +166,28 @@ void SortByPTypeProcessTest::SetUp() } // ------------------------------------------------------------------------------------------------ -void SortByPTypeProcessTest::TearDown() -{ - //delete process0; - delete process1; - delete scene; +void SortByPTypeProcessTest::TearDown() { + delete mProcess1; + delete mScene; } // ------------------------------------------------------------------------------------------------ -//TEST_F(SortByPTypeProcessTest, DeterminePTypeStep() -//{ -// process0->Execute(scene); -// -// for (unsigned int i = 0; i < 10; ++i) -// { -// aiMesh* mesh = scene->mMeshes[i]; -// EXPECT_TRUE(mesh->mPrimitiveTypes == result[i]); -// } -//} - -// ------------------------------------------------------------------------------------------------ -TEST_F(SortByPTypeProcessTest, SortByPTypeStep) -{ - // process0->Execute(scene); - - // and another small test for ScenePreprocessor - ScenePreprocessor s(scene); +TEST_F(SortByPTypeProcessTest, SortByPTypeStep) { + ScenePreprocessor s(mScene); s.ProcessScene(); for (unsigned int m = 0; m< 10;++m) - EXPECT_EQ(result[m], scene->mMeshes[m]->mPrimitiveTypes); + EXPECT_EQ(result[m], mScene->mMeshes[m]->mPrimitiveTypes); - process1->Execute(scene); + mProcess1->Execute(mScene); unsigned int idx = 0; - for (unsigned int m = 0,real = 0; m< 10;++m) - { - for (unsigned int n = 0; n < 4;++n) - { + for (unsigned int m = 0,real = 0; m< 10;++m) { + for (unsigned int n = 0; n < 4;++n) { if ((idx = num[m][n])) { - EXPECT_TRUE(real < scene->mNumMeshes); + EXPECT_TRUE(real < mScene->mNumMeshes); - aiMesh* mesh = scene->mMeshes[real]; + aiMesh* mesh = mScene->mMeshes[real]; EXPECT_TRUE(NULL != mesh); EXPECT_EQ(AI_PRIMITIVE_TYPE_FOR_N_INDICES(n+1), mesh->mPrimitiveTypes); @@ -218,8 +198,7 @@ TEST_F(SortByPTypeProcessTest, SortByPTypeStep) EXPECT_TRUE(NULL != mesh->mTextureCoords[0]); EXPECT_TRUE(mesh->mNumFaces == idx); - for (unsigned int f = 0; f < mesh->mNumFaces;++f) - { + for (unsigned int f = 0; f < mesh->mNumFaces;++f) { aiFace& face = mesh->mFaces[f]; EXPECT_TRUE(face.mNumIndices == (n+1) || (3 == n && face.mNumIndices > 3)); } @@ -228,4 +207,3 @@ TEST_F(SortByPTypeProcessTest, SortByPTypeStep) } } } - From 8f74508e403ad1b8657d59127537ad3e452d8618 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Fri, 12 Jul 2019 16:28:36 +0200 Subject: [PATCH 13/13] Fix missing doc in config.h --- include/assimp/config.h.in | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index c95f0e160..d08b929a1 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -981,8 +981,12 @@ enum aiComponent #define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" -/** - * +/** @brief Specifies whether the assimp export shall be able to export point clouds + * + * When this flag is not defined the render data has to contain valid faces. + * Point clouds are only a collection of vertices which have nor spatial organization + * by a face and the validation process will remove them. Enabling this feature will + * switch off the flag and enable the functionality to export pure point clouds. */ #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"