From f8d6fe296b0a81e3034ec5a3f8a157d7eae3f830 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 21 Apr 2024 03:01:47 +0100 Subject: [PATCH] update warp3d and xmlrpc --- .../Warp3Dmod/Properties/AssemblyInfo.cs | 4 +- .../Warp3Dmod/System.Drawing.Common.dll | Bin 0 -> 454248 bytes trunk/managed/Warp3Dmod/Warp3D.cs | 975 +++++++----------- trunk/managed/Warp3Dmod/Warp3D.csproj | 167 +-- trunk/managed/Warp3Dmod/warp_3ds_Importer.cs | 327 +++--- trunk/managed/Warp3Dmod/warp_Camera.cs | 53 +- trunk/managed/Warp3Dmod/warp_Color.cs | 248 ++--- trunk/managed/Warp3Dmod/warp_CoreObject.cs | 207 ++-- trunk/managed/Warp3Dmod/warp_Edge.cs | 50 +- trunk/managed/Warp3Dmod/warp_Environment.cs | 30 +- trunk/managed/Warp3Dmod/warp_FXLensFlare .cs | 621 ++++++----- trunk/managed/Warp3Dmod/warp_FXPlugin.cs | 14 +- trunk/managed/Warp3Dmod/warp_Light.cs | 106 +- trunk/managed/Warp3Dmod/warp_Lightmap.cs | 51 +- trunk/managed/Warp3Dmod/warp_Material.cs | 443 ++++---- trunk/managed/Warp3Dmod/warp_Math.cs | 176 +--- trunk/managed/Warp3Dmod/warp_Matrix.cs | 727 +++++++------ trunk/managed/Warp3Dmod/warp_Object.cs | 310 ++---- trunk/managed/Warp3Dmod/warp_ObjectFactory.cs | 535 +++++----- trunk/managed/Warp3Dmod/warp_Quaternion.cs | 120 +-- trunk/managed/Warp3Dmod/warp_Rasterizer.cs | 662 ++++++------ .../managed/Warp3Dmod/warp_RenderPipeline.cs | 192 +--- trunk/managed/Warp3Dmod/warp_Scene.cs | 146 +-- trunk/managed/Warp3Dmod/warp_Screen.cs | 36 +- trunk/managed/Warp3Dmod/warp_Texture.cs | 242 ++--- .../managed/Warp3Dmod/warp_TextureFactory.cs | 402 ++++---- .../Warp3Dmod/warp_TextureProjector.cs | 84 +- .../managed/Warp3Dmod/warp_TextureSettings.cs | 54 +- trunk/managed/Warp3Dmod/warp_Triangle.cs | 37 +- trunk/managed/Warp3Dmod/warp_Vector.cs | 157 ++- trunk/managed/Warp3Dmod/warp_Vertex.cs | 140 ++- trunk/managed/XmlRpc/AssemblyInfo.cs | 2 +- trunk/managed/XmlRpc/Logger.cs | 3 +- trunk/managed/XmlRpc/SimpleHttpRequest.cs | 57 +- trunk/managed/XmlRpc/XmlRpcBoxcarRequest.cs | 8 +- trunk/managed/XmlRpc/XmlRpcDeserializer.cs | 40 +- trunk/managed/XmlRpc/XmlRpcException.cs | 4 +- .../managed/XmlRpc/XmlRpcExposedAttribute.cs | 2 +- trunk/managed/XmlRpc/XmlRpcRequest.cs | 88 +- .../XmlRpc/XmlRpcRequestDeserializer.cs | 8 +- .../managed/XmlRpc/XmlRpcRequestSerializer.cs | 4 +- trunk/managed/XmlRpc/XmlRpcResponder.cs | 16 +- trunk/managed/XmlRpc/XmlRpcResponse.cs | 8 +- .../XmlRpc/XmlRpcResponseDeserializer.cs | 8 +- .../XmlRpc/XmlRpcResponseSerializer.cs | 4 +- trunk/managed/XmlRpc/XmlRpcSerializer.cs | 44 +- trunk/managed/XmlRpc/XmlRpcSystemObject.cs | 491 ++++----- trunk/managed/XmlRpc/xmlrpc.csproj | 70 +- 48 files changed, 3775 insertions(+), 4398 deletions(-) create mode 100644 trunk/managed/Warp3Dmod/System.Drawing.Common.dll diff --git a/trunk/managed/Warp3Dmod/Properties/AssemblyInfo.cs b/trunk/managed/Warp3Dmod/Properties/AssemblyInfo.cs index ab90e8ee..787a128b 100644 --- a/trunk/managed/Warp3Dmod/Properties/AssemblyInfo.cs +++ b/trunk/managed/Warp3Dmod/Properties/AssemblyInfo.cs @@ -35,5 +35,5 @@ using System.Security.Permissions; // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.0.1")] +[assembly: AssemblyFileVersion("1.0.0.1")] diff --git a/trunk/managed/Warp3Dmod/System.Drawing.Common.dll b/trunk/managed/Warp3Dmod/System.Drawing.Common.dll new file mode 100644 index 0000000000000000000000000000000000000000..7c9e87b4e1820b2beb6928ab1dd927d23a8cb2e7 GIT binary patch literal 454248 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�Q$QrG!1cj(vNR~%ioVHm)It4uz^t z;rwRUZ?pNHbHry2XVQ;EMhssmi6LW4JcOQr-5$Z{o(DX@2)s$blgDLzh5%Dp!I&e> zEfd8zcGmUz?zY1RCzsa!6XS~DqSi)SZ9d6v7GJyRfM6aQh~qz2<)uvvE+Nm+^v=y} z^A@?ueD?7$y{3Jd`IxTxn9brBhqQ*ICF1aW3|7+V#tf&N46gZ<`!K7czgYEl}u{AXlq(Z%lrqDmhi z+6~ZM(9rvLqbmYcv^!#BR~~*c&R*?fe~sgT?nNryqpZ?7u}wHlb|)LyNRsFdz7 z$2mam!85x=!GgabrXP>wSlw+D?vf+b53J6A< zg_%LR375v&3RoVvG?$un8IrBeh8(0)IT8~|3`2^A(-)xA*s~aFAzkVfzd{q8SbfT3 z#dlg-uf>_3*2cC)*dqmVDvQsZ+3$k6VUr3ysU-kyVRt=`a-VY2Knu`YZnF+&m)ILWaN@~${#kXvhO-$vEv^|EX_)YsKe z;~5dY-K@6?JiR?wkYy!Ga;xBB6W!yrPtKM|oxM}Bd)kKFwxm}jxD6|J0wsgD@%B`_ zfx&)6Y}p*I9GIV;VIw;5a_Zx;;sq4Ytla~#ugyI+6|vlA^Au_r)4W+xV!d>4J0#Z_ zyJOY$vaV(4nR!Dg(yk1{0UZbGJ(wAb)*8#HRhA0;S6;An5_a~KK4HVPKR;+~AzchH zP($#y>8DV_%hE%vK2jiWQE|;+S*2P;^g`kKM(njKP6}5NHT3V zS=^t0`%{BV_)@S(Vl^8~t0hF)CugaN?Nwok38vLy4o6By1qf{)eSYpmGZc(MLti_` zXO(S0z@p%kvIWsOmvTW^nhRZ8%@DanX$j6N6VTGHMVUa_$5L&JJr}fNp&IG9#7`o< zUVf?}gPKKU?Y;AvD47bWB_`{2T>ud!lPU!SjkOC#hi-$(>Piz$>H?T$9%X8hBg}w8 zZXHnHXfp(v7J;!A<^^0GNYGPQX`Yji&onv9uJbrZifLLf>#1p)tMp`#pIV=$#L!a= zWVg@K#I(R(UCH7vEr~3oZuRoyT>5t9snvQz4;)6OT;L#?9;VZ>Fk_;kF-|<5p+B&W zZ&>0}gSd~EnFU}dNBi+2&!)|3)d=!xim}W&yclLydip7ABE?eNB~ON2`Kmj254BYh zu9b!WhZ^)=gdK3#^$!nOx7b|sz@j0{Op|RMGXw)_2rVJH!Mp0{nr3q@0+ZUmH4cq| zf=-CA{B&T3lZ90weVW<$V;;RNXP%&PwBf_lVnPgwYRLRbYLdEY%rf2_&%cA3V#W7E zf+dxM&#k6`bhRmk$R%`ShOv2pV8YoE9zW+!J*QgK%iCd(*i{HzS&__*Y>)-@iKz>{2bZ~C)asbz;F)l)y%*(6pN_l=! z%UfRUQ=8_Mwhvw|1USs})fAtgHjGQN{(F97YYCaG&n9K%jcIcd0-UFj5f}jp7&!-7Pg}=A#4-$e4Ne0a{yag zq6}Um(m4vtz>oXn06tw008!=h^8gSN+5ocGp3G-xuaX69JGE-|QLX@3;zm_^b108N zY(5w-`iJzU3u0b61#9&nO@z_6-C_)Yd4`5uT>O$%04;m^X)JQ>OEAagd?u4tPGib{ zhSzlGSLAEZ1dR=K;3ucJr|x$zQQ;@Hl1p2tJ3pMy1j~)1Iwuya<6-qL&C;B_480Pr zq9n3m^`cA+^B9U>QgQ7B!ERF8U04lm20yQMOGEa&ZX3k3(Qc$H#iFipdtR>MVkl0g z%^>qtv@pGPNDy;owe6UK!}(qC?8}y&={48MV4gOEU0C(F3#;0!cfWr_G_Omx6ll{W zTYRis3v_G&ZLv%mn8|0aKdsOAg1;`f*PIh>Rzv%}G`Bgk&{&-EK!YnAE5Sd@=KERf zRjxvKq|D_sFvW!r2U8~J1i3~Vg0^n(NPX6W$|Vh;o!A~ogKK84Y4bk?G4taCYQE>6 z0@w4IJBXSmTn!QE?vtSNu9yV@;G&%p}1vb~t zFLO!2cY1=K7~M zSGm#eAQ9OCa#}ioY>VZSagl`2)NsSobJ6MvW+6K=F}#WvqceMurT;|))y{%D1Ve?7 zqvilBQ%~~lP7W}?fWYU%m&LJ=9ld}(y3G(&6-+5)Ag@9Pupxpn93{&_eAJ2aOlh7= z+T`UalX?!uH|g_1FC$B`wtATx6j^w7Rqxkj;hEL-71?KPu&G(CAI6$ksacT29V?iH+OGJw;Whcxnm=``!<#Re_l{IeeI-nIJF%)&X=H$1qcCj`6}jRgmZO&?pB zdScOoPh?*D*31(QWrA6s=B`OCcnpIlmS{MsXIzKF9)VDb_}PYAKK;4%i3M zJxty)su4ad?Ga2#{G-Vp#Qa@$ANOe8qpIk8i5@aSvfxY_Ve%BdJLsrEoZ4W{`8E*r znEmXDOt0umf3djmO+pW9FgHT{#dr@+>t%WOYP6YOb^pAs^Xa8=5AxnO5Bs>E%f;b6Vh=|d_Yh*r zyh;Adj#DU_XaUP!kF*&~y^#lD7=-X;9S!#=^*SHoJSm!5#hK6koMpIKO8{yUEav$p6qj6U(SJiWA)Ohpg2iEcdl z>N9Iu=N7q_!+XF1kQwm-Km)oBAHs3lO;2!a#pT3YV$E4YP)484IdU*x*vJ8dlM0zI~nZ$9R{_B*K?7w0sGSA4YTljAE}iKb3w za_}e!QQDU_-<)1Y;u@$0SXK_v+C4LGSljv$R3#%c#Yn^}b|RK-$s z}fi6qCOv*=+P0ML9StjKK<<=8kkF&BClh5$W z*0`1SR&i&@Wr}pONz(jJe)IxPLrSc~qxe;&IS4ud35r>V^!p)Z9b!e87{JqjS0y=_ zd8?|QBJGs1S!@y$ovG)kjF_x)IN3?b^OP!Fv8a(u0-1!t!fqtV{0~#cIr33m#8fZ! zVM^b(XM{$}{%#Y2J01}BKh!~FgXF*CrZ%bhXRSumck6kg8@R)5uZjTq4-8br-1G>B6V;Yw^X;mYEY z+r(S8S(DSyeaXa^@2CS?2{~OL-5UO6{0u_0h<^z>xdI4%-VSvM*9SUdXP{hd!mVM; zNxTAYrX9~~#V5O<;iOS7>8U4YtX6AqXpwxlgG_i84ylD_iGRv+J4dX1OKq~3Ja+Xm zB$1MwEZq;0@5kEnRlU?*&q0|>j!WQ^w8IjRSz^>83aRs}HRdh6oFpIlVe@*B93(4j z0`KDX{gkE>yrtC-r8?mD(e_dlBk8ywnk3arwC%1mhxbl%o^)G#m(tPpXN*!;Rx`-e zy0zW+nMIjW+yXd%U#Zs%a4E?A`K~ zlaJ%?BxU;;ApYcX=+IFbsh;4!=vvt&f90*}Lp@nN%C()er?@vwScZhOrsPFN12bWuGB%LIdph-EzOS65h1Azv%9Ju~ zGkTHqP^M4w={|09;Gl&$ZDR z;k%a6__#!TG{Ca)l#BEhCyj8z@{u0S=woC)`p)x_O<*$g=sQc(UZcGz$@l2w;U6Ve z=ZM>1e3vfhE2HvbKT3Y5;i^W;j=bI>m)a!p_&X~SeWt#x9i;#;T6ePJrR#c4xWCP3@(fH0hI;NHsTdn;#jowoh(pk2z(1 zc)@IwXs4s4?rg_8O;0|81iw>?&cMgX*1^sD!PY!rWxQZBzmS)wJm zqdm8C#z)yO;(}{hwX67x{(1-4xt`s@Az6pA5~NMD@ijTyNkN=1s6|Di>|LKRsHHEN zt(w)ceKwGM{D;j=Iz$%d96a8nj22v6ckOo-JLm)FsMW)0&1v$Ur;L?cz1+f{p`OX@5pNbg%;frH z>;XC44_^&e_PFZmvjEjdNfD+AgB9k;|XvJ{c}9M&HBN*dpyes{%uKMfCs%Ta>2=gJ4q zL8D|~4r(>Wk0V;AC3J~8$kNafiFlhFedj4q*wDArL5;I@asgkbgQ?{Jp7vh!U(e~Mzm^`A-Vn({XbfJ?R-i;&>j}w zG#(!(|NO|T^^yGe+Mde!q0u60vE(C}b(Xh@&IaB~%Sqc^K$7_;GV+tn-&l>gN@~r+ z#Ys-)wxwvNCi(E*Wj{!nZ=3d>Q{=p~OA2<6Kbo0pY;=v6oX#XJTd(MprMJ!Hmg8{6 zWh7gtT7jM|bofg07#i^YB)RRKuIl(Il}58@BWuk~n%9?2HE*6$A33bnbv$3cSOjMl zITN;44zz-xnMPa*kv-#bC*F&zvP{%}9>eEWpH|Fdb=nN2L(V$qORk%+-aAIw3(U(T zkJIGte2INIIV$do?pdR8H|Knw>8|FU@$lhb6Xi#2!XDK<%-=u$q+^q-Qu5{U` zp4B}+rw#hz#)piPOSG8m7WHDy%cTWuZRxn;RU?(8HI7VPwCX1sVqfKPIB>+fAbs z`%?U>1kXd#Eqf6@e>}e$+CE~b#p1l{5_NxP<6d$$pQ9G<+qHIsRqRJ8TU^T4VLP#W z4obcH)gqGZq^ug{^?%aEs9Mfj9$GUN#6e)y> zg6#b?^9SkuYz8D;YxiqUQF1nKN#O_7V0OGG19ke=%#8PU#o1nSaWbX~qr<>ygJA?cT-n>_6kMZVIW^rz!ae>$#|=V`tC zT3*uiQ&90CsEK*d9wTb4n1*m!;h3AX*!6<=%BAl_#d!nRw`6gjpaxp8P%jcEBac)@ zN6QUKN@JPH9eGWsElTydd2-NZ=2`N3DvVgBf#_wbzxozhtY=Amp^II$5$OSq)WH(y z^Z8%WBRNZYlxNZ-eJu#OX`k%1b+B1=9I25q@)CMYH#%ntF?t4$Une}bz<&xk@HO~G z0$0b|bI5W#tk|~vB?&cZroTUJf0KO~ezGsiHyK}p!u*uQrLR?pJG#q+gP6 zwI0$6tXfJfmRuuglHS%ztZXQ_c8%n~wUw~Ug4lNgte~;Wm+?!&&XPiSNjdh(RLWCG z+TN*VOf89#HKQS}q$JmQ`D;dn(-p?4arG@(aH>HpYr;}v4fN-I)3h}jlzq#*PB{ss zf~)^ZLul(gV3zp?yKzJ#3pePJ5Y7D91S<8ke;yu^@k+MjrSzG|jK@#iwNar>qBNYZWzH(^qQs>l*e;%z^N}8Wx&z z!C$V~N?pte$l+M7bVgRPLSD9fZKdBRnG)wkyX#8v=>j}Ao2UQ5v?duHNuSe@3`x>s z`N_3EWjs+j=`Zia=#8OT@-fSQHcmQ1Wx|% zw*XDFd=xz$OneCzzZ z2fzMK;hQ48D2KKVoB1r9m)6_cHjccRhNior^QciKJ{%%f)#485I2`IFE_huJ5wE{< zU>t^)IZ__s*Ll;Pn`-~=!K<|Zk4_h?fay?eQ-o!_n8rpf8u%!`!dm|*Hh4{T@insEJH}fZcLZ-6b#K)8 zA?RsJlU1bg$Mu$J&6{HU!;}}TuFPUUi(VTs9wTLLd{5zdYZ!bR&s5@W8#o^;3s5D> zYIcoItc|KlyKBDm{z!%6%3APs$*6rvZF4G5Et_%;jkE_=T82(#xD_v3l=L5nWoUoW z?Np6TTJy|iPT<+RPUQ&hI|e?_A@0tfVkA7xyz4Brl24j%7LIEy(s{-XeB;03<4UW~ zKRRpWIf!3cGhZivxjC$BM7!tX78BF8A!`*v-VOxbyztmpFXvx#Y&kbD=PB>e;-CBqcuyMNuuJsteicSrqWt~?*^t4 zmU3RZ`kJrAl{ss$tqixyw+~jqvL$qEd8`CBBsySjhiVyB}&sU?#E`Y`-T5fi!+7pTA@(t!6_e*cUVr*!Z-EQiiP1}^MA_R_c8t#`Cqbxtv@1RQfd9G-lSL@9xe9tbrp-n z9tDQG`mE^TVd92|$H#yU50CXGUASX?Nwrz ziRdbCFOqqY|J7n|Ri#y{1WMgWVQ6TiucvaXapf7}-Yj(WGy9O|nuMy3Uz3!250sm~ROlK^3g)>c0XQ^TD2(2a7~qDaYoO2t z@+ipteZ^tN-&ZRJ{89-n4Uby--zDD&?(+=025Nnq;7_s}#`s zV_X+)+oj??c}Y0Is|x&Xp=-FWd%SDB%YoxvFsr}6Uu{Qyif~}OtGlmQ73+`-7}ET1 zKm3Hr{8Q6z|AM*w$O3Tyk)C2|qAg{7!FQSG8I>Mi$j!oIQUdN^Uw5^<^Of?>YnA4| z?@AIPsq!muacs=$HAcP44-zhpl}3xj(hbQCh3)~-1Od$#gpXLsZGEy->naSF#3uzW zB7r7et*4d8;|6@m+^?B?FK(svg?_kIZr#MceSO6dGCMtve3(3(5EmNzVW^d|?Rq2>Ds2B@Z<{ zTeW>v39HoQ1^(^p>#8*0sI=~$aQS$Lh>oiSL06?Qv znr~KGlVkjJ*OKubQb5}nVmyY95dVp_<^Y9Yv07Q6*#`$DPlF~%%`fSvRNj7(n)Z*E zMoOp0OLXDVc;7&O|9EBlD<(e@o$cQ)5wsrDBkpZ~#g#;l>x3SvP3O6`udly7RY)NT z`4Sv9K_oN&sqWj~Dpg0-I>gfKUs98PiR|Tyu%{;a27?*^#6Qc9U+>U(EY+wW^u&P_ z+)IBPDM;H4NYf9{dn&D88t$ugmBwj2%2qF`8k5R$w_hLX>#34Y5ih7w=+n+1aYD!fiHBup+D>b$KoBShOlzQy0 zv|b)C1jMTKGGbjYHU7JW9`s_R`Q~^JjB|}HjZ7mBL(T%SOq1{kVF!X!I%B z9Va`)a@ew$l9t9Um4>LB2h`k%IT+jjO#NW{IpJ1XzpE~={d>bUL~s9orS_EyMwi1nujeAdMec6;*>kB1m*6+9u{tiB5Luyyu_LXw$MXA}+_#iwP?C zRY^WrDka?oJXORBQ3fJ`YBrG(X^+oQmvZwZ?{HR3xphqXr8sCYUomNHzF2ZS`jYhQ z@Sp_r%Oi!;qx@LuujV|@S?_<9?q01lU$xR+btKbHMJn7Zb<34D+WtfJrEx|Y>u4{h z`nA8W8^%3feteKJ2W<=+HjO_#I5^tfZF>HO;tgH>1BN#k^!EWW6%P*;3N#lAf zQhDdOa_fo)I-(IkdLScAp^B}7m|htx?-b~ej53f3DGUshRoCZeT1*RfAq=7* zttur-uFdaLPip9%eN?0xMt4RI5GaoUvWp>@;k#I>QXd8d$G=GTQo4)ap|rtQ0QF2m z%fp{aUmy9G4^cx3mBe6}(OpifraS%S4k0tLy=r}BCq_ojE~lzV8fML2gZj> zVkc#nx)?VRSxZ<(2bVzDInWnyyL#!FsX`wnSSZzs)~RluLl#bgeiig zkY8bVyh|p^HOVyv+Ozz4|Bv^T$9e{bVaoHB=4<80E3Nxz^4IjA2C(1gmx#3f$zZKZ zowap*jNVF{5ReZYgAeMZBY#UxC))*SERJ-QXsqf;0UCx)SvFsTtK|BMiPtMM{@4I| zlRpd*Wo**IXjOmv5{4TZMZM`u(zYnA>Hx+`Rh@T~X29kUj4D;f%G(Dh!R!&-;o3fQ zRBuoGBSYdQtHCePiwkh_w(`y$<(`JZz0 zi}F;lg0UnpHyHCPty|5Ax-!vMsYdhYP*0C(nDWlC{^&ufj}`lA2sffM!u)PQ2(`kE zJ9o*@73rnHqzCT((SZIB^!I^0(o-6MoF43?ksc&687bV}cTMFO)Ks>=HbRzYjIz^u z3<&=eg^~W9%EihtQz|{6AQ93)8nkG!Nm=LR?OO^m7c`bt`p9GhUSe+Uptey*@N}$N z-oCEfH&B(ua#6J(L}1NEYkuDp#+$}brYv13`-RFe9}O4~D#sWJD+|L6f>@=kGUZFY zxBnSk4aDYmE5}fMAn*tAAkOVCX-joYZ;> zR>|f78aPlK#^fHOaIoChKi;!%-#%-k{{HgL*kE7b`hHZvNKgMre{rPHp9YZJv7$_6 z#@~<75es7o8kMiK{!K4tl>HP5b<;D;JEzgVn7qTX?OI>;e;Uwi0L2{~Ep*+GFuBoR z89lA9LQ=VPb9Zm0b%#15L&_bZa_1@Ol`Y2Er=-)j%3I31by&}FZJ zQE7ho0}X#2#-Sp6_-D8WRnE>gG>~EAA{Ov!7z?!zIjokfS8K8{?jSmArS$;+Br_V4 z7;9ipwKUp?lv(V#aWUasv6o&h9C%{m7n$J~u(Kv!xHS1l0CVJmFVO}#;yc4YAM zD3iwo@fW47p5f5GgS6Z)6$a^a9Pdq_GlCiK&+)>y7vJ(ZjxIz560TGxM*ASWUB; z5`IxbRqOke?Q_^|n7-I87b+J==xh9LpAz6=<)Q|><`t=_?F+q{OO4qKbbEjAfYlrs zU=?9}cpQR%3(|Tk+xzLNzlHi{++tKAa9q;_)B_6)!xAha_y_A)TNpDc&>&+a+1cB- zntP~(*zcTWL4v+BjE`m*8e;NBab1<2-%$H8YeX5TI`9|eeTAl@3CC=HCFO$#yBWPd zCdF@K?#sl|Da0iciGU>GrouzyNFwHw_J_WPHBl|9pY422?z z?V1!K7U|95_n<^b7|LOLzqvJYZ#LJ8ZT;Ck`jBKDhE>tr19GYZax`es7h4+9Aj*gz z?JG&?Y9?S|7IVn_K`W^>CM75RD&Wqy%>CwQ-#}&On=HP-#&78dT##U3A6-#ZV<7Cq zAQ&$7_E%Y}kq$*>88cY7QI#uwLkwLEsZ_330uAA&F>O7fmqBd6Vy)&x(jABKAGF3% z?+HB|ipg@gw^-gDBt7N>f>f+xPcT#KvM$ePYzk`olUA!Yu&XNDZ=f8?+b?JmfS;+3 z1rZ|+Z{?kzwvo2Ccia@!_UA0+bJTGhy+aQsjL()A$Xg}IE|cI$$zU;SQaw@{88|&Y zU{h`i`>d;bt<(7M+T%0q0cTCsn(v#81I#_X#h@y~fmE=ui<*BE=r^gVS`~#u;{kkK zA%xJp)ynoCl=_%?6soSrxBmb}^;_L{_Dech{lWOUnUVBX+0m_`N~@oqfQN-TY55^! zLqRu>;VdJ@b)>M_dh0qHPChNgu(OHCFa@?*FylW%v>IiAg_(hYfzbgIk~gKSq&U>2 zHAGc*9w@Qc0vCrdnJPP5f!~##pF>&FrT^71w&$OeWU2ig9bCDz{?HWB@A+Kj_ZSXF zs-kO$Rf=+{pJlll5|(>f;@-|19)l2W@kXf)k+&3|t8pMydh)|k+BdbF$bm5*Nf`SR}#e5mx=&tCI`e|E=rKHhvGxj9L? zn@RG8BuRRUID{WLfBbOLop744kbLYHU;Uk@F8t@m{{G*4?}w|&|N7xS|LTAL#s`y) z?gMxKoqzc|zyI@>y8p$=jy|A8k~zW%;X{lc%_d*+6fE1!PtZ+v;`Cz7xIY5jj5`{%F!&L93?Gl%}k z3pXC$dGv2x|F3U;?Y3+G@$X;rFMs9u(GQLH{Qf`uA8)_+cTVZ_5$}()4*#L#PX6lE ziYfLImx0{Linz}3=&aTT`;?c7Sx?ue74PR`nlE0kwQ-$XyudDyP9xMv6VKcFdUlO< z?FMj#bb3PPNVJ+?vsLd|esvC6do4O$Rs&0A>r9exbS6tH^b`2C)8Qv3bV6x8#Kux0 z4fg`~va~;{JuKIg8X}%DT4e5%)8mM{{L>aeVTs#grzTFrv z9mOg|QboS}s+OuS%?5>zS83xwhpu!~NLx4BI?bxBL*LrOy?T}RAp1ztY>E%!K#lYM z**hLOPa3(aT?>)moR4JcHHYkd7AI9i*Np$~v;uo_^C)CRVTjG-8-;2LFG`z-PGH=| zs@cX@Jb+tEZl#G&1G^29^n8uBlB2M^T~+oZ>sa!MNLBs{zZj=O$=b};C3LasB&GQ^ z{6$g?=9el&07 z`sUd(upg=Q?n#=z7KzQjK64J~pXRiuG>y8e$aFbA4!_qxP5QZ{L@&0sRdP<6ote{3S8;mN)>1mQrb)DTmav7@%v!X zd`=8;ad-rhbZgqz&s7y(f!)nHImjN}>G-DMexJ#iIvHYBjjY}N%Bh#e#k_fLPQ9rN{PkFMa~=cwRnmCdV2OLyN#x_QDJ#ny}A+4 zpk)fLv-9O8AeP;p@*TAKvE(y;J|*6=bwlz=leoIoQtvIv*omgjajT~qBQqj(&p|Ad zQkLDUm$p1jZ1had@1_wCCAZ`5G75D-xB0(Bv5vBI2DbT_tg%M2q5)ISxT*crUOLJ( ziC-3)x|*{}Iv+KZmpYNy>4v7us~=C-mBmaBqWs8g&vee3<>_(DHt-o;myQ74JFg(S2(XBbb@L?!&g4Sesq<#m{;QS-mjfY&J) z4@0V}yU2M7p!fSI9BP5RjQ7|9)J}gvCZkTAw?6R*sw$hpoW}noTvm}QsSs#jaf4c7 zB+~OY%v|hrw^8o`xXtFnjZP1$uc$}6h_;s|5%nR=&sWvWcz5VIV%9BXWW_trA%6=x z(-%LSoV?2h(qRs`w|?R$VV)T4sxPxF#Mb;FK=qAD6LpRCdU`^Z{8I7>>q)w{r$%s7 zYcDmZ3lEA{XYm}=jXaMU4XpkcbGeL!sjo{8MMp-JORE}BF_P3%{ATltJcTXq z#l%oaQygB;+f+SE6RF-qZQYZ!e);X%OSf`eKRf3=yZ>;N&zJqqtDjHh6ifHG-iR6~ zN|=T|y@jEB3WDuj-@TgZ>QWZ9kCcfdu7>P+?rN@@NwmhiD9ulRqdD*%jl@dTuZkoM zS8DM(YvumPrViUbV47&=9a|RpoTA24*VCh&%1_h2vq;cMeC>9|kFZXtdylQ&nBjS{ zKF&}n4lKbBy-y^;_4bf2w#eSgly9{Jx@HWDW0wZg_Jw{eo{HCTx|7#5YL6M%yO%bJ zt)@MYe5Z7p+Xryn#Mcc?wXSrWuYI_EyhqC(mHG3lGT2qPhTMnI=%+}2b-S=ruN=n` zeF(8pp>vR^yrTUkCQ9Vgrn}jrwr`&XOt4L)4x*7w7fdE-tOi(z_YzCpJ#GG}CQTyV zr>81t&e}oazTnwHiraQhhD2Dym&{Th!PP%@4}-#ZJE8k9IC_0RV~@nx#XFX8au*Hf z(-L>D%Il4$t8FLcP4PW16upfprCY?!S5G234FuA8rBPO!M!HQTGTQq?!#0xk z7W5yjs$!387VmNKeFVwo)%$nU(^s|ak79VWdL>9wKT-nv7KPM7ochKJtJi4yoLBZy zZHNi#Dtf4B3)fc>q*pB%Q=aft5bZ_@pPA0*B0!hJYIH@kbxN&1@I%Cwui zA4zwwoIW{&a)#uT|bA=cJrda!$*+U(Ojh56GF6^OJHuCg(vpXXQL3=V3WK3y^ew zT+SnM&da$V=TSMIkn@NoJBc5Bj*V@OLBOfCFx#~!^<~G_nMqf%UPGhJ1Hfv!|5S~BK-&ArR2^hc<6eACsr#4CJ#?mfXyCmA-M_7(U--p>in;Hj z-9J$9O*u_D|Mjo^_(!`7BqtUWjt?R5guA=sD5kqdPOqFkIVCy$!W)pYPtKs6Avwcx zM&yh_RCigvihS3|w_nZy1>Ym*+7eDxzU$2y2(%#gA^S*wZ z_wU2GxeMnOIX@=k4~Vo|g?F1${J4U*%Q-0L4mlHY4ymBS!aFj6b94yjn1Xkyw7Zq| z9{KK7X&+RI`&8s{6?sCzlOs3}D0Wi8H|6|WrT7#1{#050J2`(Q=LaH<4>2U&Ps!mq zt)!a|7{ToOaEfxe?bBDxUiSpE!xd?mv+;>r$Xk5H)YpPG6u~KtG<5&+|PQIFG%5)@XJbVio`!Wa2fA zz>4#uEzh_s-JZOvBUKAGG*rjQv~HQts4BBT2S+r)(6l0^c#ljz9f#50 z&=V@c)#28gPM!m-c zF}6(vR~)Q0u?X6KnWWmc&Qn2hU-nyTqXXOqz2j0F5SiChZa2&C&2Io{ui{|}jaF`4 zUyNrC9wLL-KkjZ%m_NFuS0Wrya-#jwEZKg^iME%;(^$PRt-)(7RF6~@wEz(#t6J;O zLStN#j}hqnt|sF$&SP?Z;J&`*ksto)m2Mu2Kx4^CP-Ak?U(GL16qwBC$lVi%^!H%xq zrM}5mQOkQ_arFEXi)-Yx zIJY=s-y$p&t}k9&>*C~dv6p{E&gnLPz1TaXJI7B0?&|aV5nWxqm5cm8rMpkh;<>Nb zJJ!`(KBok45MChgct6)?TXs!Umq~s_&ff+8loCB7=Q%mg%lWdL7v;Pp=PPo4Lz%tS z!!3yIvb^5|*t$)=7m6Gu4_AZ;Z(oq}fMVX1)52*bO4#b@>K*A3?7PL@fu3I7j!`bU z7S*yVE?*^b`!1XvrEiTX;*gwUa_*(hgYrB`S`J>zOJUprmvd3h<8o%@EXY}sqZ+oF za-Na%IXRzK4lQUYw_cE=8n<4QqZ+qV zDI&I^kn{TTjmf!Hj_R;|ublhjd{QViIXBBWC}%%T>yPD2OoAP~D&NE8>Ir%;>=L-rxC*SjOUXb%;IWNljx?Ohj ziTR#lUXk-E!5y+Gb8;PrYvWwI1Vdkj^bArBy`zAbg zfdP`(nfT_f;~VBb#z`{NlW-;fX%4k>qf8gzbhaKJHxB*E4B^5kuO)D!sH=zPBf5&_ zmP)!pt$U$d3bRWBT}20RB{&6@25RAAFxOOSc1f5NqgbySPWthvDy76}fQ2<)5arYt%;wPpRmq3k8uZ%&TuFjMnEI-&o z!Oa(p^yUi$bmtJa^+_Y3^+^j{^Z=wuDm?HsBy+!5Wa0vU$>A?4(0tYc&1WsfID0Db_NulWK#B#5g) zWC5d~rdZ@QlmgtDt##W?CjIJ8vEx{-cH{K(YCtYwwmz>`)9+x7TO7@%kOv3%)w&gc zUj#fte3wLrh%1AG!?pe_1=p65Nu{xSgYHInq&HhdKqyea^BQZA_Qmou-i z$LUFd+$2d+pAEUfpM4U0rEm2>xe+Tr7xuk44^Y8?)_is_jcGp1eX`*!5o}pVgCG+0 zf?hG|ME)xxKLv(5y^>Yrm8>H2f}rZ*ZS@Qp3Fal^G@|)z8UfE*3(j#$gyY^6R%RMd zS*4Pb88Zu&P>ITdbUnH){;JVyx0XEs6(e-kG>Yc|Ml)1m0p!t}FXyA1FB5%Djq&al z<-m{9^We)!tviJ&3vPO+;al>NN&xdhBz-^lXsnono-7!x>GgmYtPm?SC^?C_lpK$U zh^=Tn1e{g@kb$0!aR`y(#4T&E1vNtS(YhfenQ+@bpsGtgE)H1c78OHREYA3DIay@c z(kWvL@_x>KDZ>l_NM+~V1L{(pKyO2!>M9f)^9sRSUKu2pR~wMLRDuhQ-Vvyk@kSAUMa-r3VG#$RYXDEQw*YmhJZ+i|6OQ6*Aib0<6DWr z19{2d7^0SF8`a4xB|l+}5d)q%q2{x>C<|m!FXy8a$fB;~qZFuGPu#w!_9SR+orbbV zD+91`46q_zX${-oi%bhtPsFGC?n&#wE2`CnaBuSWrWZKy2 zj|u6$A}SNuJX%IXik5+_`qFG6!{$V0RC$U}1tu>I}#s4sOgJO^&iS+dT1J2w?aQGb0{69n{tjr|HH z>%m`nsCgehFrMN)esRXl6`}Ykq z6`v6XVG9^>Oe-xQCKK~H3w+rFU$(%Da(N#}2Jb5dnf2&dH5FI>d8G>%tMoO&`7xWh z*-v{ko3fb=z4?+;%lkmWYFbInmlPN`bDH_TBYLs+pawH=O=5U1fvE98dy0XD0mu7_ zmGe1ei#B&MUz93nzB=3ogY=@1p}Z<~*#jSn$u^X4#t1pouk7YckL~7ty!E`(HHGiYWt<4bV8I}TW#!zt| z>MgwG!{~@*=4pD067_(`gvh;*y9m98`P>~br7|s%EF(ORYuZ_5fykI?I=J~NFCd9B zSpOX28hla(c*m-0H;O{dga?9#H3=?1u7|8p700=Zt_ExUmWt;x_Hvli z$CEz^ce9(XRW!MRtp8=BaSxyF~ej^j+p$;^3P|Fg$y6*rr9Av+|FAPW$MQ8>PY+(Gyf4RV^2L zSO);lx_nL4A7kr-SO0c4^m1H@EAvXU{QwJPRHEGcKB(mTeXG%%)>v<%_urJ7m1kA^Bss+cQ%uQ6+nC%EZj#x30qH@+38bRvrqD7 z=y$;K@XPr0uhsN00uaKoOf>*onE6;wE>!f$fE*%Yh_xmv_<+~{0Slb-z`1@jf~Gd8 z8i_^gCN&L($dYmIm2p%U+Dgk)JbYtSVUlDsd{CJ3gKCq0u|V@DV+9Ku48)#^e(7P# zPno5ijTarYVt`B`d9By^XG5&{7_7_^j{%zz+lclIYDDAVGx~3h`5H2=S3&v9>Y1L# zd3{uZS{m&U5MnKoDl3ZQrBRVII*yImo%*c@VhnuS^8B_btuQ?oHZM{_t@~1N^VtZN z53sREE>GeXz08;T!OcOI% zVUVb4S;aj}>eZ?mZw3c*GH0pwF>CYo4sn))=c72KvOm^V%%?OEsM2I6?W6V!Fk9LP zt^IQNy<$qF^?EU^0lZd{bTH`W!m3GI_~nl96~b;#F*?#()C~`;@XWs;n*jAEw(5sC zAIju|HW5i(n=fku5xm4OG#An|7t%D_rzAWnN_UDv z?jc)V=g}KR+Yfftk|F8Sq5cFLWqUvW`lTbb$9OT|N+phTZ z^b|6*-7WU@l9qiGLHN4eFuJwIU+WNk&8^}uF9~ntF*aPAp&4Me#O#lf41^ot;YWQqg z-{n=kr$d;=WlAGgP|N^J&NEOsF@sQM%)p{l6RfPN>xb`(h}0|nptW3}Rz;iSr=d2?D;cJ; z%`iim(vfVA=_w@R9ZWUmlF4x3RlvmHHOh&Im5VW|lTkxl*-ZFInX<_s5uaw&`&E=h zd&=iOJdULhIZ1f8-!RGGv0)?w7vbCfoVmy@epW^T=~d`Ke_1sc1YOlIgN_?idVSd* zzC!La?c;%Eq)t0FG6^c%O~v!0g0??vj3*#A2xu}Uq!2L>A{c5MgQueq1TZxC1f*fQ zjzHs#b(Req(-B0s@HoINjoMh(i>aM=$;=*`tdO^2#&Ka>C@Acwi`q$ z@Uh2v&y9?Tu3|{@Q{Ea#c)WS%g?`?z>`Qq5oYW%;k9iWs+hy8*qm1r;g~oe^lU^_k zII4X(%Y%pc$e$EsNi4g3j8FcoU1@kQ=_Q68JN=6R-mx1c*B*;u>j!*r$o8eo`y4d` z3h=po9Gu(P&&D6t{jm8}3V(OLj&81MD$z#;g{rXF0?Uf3qSCwunWAgwHPsowD^52a zgbA=_87hDbzOm1|R_-^51^c(fA?-Ob&_apvSbn$(7;Xwe&PF0c^DE+plYU;A;}1@G z=fzQi;)Ayj05a#TLy0p#3^?Rwezr)##|wWRbeK?7DEZ$*q=pvDOcQPf+4PlALee>21PLp_OxCd zvLdKo>zTncQFWg+ATa(7nLk?ofzz$e#nxg|QL5&-+Z2IG0?Zak`d>_A zY_1SvgbB9-*05LK-qPpQ+Yw_En^M@B1g)yLqtyx#(AT5?G)L>}5!8HE?HWL3!_vF` z0))pjBV-iri`A%CW(njI0H6c_T?*<@mc~d_oHLX(9>a9NYDrVN=!>J!VSQ%1>{d26hRmKI=87%)#@t3yErc_>7YiUcvrLtZ@3(Yi?U=Cd*!MrK0J9I*(TZ9X5xp5+@OipSiggM6)R zw3;@~^P%4K2PcoUO&431j)Sn9W>PJgaw(~`nAJxOz~P~oTkFfA@k3LKZYy4LVIDn| zYjU9vR|LH=tR z)ckVH;#k^r_+0G92SYy?Rd)|bzY_79_{3MO!#nekA^Z*)xsP>XtTHogVQCboxaQZ;W~Z1~V|6CrNt-5A!q!_YZ$>?Ft%~ORjnOKm z4YtAd)BOJ)BuUvTXtLM|k#!GFg^plzrVL4@lYv4ud6bZfr%b6qn28J}s%-2{F{Fx7 zQRB1;;`#X))&CYJDMAlB2e0G9!j9Kf|^9%bdJR4KZO@h5|$=)FOu zJfm-Lu$l-(F#p40g)NREl((+qGe?T>lHq9@6O%}y^e2tTk{I!{Ps~gvUDgJr7z|~4 zyJ9_y*;#;0g1MrFe(ZNvK>ShPGD--&Dzo!u{@*H|DzS_e>0E`H&w3~f6uet+T?o@< zA*zFg*sEGZ+Hgi~hKy9J7>*O*gbi6+n4gwOl{Pkvb<^@zv7GXpmXj*p>VRA+Z}}LY zn#ZyN7eZx4c?l|{49%Y}3-c(H71Wk7b$^g>CsQrLVxP>MOa^nxJQyUVDzFY+=_()D zimszQWSV9KeW8qosn3_B(iPxa5gv$R4ugA4OXp@cSW3Whp;hd(1)PWw7D#peA`h{H z2W=2Ld4MQdZwgZA$uba(a?rIw7r1RI1HKb(25LCeQQpe%qrtxmKbZRtuLkio23y01 z0|;^9CFw+A5*LCr(vO1o`y%N>hlIN%{S79Vw!J+Va`S& zJ|m@kHC^KzXTlc{ZXRI2s&!Xf(O_riu0b|z!!pPAOO;w*w(3EUg^zv~>{@rpr#W)# ztC~jh6$E}NJ8$}P(K`>o*avK(f@iB+$FVz^L%PoQZ7x-&x{t5fLAh;Z8Gv~(qXL^< z8Y>YJ+W}*9k6G3T^K$mHwTi>moqmv$r}@FH_W5!^O*HUgdI?PC^0g9SPDa#1wANGg zT6~DUIYi$aqFE~>`e2OaHF--7nB2xVN6fkA5*w|*j&GR%LxecJ+I1`6OS_d5%cat- z;jA*CV)v~HTjIB}s`N!m^$Nf$VfM`o`m$V(!^*u#+@SfMCK*R01bj&@b6xqrX70VX zEYzF-CX$Sq?-oH>Y6Xu&BDd<02u_7_ApFqHEczStd-kU|NMUK7G4~the$L!4oBN_% zw&O(RD+ZZWj-pM+7fOJehT_C|qGJa6ip5%H961o?tKB7S+$+cDaoG%n=3)N#z&zfx zhe)=rh54G0INA@=0*Tm77W8`tz0H!oVURcFXM$pcTHy1ztW%l) z1zgspl%6vI_*-9vmI3qqAypfr^{o%s4;L^ zGxfC%uF70kSYN%|D1cWqyt@iXb>gAZ7f!CO*N?BQ9gH_>p5#gm84T~ge|>7XetC8M ziBpS>wWX=8hX^Lz2H-w`GO^O$TSyLst2w9Er!Ft9%uk$LU0z=0#xPe)d+IB9JjxyB z>%me{X^zJmjr#KRlJ0^YC?xyN*Vmt1oT*PuFV(v*tZ&vGdTzZwS6`o8nO=P=2c2wO zSYKRT&f$uNvx_t9tBuvUjT+TiTV1!?JV|o2kWBoezgycgcA|ECX{olpIKQybsIAu< z&`_V9NRrmC7LwKrlPeqb^_8im+T!xsQhm9;vSC@))~05jn3}KGR#rD^%k`<1joRGm zdTn-by*{&1o8cPM=CW~nqIPbnKGmq#W>*c@>QY~s0bsaGTNAZ`bL*>5F3#2)wW*nz zdZSU>SgoBoHF8(R}Y149GO-KDMCqmBA(?ZVbtz44); zfsQ6PvAD53wRRQosi}>r-BIhCjfGv%`&U;sb^*@>;&w$$Qhky5=*r?#wZ`Te&9G5h zY*68bT5Gm8y;Ym7&rNYrYod0(Uau`|Y^*gtbmyH@PfSfLH|}g~H8$$Yvo1y}t1C#; zED~!BUPef3i!1PY+1i12Di@ML-Tpdqc;c=jM-CtI*0%d#w;#Lr$dUTdnWIxv_uNyT zzW2!Vk-6!kM`u2G^vL144<0!>qg!CJv-P`8&_+}7_(%UuO=6lP#|p{NBlWe_#^T26 z`qrcCOE*PY=NC5?Hm4_MR+sObUENrzZ``@=SJF5aQ%D9LbDHm&fSkcX(ig&firl&# z{l;$7B+Y+NNS0~B+Qve?7CAa`roM3!me-%!n53$;)hFxgORH0}2*$$Hh6%|UKkM_G zl1FFEbbSLst*zD9jW-gosg>EUc8simb=zu4B8DAJ_|Pc2T(udFsU7H1}B2hM-$ z{DYGx4jnpt_l4np>Uh5~_kFRZp;f$=cU+!YU!iw5?)=$9Kc|X?#z~UquN0Ez-?~6G zB#gDyx!RLcOPlrD%+!kd$aKBdP}iA(7Mg#tF{4`1(P~PvW_R@HR8lq6V@bV0y=Y^7 za|YJe_$Lm1;FQbF2SR4GxyAa@Y;AdJO9gOAaCsT|npYbDIMxKU23bb*tl`>!6S!`-2I_P>Yv`EUp5|HNjvK7$<^7q6yeZMZo;v6Metr# z^HjYtv%a_ng`v%;lbQi(Z%obAO=Q=gYH?%nN$68Aow&b{99M_Be`u`0;T48cGcWYugp-T z^>dNiw+28By`+eP^stTE%I5NPef<+^#Kyr|XBvZBORH7~Yv{>y8|&WYw~;=QIbTTr zYS2n27pBNM1m3+1oHZN8a^;-Z`U|-znslq6A1frEytcShU!On&={bi9F%_cdql;C`E~xIHo;*AVY04fE2x#*wx@ck1Djv!QdS zH=JiSrCE-;01uq;Fjgr>CyCY>@(I)k0dbtQ6h*DFoUpiZd?R$jj#y>{rt>z|SC=rv z_KK~qcaTsmB-beC={gyEH+9;q+Y(VOs%+zS`f#P@ykmFM|?N0)QYIkUU7v$M0a zyED58CdyhEr*8%Q5z3~P>eo07MR>SiiqV{FtxNnx}3l8jxDt z*EDscSO2?{?)%4Xih`+SKQDYL*-}|32TOS$(IY#SJ=~ z9s2XtxjM~?f;+T)J}S9&P~(+tc1DzRv7f8;Yo8>?U*o>-(#`I$dG`)?diTiPzqLo^ zp(ecoYIW(|aQfGOjJy~2N84RD{+KY>;7|Kn&i$Wc*BSUQKzCq3r__P3BdZTOsBvk? z*`0NUFOBIw{O!bsBh&R9M}{8^7}X_l(U>`bW5+ox51p{}ZrG%8dEZQ5S@oBhwa8=U z*65^}kK+1j|FJ>CXjkV3Ms=-HOaWzQNAj0ji16(Xau5ZZNxNCGcl1xd`pW@erUYw#`{ z!JwuP`|*CnH$wDAI%d^{h{X3Sr00jX2mVoVqbYH?muwQK<9%5GW z5!mZCen%kx*7*GzdAa@oIHchPxRc1|A@b2fyduE$#JjH%<|DuFs|rzEM~G#J>yGbD ze-z?RgzNa$9q%6_?m^@a<2amWAjC|RPY1vI1CMP;YZ>tNM%wwvuOrIZ9eFQBnhg=J zI)3K^e<$EL8t;C`??L!|5Wm+W?YoGZi076lXK$oC1!5T!r8PZS1yLrG-1Npci{Ya#FA2hrL+`mHk zzDAng;(HL%EJ8YS@vSb(J{iw(*w?dJUuoYr-i=JPH~nbrX~l=Z@O`ovDF#VM_3nq4 zd^CX`KIni>@*zQda0(x^VvTdB_&aYN&U={+#b4e~jyJo~6HSmrua74%0ziKkeLkXp zBL9&PJ{+C5M&|v=CP+H~m&e6Q97O`H`UAua|M;x8{y zph}&OUeQyg&Ii44b;{>V$vcZL5dHw|$f?Vqj3;vebH;cng1q}LAA_YQePO=f_F8`c z$T>mU5s>Mk&LFkHy4?myrI!oFJ9P8T!+PMWil;`5M0&h$oqSf|U_@=j2US=DYd(){ zcIozROaPf3Rk?Zqc)&os<|BI+fYRnlO8=|5`XQm82LnCcVNl+evFxNxE_gBkT2urm zJ1CX+!{+0m81x}Pb;^T&F0T$kF?iERrY3K!&WD@op;*S!FB6czY@>4xh>A&{&W4Xj zu`U<&^OK&0q_RXk%d2IdlD#?|4H?I0*`^@@SPOx{7AihlQa-wR2f+2f8#6`&tO9jLo6HWR;7I}?;pxFvmH^|s7EQ&vRLJ?Ld~cP)*}G_X(+PdcFRYfn5fa^ z0X`1mY-LSD-kh{-W79H)!b<8=WyPaRjgvV~?l*pbZrSYWj7!skU}sIsz_ z;Z}x^dSh$|{dw5GlQ1g>+=DW`Ws$>pjzDsw{+;AxCgf&SF-Zp``I!_`uh4Kr`}6ZK zQI94wl8O_bAdyz(3ECHo$)_T6$G{9RN#Ye|*tQr#^!r?3m8dPReKOaNM=}-i><22# zN?yg>mjhU{e4czjD)YA$B%Z7hKLHSU`{Qi!H3mA(%6}Xn#ph#>n8#(FHxrEk*rGfj z^8iz#B3AtXisq`GA#10d(aJwn5En$LP>{iB(?g{J3v(K`%fb&)s3=ERHZnW*#rUp5 znakwsWPFrZs;r%i0A_Z7fgj^EDFkA^-R4a^2H)oY~u=;==+IOeYaK9(R0Hr8Lu4v;LL z=zJtvftAYxs2|j``a|Fe=pN(~|IEOO+!3f0rceOaXZ}`=%hXfqD30QDXU2}37wh}F z;8Ct@PD3?*mwQ?DzBU=rH250uhn9Y=1lM#2Cff2yCB$SRfk2&1o*riz=a zpl-?21O1TCcu6`l7N}NnlCO&+2LWt4FdZO;wiTJX+?72NqVAY6!hVq-Ls|$|9*s zQigdmOjVVlR0b&Z3rLft@rT77g-Bghsh1rIu1fvWbP7bopU0FoDwzpcakD}`JX{u- zb?Y%kYBOagrO99)>cX>8U{>nnUF!|S5Jc|$`H^MGZ7&=U4V70`UFDfYMJocBP8pa? zm5+&Iu1v0%vctTGqDspo|HtB#iSW)mm-#u_^h3$@&_Yc?t!;>ek~1%qjXz{@!HPop z{J9gAXIY{UO_N8Fe0T!Od)97cYO|D*px#=c=CQ7?NnkgK=ZDMb%(Tu1O1tEc$8#%|<_AW?D{}`<`(C zVELyH-kbQTPgBJrd}>mL4>LGxEZoDgfSiWE%&uVoYf%O!qdgh`d={aBnzCypQUO}U z3~~5l-Fi^BtntA3%T3p?v|DWWv5%ZI$83R*8k#W7)HFAPk)^h+wy_CY+~QGl^2k9j zmV{lUmd41TW>{=C*3;}~f<;43bDg=cg;d&MvZX7G#jIdYW(lK8Q&>F0DiOAeI9ovz z)``Y!Zfb0axB9X*w3fz*e60Nl~mTAa_04gJ1 zwqw?1N>@zLQrpZ5hOn>;)6!(q4=YVG*v;DNqefN6)Hv8e38M@1ah3Tn1D7)Z1#-&r zaf$F%OXJs8(t-s5M3U?x^>mRJjtVgYpj6{(2y1Edn`kCA z9ngeLuQdmZ7@^q`$Z!A9h8L@>25H$!!5 zbT3O&LKL~;wdDfvr(|%88qt$8VP=+d?Bvu^OQX&prTxFA9oiroE$o6d>JCEbR2~DU z5j}k~lo=%MTgJ|E+g4*SnIDuU(p+k`!vj6gpx9hkqJzx&7RL7aCR}kavAt3|-Nb zyRhkZSNLd@dk6OcV>7mTl}yHAL;+hHn2yWlvE2IMb)awA-bCNPx}Ea3^}YQSxn>FjIbPrS?(J995=fqzohe1z_Qp zkPKk*#(gyn^-&qdx|;fymZ)`eUC1K12ykOycNi^=HvMc2VDZfI$J!ji$`sB7nytV| zvWt%7Gb6W6rbGG}336AWeuPR6N8$ zM=cF^jnQB$nS+&big{2H$8t+i8d@_8%1ul1MUW6f61Z#eA2$7iG<`6no`(=MNzmPT_~Kyy_|m2PG&p{~iD zs=kSpz8wtf(PnClwgDp=z~c&zKnuW&qtRs;poPgSqoq*~u2d*DIwX>{w)918ANGqRUz;e!g6x*P*28#tRv zOG8uE1<*_lnPZTowh8K3Zvi?R-FaM@KWVlaY+|kh)>HY&Y4(S9gWgU@ZUp*zDtDp& zs4ut*sg;;}Rde zOq5-TtT@6Hm^_W7WqGSyuJ%v3)wAo9Edo&fkS{kjSK2}o?zG*6f zuWyh3oK+NviFTkEI%9{%;t!nNJcf7Ut*xxS}K#2=s~8Kt8|+oqL6NWqlf}Zj$>FP?x$j z77A9Zb;+JJjIo+x3dKcMuz(OeZs0dqs*9^|hL4vWQ(4l*0S_g*9sdE6deXsVsvbv8 zqsBL>3y``dEsb^`Ak0F=TB-#m35do5T1mF{DjeWHrLLxz_S>bx5m+E`HSltSgb(2X z-M6na!~hj!s!CmR89}5%gND}HC_$;dZ*6C;>U2>Jze}Y-<0+@(D?KeGdHxZaEBkCj zZ!t;oPy+^17gVnBlFBMEqn1VoH8}1t)QKd)vBcE0f{2A=Fow2dpcVmr+lLY4gq1>7 zIj{j!mXq^fa#>rEJVIIWqf;s+xI-6c(?9i>I9*cZ)scc6lqgePYAY+X(Ex2LqcPg- zE@;LT4aL8F+`bdQsPYGpM&)LxOsf^R9{ydWZA&WuKyb9=-3Fk}+?UI4W6a@yNm*r@ zpCCZF;{H7`2EoU&OJ}IDW_l1;rCe@cW+wgyilEsgJ0 zBT$umWqVOv~GZj(;(x@{Ek{0s~%4$Tx zASxOo3-(OZRh!e`VTat$^g`+y4F<@bp(<2VB!f^xYe0Ot%6Sy5(#&{}_7OFzX_=14 zqEJo6Lajpu)|}F*6VSAQ>d7?+H*XB!w5^RWW>l4_)Q=S(nESLeM@i0B(&#vv6=EofM(6{WYgH!cTvlxzbErn(a@43?8z6sMZpo5# zKuata15FOv4#4>>H~`*&-5A({hl8M*%mK<+(#tZnOh=!GHf5P8(b6`6L@A$3X(391 z#}1XdDwJDlp`1NNZnBrVY&FlNk=&J;EtTUJAlSZa$SvVxDtowSX>=N=kC_TI6S*l% zS%F(Hu$HXrB>^Q5_>~6!&@z-Lo3jGKj3iAQNy%2A;JEUMmr{l!G467~Dij+9_yn~; zaV?D*%G`DIkqXpXp0twkMm!avG zPqIHh(o)T%U`q&VZireMOEk*IQEmb8(Wvn2Z?ANU*HondrRP804nU(5fX$;3|s4={;p`ny&p)z5h)G6y9zUP<{Re4w_`|L9Ak%scDNXm+}QY-55 z`IRj-KUFPb5|Sm2y!{16aq@(Q)C3(v6SY2*|2p;&M?nx=ye(9?t_N zxED)XaHt^xR+0QudB#ObwjYqi-X>ZaEz2fTRLRPkI(lkWzI$s<=4lg1*1rWDB2BBH zH{)86_*9}09=N!r1wmCPMHRG^h5D9|o4x)v1~&$7BFcV?nMiI48eg|U-C^+jbt@iy z@?Cm{jXqXoE5X=kB>&~B8$v4tm?lU89$okaAANkU4bw3Ws*42uhyNGp=vejWVf$O} zIa(e5BB}X({+QpR2LLAj_S>4_!%u3@nbW>K9*@}C8fkU#H#IGddwhGK$B2=~ZSBrF zILvO_*0^J*1Ovk}h6ZZ~4jkF6xmAyzVPnVI_UO5G+B7-J;gudCvu54*_Rd(f>b#@l zDI4pnHZ~W`%+44Z28|zoV9Mm=TOAguCm@)HtV9;^<1@7a=p9=^$?dO-c(j(T;P|jubZ@*de=xNriyN|U^ zn#+pf=-9&jk#BbHa{B%Et6^bEJU4Iln>`Wiwse;jw>(y5tlv;{Ao|Wa@9jp$ z7lVQ>1qbi4vpZ&GeaB__C42jzY14csOlw^jG;m;Pc#h%(u5szr7Nd5$xn` zt7AQVFtPwV5&y-1z~8%m5&th8iaHv)dEZ92KsVd<3k~sqv(r2Wosp&Rh*Dfs2zdM` zd0$xk_EpidhXoHZ-eul=bLY~l>!%_-tXIyTsSaOKQUYMk;`WVw8&?Fm+pl+=>t;5? zNq6jG?crf-oYvYIsKWyoSHbHSh=Tu!t|%^k^(g0RV$4d*IrtA?{Ey$UL57t7zAwms z_xv&El5+Wa(xIY){8I-)W4EvKS!5=|9|_;A4v$0&-@bbDI2Tdy9)-$(^D^&lvX`Bn zoW+Rv?RWGcbC zTD9b7IHrqSn0|NcD1nPG7;|D{aKI~{VTB(JV*n+%FoJK&tR`yk&EYxOQVkx-nvGP0 zhigpTS#m$fSu;p+*4=KUYw3GmUtjM{^?KHB z+B7sYtcR->_OY>Hp{n#zUcAQT3etpm*xPki;m@gf@)tLI54(lS zO6gGG=IcoXZXV!gdwV;3W$UQo>gHL1M#S*0D_6P|xY;RL)Xfzi3JL%o7PGj(O-^2_ zV|?Ixa&xsWa8&|J?}aPp>tGi~=g_65E%f0Xg8oDBX}K^6rSn<;;F z_53guDVD1WZ|Uh1e`F!gqKPlQzo;qSGb+h@j6k3F@yi*V3cjFUk#6{l zpZ`fe<7$WG$}U%Mci9to&duJ_x$K3f{UQ%niPF-$$^~u@JUq&pf!i88M~M1T08hI- z$o?1=K;?Xs6_CjuvC1r}tZ+?obW5<0Q9irnCFR=UvnoY57LE|TZc2HuW|Zv($Vv~- zSP7*nuBTb1`mtzcbUaxk8#*M|qs8(w`bQg^`CYn40RWFx++0~mLgd-G**k+d-Rw4Q z>ba?D6Dvbkey-vMDd)AxvU4woFy*t`#syw=+t`Ilf~s* z%e`D-Tm{mG1&&)-jGdgHQV7M^N`e*NA!i_BTriqyCS(Grl<7y@3IR#DJRebJ@-ygr6!PNKy z=9X^wi=Us%=XNqrql^+Q7UwT!j;!Kow5&j?r&X~>!4QiV4N6#+FRJ9PGKq~n>RkEV zDuaaB#TJ#{x^J@W{)a{7LibHgbX70rBlif??Ui8h-D zimqFRiar73MUNemMIApqp&4i<`s~yZ9l~tH@Xb@jgv~R=z#UV?^vy=XWC_goSHSGs zcb+g^Jzp$zaTG@W_QGn*0x@_u_ALlE6oVt@in04m#N>$iVtCAaG5Mgi&<%l|`94!& z5NaoE!W@LoL0e&Yz)85To+~!`Sc|mDk)H4hd8x`l|Q z16N|FgxzAn0&g+8ZSk0sFIhWew$p;zH6rVZAhBvIUq|+oPAGB)lL`M25G`7Z{OGNkE3h{d;36m@7V)Er@V)(5RF%j|HuJXVRV zTbGE{t9`^;&$YsHt(Vxi(N`?nwMHyEv`TpG@)v8P{Y6lShu9VAD=_;ayj>zhK)^QP z;u0zR{bNPI_Pt_DT%hobjuoLHq2j>NFtK;q<6>9D72)P_U%2`_626ob_U-P zP$C{Yd?;SOcp`Ei=8LBZG>mBMT#SR4`0d%SiSU};#ut9M0ggfDEjl_rAtRKr5dn2 zTk-O*BfJE7-%A&d9v&7KhzP6G|0Y6MQBmKEhldRvGHCC1|E>Q1p6QBbe^CYyrte_@ z4;;`xVk=+%YqAybpQKVsisI1Fp@Rkt=-;pAj_q6c^0$9i6#j__DC==0bj_N*w@L6@ zw%Qecs?1?UuLc5>el=^>s8PK)(%*_GTm4)WV9$?S@y`^043wd3RIl25+qSJ+xCGl* zXBU06qQe!32KN6BY1hD+D^+`M+5~Wa|E&Q*%RWk@*q3hx_OB&FSNW#uZySAmw`|!S z7#I|^`I;j3!y<<(2KWC?O1sK8-&CpE3PQ&h@IenAJaQ@gkS}6iz8VPdYSq8NxoSAP zwAF?UhyeJ9j~?a5C?YEsI81S63HqzAz63lX_-+e)@Z{;!rwc2T7**Tn zJ7wx$A^ESqtOWS=8+?5OA3T2gv|vSHd4y6@-t_sdT2)+(d*v^`RKjoAv_0s_(}IF~ z8Sly=6#MMue%r~DCXO9DM!H6f7+1f3qehLeHt6V{bmsi|-HI@^0=$fTbMuPnd?S5* zT^${5zVwFIu3hK*AAYL$bMtP!5>K3Mqli?ivXuD38&^!{%{7{>J9FlY>A19q*ZE!o z|D{7u=fo4I+ACt!m@Yo0XzDGKd2DsA7)27YNskh>=Lf~`D#^#py?c2B4A&TXE54XjO9dWricJ17uS<`0EU#hWm*qitJ z(o$1z-%h=Y>yGz2FRxYZE2YbQ$zrGOEn2iFpZ=SoeOalgchl1H^3u}M?%uiMvljDe zxK^(G7<~Ba{QX%BnfDe~ULNwuIn5W~-KF$fXR6aP@}*+J{u?&|osUb3k#hmp>7*q0 zCC*OWe{EeJKK5CWqUgnq{WspeFIE)iBZr)v`}Z$M=`V5mwOuqi9Q6k9EH*MW2tj zlz&W!FH#)1diT!t#6+h|#mjKDR#F~N_4F+Cr6OzEtyPQKy;G$8b#JGPg4R=#f+_?)c8?7U`+Lkk7V(33(x6hBPvKuYukwHMR-ZEu22%YO%v)8F z>*q_gYEm1gPU>JTrgpOy)rOji>cg$X_Y<9j;Q%{fIm%vao%F5PI_(D$G~)*mI7=S4 z?$r53?AC80_8T-7ktS_Kv`I%1J-?HPv-%a|*B;{Nf}Y~I!*AlK;{b7dQGZcmNi|W+ zrJnf7y`^Zl9^=+^Jw!X-ei*Y36P>q>57}NR)|~xZf1x zPJ3YxVk_)-FA|0a?8IT$*&@+-kVtYJg>mH=adOpkamI6+IPEo4oLx6vT;8fLuK4MS z8^LDc@{Wb#YKV=vw%bYE+Otr!F;~N6f8n_hKXG}cZmp$3HR>XFQN_{6w&dq z;t;;yi*Xg#qgIK_F}~t@q>sn~JzP($7wb-M5lb%yiOoqtVkyRh-X{+W_nVPoZF;;2 zJ+Vs!pF1FYFgDCSwoW9U*e!0K+9PhCkHuK_mD!taIf4Sgauguf60`(KE#hzBC{;3IJ$E>ncZy%PHly%fie z9uw|oF9`pWm&Aq(m&MvUiNfRLRk8f!EwSm;b>V#>MQpfqO9WoHAY#uZir|~4#i1L? z;^2*R5t4OT1f*q&?I$0LxTH5?^<#zb#@KS>3xx=KRV)s^DH116o)o7}pAn}ooDt_P zoD*lRUl8}MofdbK&x&KmuZ#1iZ-|SRu8Gsh*Tto)SH;DG%5}7y7i;VPCk(rS$9%bGVm(nxE&D(k6#D&-5V)8q2^;W*Po0To@ zWEP0b?EB(zZjN}E`#?N;^jKu&6^Lgq3dHNTg=zxfbAGzIxVU^8Ze8N@XZK&F`hEh! z=lQcOerpX*=KFjUAhB?l-%kGvix$k!PyGmh>x(^pn|pN&c5+&HCV%}006sapdv*J@ zi`Hs8J3DK`!o-RIiO+ic+NHx!bywNqvOJr;t~`M2%kEvY>i$rt)+$>&TU$G;E2-s? z%--6i{SV*QsaeRx>V!D}yt@Dy)4j1mICy?g%)AzM7 zXsZ6rWD_$B=OrmOPdrweCw6_>5s@+es`kyKc_yYdOHyuKyR24(w5>mW$D@{NRjW)g zo@-*^oO0{7*Q3%RBtC3cr)G_BtMm7X#>Vr^op0Q}ed<9ehQ4{r$5#IO>o30eLPMkX zxN+kr%+g7^dg zM~)mZcwnD?U;X?`!^X{8wr$(iJx%rfePrzDQKLtX964n0px-n#8@FiDs%7)0ZPJt! zaw(3C)g5DGWW*pdTgg}RCT-GH--{v-$Hv4%%D{3P*|B4Xw)UKT&ZDfz z*I2htzJ4CxwY8_N$<0oMlvG{oToa$CrM`ZbeCYg+9YHhJFbA5Yk~ zea_0<9A9r$Ott&^=h^#XJg0>f`jpnJ+E@HsmsW6eZ~EiXdM*EzpVpx8%Wdi0MQ1m*z5rcIlr`O(0@ zU=bV~B6jcIjrmaYdr=2OTwJ_xy}DCG9xV}}56*~HPYH92XT`;f7schvSH$(}*F|b- zD&+ZHaXULtWM^lK`}gmQ*RNkAybD5AoKi;&_)NIjJ`u5MRTWU&aI;-U*z4&MUXdpSZlz?@hz}Bh) z`%?<6qL!Mk*TU}_2yC@p8G%Qq9Or+-#dt72RS_6ZSS<4BUD~?8DekI(iWWa3e2-8a zfy>He{+}!d=l>N#^d>4UM^YxG|wt5&ThtOH5`lhVaIcka5RuWv94uw0%7 z2-v_+I-|D|?*9{IQ1MI6l`B_|4-en@bo=%|N={Cu=g*(hn>TL=^OjP`%gdwK*f{d> zS+58W-?gWE_wL<*GuKfa1g^LL?lMTY*R5OkyM6oi`}_KCQedW(ii?Zs`QtpodMIIi zoUqnTBmMSM?DacT^!`0Pdi03G;2D6j-k&{t&M@H3JjML<-&qE(Yv$b=dn5Km1_$q; z+}vDx|GtP4_xVx$W*bV4_N1pLH`9@eN67frYZ@MTnXtA^sX31bItraSl|+d5&dA7k zEYhi12E6(&yBK%o_isV>2D8-O z^ycMLit)Dx-kT}&*jma+SV{MyT?n=kgf(}Xn^8ihcM8b#)?2bpE2feAE|K5yOHvsw zUAn})^tx@^cHD6^MBp~9l1upck6uids;~htK|7=;Po5Cg!zte1nzDd*TJ&K(jN5wC}v9IkyUEc1k`O-Yz28JkTa(84?pu(C*!P?j!A12rOT@ zpWr%Xdi_%ur}g#LtpUG5J{G5@-jQ%m*lJ1XajtZGk3HQ8F{i5mhIDC@HeK|dMwdNC z5$q#q!absisU zNYH;YSbo$^1&vHm(1;twH0s8CnucfCwb0F+hlIsV^7Gq5SWR7qyqX}eT;+cHpJ)pb z-$X~pgocEKNc6fG<40FRtmxYIxpdKYHl6dDOi7?O?1$+1lKyne@pn35)0sB;E+p8t z(8xOow@PUE%@P`xg8PjUltDpLl8XrTIrREnKHa=|lTf#XLxv3P1H6AhVEyAi&TP&=tl(BrI?TY-HYRXvu964 zUQ$vL!HtGsuLYXx&>4?$p!ZNZ?%a=#fZmLEoNZT%vFbzzEwm`kxC!}qS9&n$RRYF$x3kh~x^d>)_&YwSzK05qq%a*O!^pADHf42^p z@4pES3VzFd&C}d_bjoWMomx4bjxFj>hZg)saW>s38uX3=z4x28rhW5TQG{`G+GE(1 zBDH@a7Y}1<4?Md8_dekN!N7eSaG!;D#+fB#kzGQzxh1sdVF|%Dj9@=Sm`fnoaS>>? z2KhHe;5ztM+kuMqi(yVe1qB5JJ3czIdWwv@Eog4pg$|l`peVDpwBMw)g!gX4CbZL_ z3GLEtM0;m{PmA0QsS)sP1-|c$w)@-d5*h-YnU-E6(S0FsU;0o%u8$SugDE%I%u(L6 z=XBx1MG6ZIdxnMfF2J4TDeFi7iY$_34&;GE`};R9&>7E}bbRq(O0e!hu@;@_pecA| zUK`qH+=3#En$b@E#uTpCh(dK6Qs|tYY4?op$j()tYJ>I-Qiz&Ec6LA=^hs6Fn6wfy z1m8FT|J6@``!faky;KnF%?S2#61`xyLmsPEF9P1Ir?M>i+%XT+{!8@hdXUG2IU+h2 z=uAnjS|Iba1Wl-fC6XNr)|J}+w>o4$E50Hwp52Sz&qbuC#rp|SSmwHl;Jnj!T5U$nxCVf zwcwo{pnb#}1x3A6koUFgwEy%OVp+{H+tk#2Jn*THz!rFr+}G1(N^HTb!uG-9AAy*I?o{i zdH)sguYR?dYF{s=hUm9Cq7H_oE66AtJo89Fp`iak;2-z4m=5O`5bR7P{SF3xw0(P! zKk#NAV!eoY=&vpfhlLI`f&zm)cI?<8@y;Pnb2{!YgktA)r6_}Tv`?=kMd&t18*L2S z8_|wAzku!yfO|dKGW{p=oBAVdo>Yed##A9gr`aUH`(FV6DpwWsEy9o271RoSS6{T_ zIpCq?Xuq(jq*&m81oS^q1evsRH)TC|2p&qL-MjW=!&IgOt~e2q5n4MJ`FZh|(^GmU5p`ZlLWMB85yXis#!fanCm)@#@3 z!0EG;m6b*C-!2|Ga#U~Nj=jA9whep%9kW-+j*zFA`=y)L?-J~433kK;J7$7CGr_Kz zhV%-hzFk7-_x2&wqty=jwRtdgZ4yjf8V6D5MuF6+VIXz+DVSgnO;xr`qHi`%r|KJL zQ?1Q9RClWZ!QPu->rJplrs;93$@+vJt-KIMe#r+Y1j`S$l|ntl#-ACyB4JLJ-jE%NBQMtKB#YpPr` zkG}jSm%jKK;j3KI_!6N~9>E5iVB<}&X(!mmQ!U53^!?)cRBw3`f;~I6_wGTx{RY#h z9n%PQ=42D^Ol~LE(S|cy3AVxnTXllLD$}1(24~>Ua~3MS8b3dJRf4r-#Qy#Jiyu6A zfVOj*;5Sbe*6E~em`0Q5q*2d7Y1FV?I{nxro$CCYPPMQQP`y?4L*$t*CV|H)?|0cg9EF60#sXU&pYgodU+Hs|Hv4WmaqQS}diLxYS{^cIk_%WTnt&>jGY6ACaGX7Po0RJygKOF+7mg^5x z+ochG@6v*Pa%)Qs*L0?qKEF|C(EiWBi8OY%KIsB~t3%6Z$?>(c^5lB*I<<+c0~XT5 zr;jKmCKhxIya2p45m*NO75-mtSid3p+_`ho*mk};{<^?@_C1<_0pWx>nKW=*Cc$o> zerO2#GybT9YBkd2dZ?OCRlm%k>K(Vy_a61>2e)SQBXIwDRVQk?t|ztK*pGe<7)kwh zOr^0AhBW7Z4Vfn_p#?|WY4H&cf{i=DXMwKXxlTuq9^*RT@nvHKRX>cMAHBXrpPP&I z8nm4^WNRBvo@+D7()u3Fk?4PqCTeHUc%3YofG}!mCiNPaN$^3S?|uRg)db$(avAXZ z>l~`tC79}YHlg~f+tDwpyHaDXUewCBFLm59jCux5paDB))7X7;Y34y|GK_O1^EhXk zAH9gIBAv)2)S8m6pP|#}%isX|7}$Om{;=nG1*1QD`0yz$TXulFeX_{bE`#)p(rLQR zJ({eSNs|q-XrlgonxK21#?8J@!zW}>k3Z#dd|#h==^olZE`8TMl;DFxjlH{3^YwpF z>rDem%YQg^1MYox&ZHp``ZPAmjHX4~(41&H(v7ku-3Uw4+i6C+fpaPG+DS>DMql(4 z_%}iLTl#yg^?HZ(G4$Ur$i*d!Hu`3hgJT96&ds1%2AMQ#ZZ^#^&!IV%xirf>m!=ry z&_taa8aE@GhK!y|NPz%Cv7BV332?AD<__ZrZEsChIr+JZ*KTG6;zYpD$5_L|V>Fnt;kFr9|_ zPNeZ$rqQ8GN9gL+s}#6B@a5;?zh?ECLYAG3zq{Lh3f_^8Ipj=oT%1J<7Uz(I%L7`v z<}tbWJRz6$PsrKp3E8x=RKe)`i}{|E(G5fGDf>JJiLZ3+)Rkt(FQw@R7ZQArXxt8C8o6Z_4PQT*CT*Gp{Erg1#qHa+zxr(a)#&fB zaxd-KbDw-S-KSNaIkeLAA+6c)gf?t@Mt)&0Xv@x*MchMmr;4Qskl6 zbnxgK+JEE??TmX(+xNes_2I8*C6pQGO)m&OLFC8o;yK10J3^TMKTrO6`@Dob zGd+0l1pW3-I(gy&9Xyyvdm6Iq`;0UwTIulJn`@^?W*U`5oqJ-_jnG zXIu0eTEF)-tqjd4*O*LNlNn8`@5PcwS{$vs6HRWn_LEEU9&)}ELXKPq$9%|~>Az<& z>FltknZSS6W+T$}HKya2PZIYi&hJO>+Sf*%^nu#2{BJ4KNP zGU@QKmy~$wHC?*)j_%wmr0m=x%FZjIyP1V_8S|URujEryQa z>Kn>?SVXU(v%i7P{^Uh5-ODMWOQ}V4^l|}3oXV$d35DdFlt=KTA|K%I4gA;Mji*(& zfd94KwDf#1IiB<*`@>!|Kgxylwp-KOEw%)oF}j*|ogO`ZB(33a{|o)+7Vu{M`R~T@ z8askRR$*@Z6+C^o+;sTx5rRRzRGxS53JAVhdB=a#x&wNjCVJX2{2V3c|?)f=mcnyF5fF`~heNA_UhX=vWAtk^+ARl@u zVgC`z&c2U1&|sa%lQymc4j-x-MyS_f>%)X;~Z&jjMvS09vb>`3Hbf_o;`bx0&fqDfyV)F z9=kK&Xdry<#jreHt^}Q^vG2xB#^K@Nw~)T#;K67~KjrnV!$(dLd<7{v^{v#F*p$!*t7T5;Z=mY&{9E6xOv<$ia9-z2?$`$po&{re-qBRldj-{xguVZn2@Dmtrh zE{DWF^dQwsP~eV{sE>!>FR3n=r{GIT@S`OET`y?0-%DB+e2tc$+CVE#Z6=c)iwHiF zQh9IONFg7TAM^1?Y;0^hsNt=m(?5jjUnZl0bVAM573V* zRna1U>+&4b-_64UCyEaI#j}XWgFN1~^Q!+zU1+pu(ejtI zYrXcsE0u0vh@qP??vxhe0=tR@l)A&5d`=!Fy}R#8?^Zs|x%`}5PTi)o+=tv3-h*?% z80619%6-beeATR3vkK(<_G{O!mE20&L)Z5>&<*5yd5bQcUpJM`yN{q1F?(qo)+@%Q zyr;3S85n=@6=_c54kYaFLeBd`r8V>su(=qXqL6GCMxJ>}o0G25HH9zC?s*Imy|zd;v698lhAbjp1s9mAUbAqQSx?n?1yZOCcQb{dKG4z@uUjrFuK zSc{#2HK^&QGb#3V29L4cPM$Kw9(nT^?Q`p)(z0dC-C^HeeEr~hI^#8+5|<5v4aD!T zBfz@0Wk+dk_@Gf^ve~(n1|a{DSc@5tFb(V2vw00QEuW0z&(OV$j1sJOrhfO`cTJH$ z%csAifkw-gEgNGmlA?RpPt&PYldKw3{fIKLy-(%aD9xmaJsI{IFn`lJ`KnY57a z?^wSb20qZfC$F8u2aX&vo>TDIi*)kTDKa-RUjje=ddQ#q+`qainVVa9T)uFQuI*VR ztt-b{b(7YV_s?rhd!+SbO$x(W@~&Aw(3Buw`WbX@hx&&PpS1pMbH9W;f%r$tPZq}@MS7iK`e1LlI%wy{8t0!SkFpc8occTOI+Lx}W@>;O8o;tH0?U?!< z<^(rTwHv$!TuiOOf5XxgWQw)N4NtLlhqcqq8QBz#byJK_^TE&7$e(QnKGha9%*@Q^ zK$aHW*uQ~}It;>^F6;W`Z|80(3=j_Z%L(Crgz(%`M@=}WA=)xsL?&%mKK z)^D}3cDNpE&k9Fe@N-*z1 zQCQR5rwh9SlpA|3VLkIF@}Kbo`Aw-!{^P39KtFF1D1Vh}3ar5@NDF+x>&L4=|NY4S zSiXXMFJ7iA7>mQk>Lm=%nVhjiy*?tV}f{n9R*zWe1q zRjYHKzWyegzO0;0mA*pw;t^FDVoEg@*QDKvDU8wt}K{RovF6Pc0Xcg9c zwq6XPEm7N~`4(*GJ`edb4^-?MH4F_57Qsdab0R0nd1*T78mH0tnQ8Rf;B>0jES+j% z9i)0Kc`d3+6|ALwd7r8ex1d_f>q~1;4Lr1{_4+>4%YQUYiZCX#xJ9($gg32A@*}_4 z06LYFL?$MtYmhhV2p{CHr>nP)=Y!xAO3RjKkS^9WcwK4yoDAwWI)hqvme*oR*TcTf zqMD;^sP4+9RNtc`HSy|69sGt+pWw+fai0kp#yVi_ZUs5Ut)R6r>*>_lB*Kmo!N{9s z;RpHa>gsO0cP|5T7N^L~J(G;)rPCB`tP5dXWxP%{jhLEE-T%lS&6Zf3!@5@0s@e4Y zL`Q14t^+mo{++OY7uHY4Q~zCaXhM`Z&BnTkVU#@?MOc$d)JlTSD4Cg=g?%djIkV^N z$jyC#IpNcoKguF=%M8+)2J`jZH8g(W5Pl3Y1Dk07;Z%4w#}x6m;*j@=`84Pvg=d%L!S%9b}keib_zB| zS>(7VldK(bXo1T^TD1BxE%bOqri&g*YZ_CHa%uc*tOcyyNi$;?Vx7g2CdWF_l!V1J z?T|A~1@BGTXGx=j^k}&66tdgpMCY!Yr+IVd?fz8$vuDo^$;*3yHN6vopOu z*)g+0-mH^;^fEFuYH4a>qV2rcd9&YUzZ1~E-odx7=Hj;> z%qQ=YS7{CC?sjz-Eyo<>;`oiU8Mf7Fndx-l{Dor7JEmfrmOW_qIGfO505awN|AVqu zL-g$o@vVcQ$-XFt`?t0nz;KWo+c?hE*4w8Z#k|G9JL6}Rs^ zricR>n9Dv&N0U#|&D%FQ@25ICx|2Y=kMh^&<@DE3=)WtiUcJT`^v%E?Z^c*-DS>V6 ztL4j=Z~yhzU)vz|KUU^XVWQbc^NSumdUgYUjPKa7X2d#-|2M(2A$e|MMl`_hL#xd~KUj@KwFg(XYdh(v3 z$B-XylUq=;!hWHeg0DZhg?z^4?7Xxy{AgdSS>p-)){DHn)?r@GbKz%16-GvL+qt`Y zWW$%1r-r2qezQCm`NrOUp~GLLfw;97FLu5Lxggncz;~G711zOcSX5L3JRX{um`(Up z8W@uujE_$!E-HFY92b7YwCX99rU5@*Li+cS509VAT~1Dmbx`Ja92Y*xQe5~j6MT)! zr4fgD0T&n7!{s95Vb!YDkJye^l`njs3I5Ph{N}LzRpG&N%@}tp)Z@cHS+W6E#fASg z^x~RQe3&{(Y4lP{gY#iO8dVx=)~vmto;T;K3aa?($X?)PSG`_2=+j~`f_+v8;C7eHe z!KJ+6J5KN!FBuG~h2T1&#|J@*=zQ0ht_^1N%e4S6rG3 zWC}PsE{eKx<;pYm3*k#W4aB7l_m3oVJnWezeP>&j^*Gi=Nj~QJIf7qV*|F!oWU`;L0;%W3= z6r=_OL51WbbZOE-M35q&B7_in2!oswj$x2sW@`!Aem;5kx6h zP}H|45a8bHE#LdU-~ac%_uk2OlCyheXJ==2XLjbm8Sr<}@R&b<_R#?DFZB_4KiSIi z^2(vKw4FcT^U$HfQDE!f{rMk`3B^CYYxfG!w28o~{ngn8VBogX2MW+Le6hhOG$<^D zLN^iO$6}4(^o3Mkkc%1Qq6>vHa~UiDH0_r%b|$y+?0rr)u?QTDNoQpG# z(*_KY#7@R#tUOJmG8WIl%T6Qu2bQ`xb2tEaK&3DOX?`FnW&lVR5AwRrGIk~@7f28Y zGLUiTfmy1)R2l0_^NTT7j$lOkDo>}rn&IqR*Lb+^Y2o5I(gj5${EXNUhyXn0y|dYm zttX->efFp&a>+eb8x2!dv>9O;pZSnh*WWM><=!5`5DFN)W-19-!m;`#h$Z?zFeavF zR*Zc;6FI!~qT9pTfCu97M${D^Jc1*QOd0ay(3nhxX61+6jghw+qg%3M->-Zs?qi#0 z9$!m-c*W-1=@R=aC$}vG%QUuS7<|0@0s`sa1TT?ra27t}MigF}3rxVaiX9W2JNk!m6T0Hzu z#flH3Fm!Q{W)#SoNQ;WlbOt$uNfDHv1LMN|d=W0TW<(ub#7viun`gR&hb7+~cI=C+ zdwcTrMKa!ibj}e2Ut>9?-~NWogF_&^EogMh&`<}E8xTQ_AcG{56r5jZ=ya1GssM@T z&Z%P?QcK-l{i18$xUT=jmkSs*C7=9$DRYNuD3(t^@H3*V+zCn1L`Lno$=!9P4e4*D zJPz{69y_k)8CLvZg}H2cWoW@mubQ=7{qOhW968|QU+Uy!PLGTTL?HG{>mMNqB*u`RlKp?B|D7B_vezGY#FF^i@gv!87W9*P7|s@)9$nmsmGJYk znPVl7Hn>`ca6x#3Z!3f|xUxeyBG!Pi9r(5e*YHt~83sE6ctvv~MsCjq(@oiTr`^im zn_Ek84+*f65Ojbi2oZ{)0Gwe6e*}%)N(BLLwOzqo5~z&;_elr_fTMxZ^ihc^1Zx%v zXkq}UAW$Fh6HM`J7zn)4e)^-0@MXWL}+P>ulYVNT*ezi@w103$3p9KxK9o64pE#{AErEkPRw zIR8m2Iv7&~7~|i=TC?TD_y>?SzvJBBV7 z2RWSJfhExdk!D00bpDl4Pgl|M6lZi0a#A2FwVc8E%Rq7f;}7O zaIOP5=>Y4TZqWstguqEe5Wu$%_=T^4<^+c;_@_GLAGE~W=>9n!IIu=S!3S2zIDm6C zpd$cqDU{8NaKJNIs|Bzni9j3&Jo@K+gEfE|8#{I$7I-GXdIMgcbf6jFRSHXNBYRAa zGmx-kVJbL0hfC9v`_GJRC9p=w8C~QD<|(XA=D784}2(K#lUxb!RZgawb=I!z&9*ge}n<}gvPaS`A6fs}X_t&`3lf<_Ibg0TF)J7K3GMN%#6FsPSItz-78{YkqLtH-jr@X9e1NibPO^W{ME`UqYV{Brr`M2Diq_6=ETe%;yUb6`F<20HQ^vp;$;J;(%wk z`O8zJUOA=M!zQwdw;$(a=`9++v8~!;Z~$+^3&pIUsv-~Ew&n?V%R23g85(=>?d~S& zqS6U_oJY*F0=|0d-L0$i9IF!72CI#e%jnysWNEvx7c5rg<_8bf`@M2^m)tml>MZ}X z`YJw~>*|uOXOTzDeU2996r~97xn|KNc6hMH7gf4XK!F-KFm={+hw{5(l{Lnv9`|d0 zXL{%C#N0FFe>l*rcm`jXmdh)Uw>J8z{_*A_>HW`X1?jdd2j7VgGd z>?(}av(hn>8eF0uU-OIkU9dU0YUrA45aGb`uTK=8>pe9JQw|ACvK8~*G{lUF?Mbkl zh!|h+W@qg&v#WRH$vARcyT`*7Vv#Ff+KWzpU%j04w7&N9w}}b4((pt%!Ecu99r=o$ zOA7U`+FntD>~F){)i_s+MihRcjGKKLM8x-#6pTdbn>bgX=vf>;#tj$ zD)X#>a!zAc8agKUmkK^BCi zkQzJ}1SGI?bJa1NGCh+cuuhQ_I!F!g97LuO7DAFRD3^qgwyrjjsI?saG0>W(XfaGt zqNHt@U#}BaJL`m$o}Thq>akmqs2s=@?otus0NF!!g*Js&$rgbOM)(Rm9=ui@`Y z+3IgQ0KHMHiQt1C#g%d^rk98c6=Y{Kk;lrvamwsdekC1N7T_kL(uhJEwTZ;LeSb zktnOv>(3uSBvlyh&v>uqOljD+ zAh9YaXFx&YV`LPbH|^T0aq)(`6=4(*+&Xd^~B;%g4RN(sJp`y#8+^;Wz`UVk;u!*7f4zh4{7 zlToJ76MyGg_~jeg7H@ByLmvIER3jW7@;Ey4Lh9C`J6X%3jFI{`V|Uede(7Xt;vSt{ zlZtTKnXrcRl|RKe@a^Hapf%l8ehXoHqI&hNhXXe5_a~M!W7^aYEai$(RHsp((G2}l zS{F_S@8+c?X|*PFjNjbSW^)2jPC0EAXd$o!-2SODis3uJ!JAEvT&kb3f&t28=;d|NnoI4teE@$Mq z8>YBiT(IkAs^Xrq@6Ww6&*(icY#kTO`MwUSJFhR3V4ps+$o5sNhp$MQtDl!uSb1)a zXCG=;FJEhm?%@zO$TPN-uO+`wH^lykZ2Xu^mPq88vo>Ea5Q`@kVsZISTO59paOMN{ zRscPJvIqb}!%X>?XmVgD;3bKcSUR_QooO^+j==m5@(&`D7!<5oB#122gBUThHV0T9 zJxHGj=@>u;u+7nB7YPtt{I}ag7D{qgFbC>=JJT^90gz0-;F-1;&YySi;nf(kG7LR!Q-1U~9t&F8MXVekkkNic6u6^ilO zk&x}95?XP{uHaqZrU#t|T&npsnkt?iUtS)|SMem*^Olbc*E|0$Z;8%J#PR(nd7bXo zSk`)VT*ILwPX>+9v~MHL;^sUL}@cZsj4wcFG&G-R1kYbT(ay_ld7FwZ&Mw&%ZwD^ zJ>O&+YL;txG}oGZC$`t1jjO6F_Ub1*IGCt0zGmGXB^}i!gD}5sE_DSWrxz{WetWDA zoxJO__s4tA4Lh@UT)LvfQ1zBpKi8_JuCKb!V3qFO_^Q3-a*NB3`wx;7gS*x34tl5b zsCu<4*_$|DJL_($g8q0vb_4#=;@9C`B5SRqYQRhFFP%cMyq{lLP*X4a(9OQzph)E1 zV!KPy=Pl!{-ZVDRW4q}6DxD41`OU*u<=meq?HY8jgUU|s?i}23xMJ#MrGHOj9y4y} z@sNGL-O@4%_0qDf0f~KSzR}**`0Y>J3pO@HsjGh)qBf~#Xk@I^b8PJX#WM98-|FUv zB^G!_&gZa+Xsm~ZgqKgweiKKn?awMxq+T6bGnQL<$+|Ff--E8lDLVx?=5Pp@!*`G< zH{Z;rasd(p_I-$X_V1_0Xwhr~Nfbs<0S@{^0{6-SVaDDS$%|lMG|7<2tnTN6(7*)z z#>~+*SD*-)78fMB)^rb)T_@aMoT?42pEh&A5OZOM}84&X@ zOgt9D%!8QO5OdEglW=H=nFN_~x(8^f^ zC`em@h?Sogd+=t*VBk0(8Th!~z~hA@Ytu2i=_n}Ob=DAxR#0X|KQoL~*CKFBc`_!cGmrdXD-!g2lIbJ*(^qkzFtW%Qb zMcJOYHQn0DtwV5c`~w+#xltqYbmx1O-$GuS@Zgpn=rfird3aVnD%+svo!?DMH)t1H))#wTz~fT->F|W$Gp@Qwaxu zfh#o*+~no#o7^N%*3C=qd^S2Uvao2sT6bGX-n|cAWV6>B zcnWrya7S_9=dOxWl)OwLo&ETtMHX|p)9kvi`iEB({9xX%MH_QF5RZ$j>pe&FO8D$; zMe`FC?;(~pR~;xZwTe>EX?bwu$l=&n=JYoFKiJ~CQna6m6V zM%w;rdgDbDj@e$Sj9|0-A9_`OpNx*-bp~iGf===OGIagu(uEuDk%k&o#sl zlrh)^_{$|;y$|2=P2G4g7<{L$e#+z8$ArQ?yhiozuittXL+10E3lg*;VxeTB_#Z=h zMKX!ff3~LQFFRm~F)Ut5R3GKG$gXMn71cf#=kZ47!n@aK<5f?-Y%(dE>eKkF zKgAMpzS{DpFHvFZfPYN$64ML2UecVthkM|WFJh`LI-aHKdNvgqpOC(KJOAr~z6$FH z<=c%9(igo?x^yzT&`G2&errR$y_ezIZSh}HYR75OMxWj5`)ao#J>uwXlZ`E9+Qjt! zAtU@A>abYQdDa$W+SMJc z6W+ARC)OaV=-FVB>(uIHO&ZGsl(Iz3*6s_?ZK1zl^1AE9tRBFK@m?@}e$quc^*E!m z!*A**dUyNsXo{5&QxUUh<@?nxwS%zHxD^6MiDh6#qxb2Tbih`V~mnuRsF70z8;K&4ph5ZtJh= zo~D??(!u*XTkT_U#uKu=_8Iz$;^<^M_ekQ0e*S%{?v<8A!H3jGi}pO7S$w4 zStxKjG70ztheoST%H0Sap#~|wbPM@ZcerB&D>KC}cpvN5T3BiQVFTCb*!eoGO5s8i z37+e{U6{U!%gNyza2=s^GXEYiTRQZ=i4 z>R|D0;ughxmS)HJl|~9pHVNIG{CeX8&nM}JcAQf^p51!kbpNlG<9wNe3RB;U535xt zmwL&GN%no!(0}AUT;P{%Z%7NPR@PQ*pwglqTvnvIoIwq$pVY>B1f%UZ`)XOh5vh5* z=ra>9Xs`T=lHFzT7N!!>;}2Ptb-&pRKO(Q#m`2DPwTO$imTlGwT>lHz4O%z78Fqv~ z)Tap2@qun~qs$g4PIun{)-Gb;v%mF70W ze}en>;T{Qi1gaQ8h@aC1n)i3h7sN@ygQ9D~w{#L>P9GJ2G$3Ll9B-W8@yT^D&JSmn z-OsRCbiF%|C^pXL!rVKs+SW(oBFi3EO!vQBFku*%)AcxwMQO3K5m;-geO9WX_1*TU zH(Q^ddbuRX^KN5ak+_O zp?faTtM)OKR9c=&ZVU*ld%^8VGg_*+)~d<0%QyYR%iR`N7K(9yt(F!XDR8(vH0CH( zY{|dA+ecBKr+9Uk$#Tg5{n^)U2UBvwMfQTI`(~i{JUKBCJ|_Hk-yqulJ7)d&iGi>c z9{~bE51{$9bf*=c&U8^5F8-U<^51Ey2$d=k6kNswn1WGV8=CuO^?BQDH>%021c=jRl+r5f{ZgHLtGvENDH?xtk33bMX6~?Z?R}8~ zZj*p*+QZkxJv<))?AriBGaE=Oetj;87ZnyTk7BLF{DP5&hkfJeWsyPfg&$VT75 z`^MRs8=lHng@fF{hhpx9m>Iuu{+W<5 zNB;rmpYW$D*2&&(+Vi(?a>4si{7I3ajDO(zJApsKm~;JI{;BKF)0eVVsHdfCpDA-Q z+W25`krJ)ueQE1B;qqOsp5x!YxKkHFHCLPcZw7ac_kWD8zkckH>g&l%Lm{s2yvx_J zEXBWGaM{^9<|CfArFG>>vjZ$D?N%&;W)E4aC>z}!LYL%bcrLwk?i;2%{4jBq+Bdff z{P%OcyR|O*dhdVkzVh9tJNf>ZJAVZGZhP6hN?^~D9)rO}t2UDl#wk&o3W|>yec84( zH#jr8l=`5%U{Zcczax9BrG!5>`DpzinR1MI()qKA2bA@(XH|Rs20xEAB#=E-MPhLV z?puy*KEa(PlDe>tR3>(Ne}}Nm{>0{cZDsd{wBZQm%u9M!>7+ob|J04> z>?iOX&06u9ViWy#;+XBp&#&kSq!3!vapvtZzEcJl2U~7;u+GricQSDx12@ z)u4LAvl~Kg`LR1940-$ZX%6h_(!z-^9lwHiRa5$WgTJDKhLc$Ge2_k?jkdX zuH?yO`-{c=q4tXmqJ1YhZahB?FMD07!t-qT3rnLsr_E1_9yGhX4tnL@c>fi2~0<_AOL;pSQ{j7G_z7a&fSHJ&jODr#D3dn>&?zC~3NqP#2wp*d#a z(S(1qy_)x6jEf6_;zIPQ$O}Wt(jqhNpFTv?*45HqKU-liQvpQ*OJMyt-bk@ID zqU=!C&s^8}p)3}16(lnoZ~(C^q2GiQ{t&LZ&{J$YSx49tq_#prZpyPKQ)5XsQzz8% z$A(+u0Tuk8A?BBP2vB$ea{yxYL(Df2vmS$udu}9-&c0j6eIqrip<^qld^y53J-pRW zj%IZtP5Rw05Od#ugQf6u#(`K}i)7V0 zJ{@_CtLmMsf^-f&@>Jh(qODtEa?=WjI~#Z8#974^$1Faox+z#v@`D|8)-65ALVfs= zw)i69$8pV`y{g~7yi`dVJFcFJRk;1h?mbugv5gujqroM2Ph{N=s=4E6(OWBVXIa?D zE7hYSyA(DmwO>2zJ5;$^%9)?<{rmBCy<1D@E74{ z@=e4t((@k$Pa37>ym&GA3^SA;7q`uDxWJ^ojGN3*E?!SbP2Z6F;X}5%XDwbyUq-*=+AKb=vBK`w{+wTwn literal 0 HcmV?d00001 diff --git a/trunk/managed/Warp3Dmod/Warp3D.cs b/trunk/managed/Warp3Dmod/Warp3D.cs index b205f84d..2ea83620 100644 --- a/trunk/managed/Warp3Dmod/Warp3D.cs +++ b/trunk/managed/Warp3Dmod/Warp3D.cs @@ -5,16 +5,16 @@ using System.Collections; namespace Warp3D { - public class Warp3D - { - warp_Scene _scene = null; + public class Warp3D + { + warp_Scene _scene; - private Hashtable _plugins = new Hashtable(); - private Hashtable _models = new Hashtable(); + private readonly Hashtable _plugins = new(); + private readonly Hashtable _models = new(); - public Warp3D() - { - } + public Warp3D() + { + } public warp_Scene Scene { @@ -22,39 +22,29 @@ namespace Warp3D set { _scene = value; } } - public bool RegisterPlugIN( string name, warp_FXPlugin plugin ) + public bool RegisterPlugIN(string name, warp_FXPlugin plugin) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - _plugins.Add( name, plugin ); + _plugins.Add(name, plugin); return true; } - public void ShiftDefaultCamera( float x, float y, float z ) + public void ShiftDefaultCamera(float x, float y, float z) { - _scene.defaultCamera.shift( x, y, z ); + _scene.defaultCamera.shift(x, y, z); } - public bool AddSphere( string name, float radius, int segments ) + public bool AddSphere(string name, float radius, int segments) { - if ( _scene == null ) - { + if (_scene is null) return false; - } warp_Object o = warp_ObjectFactory.SPHERE(radius, segments); - - if( o == null ) - { - return false; - } - - _scene.addObject( name, o ); - _scene.rebuild(); + _scene.addObject(name, o); + _scene.rebuild(); return true; } @@ -66,733 +56,568 @@ namespace Warp3D public bool AddPlane(string name, float size, bool doubleSide) { - if ( _scene == null ) - { + if (_scene is null) return false; - } warp_Object o = warp_ObjectFactory.SIMPLEPLANE(size, doubleSide); - - if ( o == null ) - { - return false; - } - - _scene.addObject( name, o ); + _scene.addObject(name, o); return true; } - public bool AddCube( string name, float size ) + public bool AddCube(string name, float size) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_Object o = warp_ObjectFactory.CUBE( size ); - - if ( o == null ) - { - return false; - } - - _scene.addObject( name, o ); - _scene.rebuild(); + warp_Object o = warp_ObjectFactory.CUBE(size); + _scene.addObject(name, o); + _scene.rebuild(); return true; } - public bool AddBox( string name, float x, float y, float z ) - { - if ( _scene == null ) - { - return false; - } - - warp_Object o = warp_ObjectFactory.BOX( x, y, z ); - - if ( o == null ) - { - return false; - } - - _scene.addObject( name, o ); - _scene.rebuild(); - - return true; - } - - - public bool ProjectFrontal( string name ) + public bool AddBox(string name, float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_Object o = _scene.sceneobject(name); - if(o == null) - { - return false; - } - - warp_TextureProjector.projectFrontal( o ); + warp_Object o = warp_ObjectFactory.BOX(x, y, z); + _scene.addObject(name, o); + _scene.rebuild(); return true; } - public bool ProjectCylindric( string name ) + + public bool ProjectFrontal(string name) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { + warp_Object o = _scene.sceneobject(name); + if (o is null) return false; - } - - warp_TextureProjector.projectCylindric( o ); + warp_TextureProjector.projectFrontal(o); return true; } - - public bool ShiftObject( string name, float x, float y, float z ) + public bool ProjectCylindric(string name) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { + warp_Object o = _scene.sceneobject(name); + if (o is null) return false; - } - - o.shift( x, y, z ); + warp_TextureProjector.projectCylindric(o); return true; } - public bool SetPos( string name, float x, float y, float z ) - { - if ( _scene == null ) - { - return false; - } - - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { - return false; - } - - o.setPos( x, y, z ); - - return true; - } - - - public bool AddLensFlare( string name ) + public bool ShiftObject(string name, float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_FXLensFlare lensFlare = new warp_FXLensFlare( name, _scene, false ); - lensFlare.preset1(); - - RegisterPlugIN( name, lensFlare ); + warp_Object o = _scene.sceneobject(name); + if (o is null) + return false; + o.shift(x, y, z); return true; } - public bool NormaliseScene() - { - if (_scene == null) - { - return false; - } - - _scene.normalize(); - - return true; - } - - public bool SetAmbient(int c) - { - if (_scene == null) - { - return false; - } - - _scene.setAmbient(c); - - return true; - } - - public bool RotateScene( warp_Quaternion quat, float x, float y, float z ) + public bool SetPos(string name, float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - _scene.rotate( quat, x, y, z ); + warp_Object o = _scene.sceneobject(name); + if (o is null) + return false; + o.setPos(x, y, z); return true; } - public bool RotateScene(float x, float y, float z) - { - if (_scene == null) - { - return false; - } - _scene.rotate(x, y, z); - - return true; - } - - public bool RotateScene( warp_Matrix m ) + public bool AddLensFlare(string name) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - _scene.rotate( m ); + warp_FXLensFlare lensFlare = new(name, _scene, false); + lensFlare.preset1(); + + RegisterPlugIN(name, lensFlare); return true; } - public bool ScaleScene(float x, float y, float z) - { - if (_scene == null) - { - return false; - } - - _scene.scale(x, y, z); - - return true; - } - - public bool TranslateScene(float x, float y, float z) - { - if (_scene == null) - { - return false; - } - - _scene.shift(x, y, z); - - return true; - } - - - public bool RotateObject(string name, float x, float y, float z) - { - if (_scene == null) - { - return false; - } - - warp_Object o = _scene.sceneobject(name); - if(o == null) - { - return false; - } - - o.rotate(x, y, z); - - return true; - } - - public bool RotateSelf( string name, float x, float y, float z ) + public bool NormaliseScene() { - if ( _scene == null ) - { + if (_scene is null) return false; - } - - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { - return false; - } - - o.rotateSelf( x, y, z ); + _scene.normalize(); return true; } - public bool RotateSelf( string name, warp_Matrix m ) + public bool SetAmbient(int c) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { - return false; - } - - o.rotateSelf( m ); + _scene.setAmbient(c); return true; } - public bool RotateSelf( string name, warp_Quaternion quat ) + public bool RotateScene(warp_Quaternion quat, float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { - return false; - } - - o.rotateSelf( quat ); + _scene.rotate(quat, x, y, z); return true; } - - public bool ScaleObject( string name, float s ) + public bool RotateScene(float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - - warp_Object o = _scene.sceneobject( name ); - if ( o == null ) - { - return false; - } - - o.scale( s ); + _scene.rotate(x, y, z); return true; } - public bool SetObjectMaterial(string name, string m) - { - if (_scene == null) - { - return false; - } - - warp_Material material = (warp_Material)_scene.materialData[m]; - if (material == null) - { - return false; - } - - _scene.sceneobject(name).setMaterial(material); - - return true; - } - - public bool SetEnvMap(string name, string path) - { - if (_scene == null) - { - return false; - } - - warp_Texture texture = null; - try - { - texture = new warp_Texture( path ); - } - catch(Exception) - { - return false; - } - - warp_Material material = (warp_Material)_scene.materialData[name]; - if (material == null) - { - return false; - } - - material.setEnvmap(texture); - - return true; - } - - public bool AddLight(string name, float x, float y, float z, int color, int d, int s) - { - if (_scene == null) - { - return false; - } - - _scene.addLight(name,new warp_Light(new warp_Vector(x,y,z),color,d,s)); - - return true; - } - - public bool RotateModelSelf( string name, float x, float y, float z ) + public bool RotateScene(warp_Matrix m) { - if ( _scene == null ) + if (_scene is null) { return false; } - Hashtable model = ( Hashtable )_models[ name ]; - if ( model == null ) - { - return false; - } - - foreach ( DictionaryEntry myDE in model ) - { - string key = ( string )myDE.Key; - warp_Object o = ( warp_Object )myDE.Value; - - o.rotateSelf( x, y, z ); - } + _scene.rotate(m); return true; } - public bool RotateModel( string name, float x, float y, float z ) + public bool ScaleScene(float x, float y, float z) { - if ( _scene == null ) + if (_scene is null) { return false; } - Hashtable model = ( Hashtable )_models[ name ]; - if ( model == null ) - { - return false; - } - - foreach ( DictionaryEntry myDE in model ) - { - string key = ( string )myDE.Key; - warp_Object o = ( warp_Object )myDE.Value; - - o.rotate( x, y, z ); - } + _scene.scale(x, y, z); return true; } - public bool TranslateModel( string name, float x, float y, float z ) + public bool TranslateScene(float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - - Hashtable model = ( Hashtable )_models[ name ]; - if ( model == null ) - { - return false; - } - - foreach ( DictionaryEntry myDE in model ) - { - string key = ( string )myDE.Key; - warp_Object o = ( warp_Object )myDE.Value; - - o.shift( x, y, z ); - } + _scene.shift(x, y, z); return true; } - public bool ScaleModel( string name, float scale ) + + public bool RotateObject(string name, float x, float y, float z) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - Hashtable model = (Hashtable)_models[ name ]; - if ( model == null ) - { + warp_Object o = _scene.sceneobject(name); + if (o is null) return false; - } - - foreach ( DictionaryEntry myDE in model ) - { - string key = ( string )myDE.Key; - warp_Object o = ( warp_Object )myDE.Value; - - o.scaleSelf( scale ); - } + o.rotate(x, y, z); return true; } - public Hashtable Import3Ds(string name, string path, bool addtoscene) - { - if (_scene == null) - { - return null; - } + public bool RotateSelf(string name, float x, float y, float z) + { + if (_scene is null) + return false; - Hashtable list = null; - warp_3ds_Importer studio = new warp_3ds_Importer(); - try - { - list = studio.importFromFile( name, path ); + warp_Object o = _scene.sceneobject(name); + if (o is null) + return false; - if ( addtoscene ) + o.rotateSelf(x, y, z); + return true; + } + + public bool RotateSelf(string name, warp_Matrix m) + { + if (_scene is null) + return false; + + warp_Object o = _scene.sceneobject(name); + if (o is null) + return false; + + o.rotateSelf(m); + return true; + } + + public bool RotateSelf(string name, warp_Quaternion quat) + { + if (_scene is null) + return false; + + warp_Object o = _scene.sceneobject(name); + if (o is null) + return false; + + o.rotateSelf(quat); + return true; + } + + + public bool ScaleObject(string name, float s) + { + if (_scene is null) + return false; + + warp_Object o = _scene.sceneobject(name); + if (o is null) + return false; + + o.scale(s); + return true; + } + + public bool SetObjectMaterial(string name, string m) + { + if (_scene is null) + return false; + + if (_scene.objectData.TryGetValue(name, out warp_Object o) && _scene.materialData.TryGetValue(m, out warp_Material material) && material is not null) + { + o.setMaterial(material); + return true; + } + return false; + } + + public bool SetEnvMap(string name, string path) + { + if (_scene is null) + return false; + + if (_scene.materialData.TryGetValue(name, out warp_Material material) && material is not null) + { + warp_Texture texture; + try { - foreach ( DictionaryEntry myDE in list ) + texture = new warp_Texture(path); + } + catch (Exception) + { + return false; + } + + material.setEnvmap(texture); + return true; + } + return false; + } + + public bool AddLight(string name, float x, float y, float z, int color, int d, int s) + { + if (_scene is null) + return false; + + _scene.addLight(name, new warp_Light(new warp_Vector(x, y, z), color, d, s)); + return true; + } + + public bool RotateModelSelf(string name, float x, float y, float z) + { + if (_scene is null) + return false; + + if (_models[name] is not Hashtable model) + return false; + + foreach (DictionaryEntry myDE in model) + { + warp_Object o = (warp_Object)myDE.Value; + o.rotateSelf(x, y, z); + } + + return true; + } + + public bool RotateModel(string name, float x, float y, float z) + { + if (_scene is null) + return false; + + if (_models[name] is not Hashtable model) + return false; + + foreach (DictionaryEntry myDE in model) + { + warp_Object o = (warp_Object)myDE.Value; + o.rotate(x, y, z); + } + + return true; + } + + public bool TranslateModel(string name, float x, float y, float z) + { + if (_scene is null) + return false; + + if (_models[name] is not Hashtable model) + return false; + + foreach (DictionaryEntry myDE in model) + { + warp_Object o = (warp_Object)myDE.Value; + o.shift(x, y, z); + } + + return true; + } + + public bool ScaleModel(string name, float scale) + { + if (_scene is null) + return false; + + if (_models[name] is not Hashtable model) + return false; + + foreach (DictionaryEntry myDE in model) + { + warp_Object o = (warp_Object)myDE.Value; + o.scaleSelf(scale); + } + + return true; + } + + public Hashtable Import3Ds(string name, string path, bool addtoscene) + { + if (_scene is null) + return null; + + Hashtable list; + warp_3ds_Importer studio = new(); + try + { + list = studio.importFromFile(name, path); + + if (addtoscene) + { + foreach (DictionaryEntry myDE in list) { string key = (string)myDE.Key; warp_Object o = (warp_Object)myDE.Value; - _scene.addObject( key , o ); + _scene.addObject(key, o); } } _scene.rebuild(); - _models.Add( name, list ); - } - catch(Exception) - { - return null; - } + _models.Add(name, list); + } + catch (Exception) + { + return null; + } - return list; - } + return list; + } - public bool SetBackgroundColor(int c) - { - if (_scene == null) - { - return false; - } - - _scene.environment.bgcolor = c; - - return true; - } - - public bool SetBackgroundTexture(string path) - { - if (_scene == null) - { - return false; - } - - warp_Texture texture = null; - try - { - texture = new warp_Texture(path); - } - catch (Exception) - { - return false; - } - - _scene.environment.setBackground(texture); - - return true; - } - - public bool SetBackgroundMaterial( string path ) + public bool SetBackgroundColor(int c) { - if ( _scene == null ) + if (_scene is null) + return false; + + _scene.environment.bgcolor = c; + return true; + } + + public bool SetBackgroundTexture(string path) + { + if (_scene is null) + return false; + + warp_Texture texture; + try + { + texture = new warp_Texture(path); + } + catch (Exception) { return false; } - warp_Material material = null; + _scene.environment.setBackground(texture); + return true; + } + + public bool SetBackgroundMaterial(string path) + { + if (_scene is null) + return false; + + warp_Material material; try { - material = new warp_Material( path ); + material = new warp_Material(path); } - catch ( Exception ) + catch (Exception) { return false; } warp_Texture texture = material.getTexture(); - if ( texture == null ) - { + if (texture is null) return false; - } - - _scene.environment.setBackground( texture ); + _scene.environment.setBackground(texture); return true; } - public bool SetWireframe( string name, bool w ) + public bool SetWireframe(string name, bool w) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_Material material = ( warp_Material )_scene.materialData[ name ]; - if ( material == null ) + if (_scene.materialData.TryGetValue(name, out warp_Material material)) { - return false; + material.setWireframe(w); + return true; } + return false; + } - material.setWireframe( w ); + public bool SetTexture(string name, string path) + { + if (_scene is null) + return false; + if (_scene.materialData.TryGetValue(name, out warp_Material material)) + { + warp_Texture texture; + try + { + texture = new warp_Texture(path); + } + catch (Exception) + { + return false; + } + material.setTexture(texture); + return true; + } + return false; + + } + + public bool AddMaterial(string name) + { + if (_scene is null) + return false; + + warp_Material material = new(); + _scene.addMaterial(name, material); return true; } - public bool SetTexture(string name, string path) - { - if (_scene == null) - { - return false; - } - - warp_Texture texture = null; - try - { - texture = new warp_Texture( path ); - } - catch(Exception) - { - return false; - } - - warp_Material material = (warp_Material)_scene.materialData[name]; - if (material == null) - { - return false; - } - - material.setTexture(texture); - - return true; - } - - public bool AddMaterial(string name) - { - if (_scene == null) - { - return false; - } - - warp_Material material = new warp_Material(); - _scene.addMaterial(name, material); - - return true; - } - - public bool AddMaterial( string name, int color ) + public bool AddMaterial(string name, int color) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - - warp_Material material = new warp_Material( color ); - _scene.addMaterial( name, material ); + warp_Material material = new(color); + _scene.addMaterial(name, material); return true; } - public bool AddMaterial( string name, string path ) + public bool AddMaterial(string name, string path) { - if ( _scene == null ) - { + if (_scene is null) return false; - } - warp_Material material = null; + warp_Material material; try { - material = new warp_Material( path ); + material = new warp_Material(path); } - catch ( Exception ) + catch (Exception) { return false; } - _scene.addMaterial( name, material ); + _scene.addMaterial(name, material); + return true; + } + + public bool SetReflectivity(string name, int r) + { + if (_scene is null) + return false; + + if (_scene.materialData.TryGetValue(name, out warp_Material material)) + { + material.setReflectivity(r); + return true; + } + return false; + } + + public bool Render() + { + try + { + _scene.render(); + + foreach (DictionaryEntry myDE in _plugins) + { + warp_FXPlugin plugin = (warp_FXPlugin)myDE.Value; + plugin.apply(); + } + } + catch (Exception) + { + return false; + } return true; } - public bool SetReflectivity( string name, int r ) - { - if (_scene == null) - { - return false; - } - - warp_Material material = (warp_Material)_scene.materialData[name]; - if (material == null) - { - return false; - } - - material.setReflectivity(r); - - return true; - } - - public bool Render() - { - try - { - _scene.render(); - - foreach ( DictionaryEntry myDE in _plugins ) - { - warp_FXPlugin plugin = ( warp_FXPlugin )myDE.Value; - plugin.apply(); - } - } - catch (Exception) - { - return false; - } - - return true; - } - - public bool CreateScene(int width, int height) - { - try - { - _scene = new warp_Scene(width, height); + public bool CreateScene(int width, int height) + { + try + { + _scene = new warp_Scene(width, height); _plugins.Clear(); _models.Clear(); - } - catch (Exception) - { - Reset(); - return false; - } + } + catch (Exception) + { + Reset(); + return false; + } - return true; - } + return true; + } - public void Reset() - { - _scene = null; + public void Reset() + { + _scene = null; _plugins.Clear(); _models.Clear(); - } - } + } + } } diff --git a/trunk/managed/Warp3Dmod/Warp3D.csproj b/trunk/managed/Warp3Dmod/Warp3D.csproj index 18ae9f71..02e9e7ce 100644 --- a/trunk/managed/Warp3Dmod/Warp3D.csproj +++ b/trunk/managed/Warp3Dmod/Warp3D.csproj @@ -1,186 +1,49 @@ - - + - Debug - AnyCPU - 8.0.50727 - 2.0 - {6E48530B-16AB-4861-9CED-97334739D675} + net8.0 Library - Properties - Warp3D - Warp3D - true + false - SAK - SAK - SAK - SAK - v4.6 - - - - - 2.0 - publish\ - true - Disk false Foreground 7 - Days - false - false - true 0 1.0.0.%2a false false - true + false + False + True + True - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 true false - false + False - pdbonly - true - bin\Release\ - TRACE - prompt - 4 true - false - - - bin\Original\ - true - true - true - false - - - bin\64Bit CPU%27s\ - false + False - true bin\x64\Debug\ - DEBUG;TRACE true true - full - x64 - prompt - false bin\x64\Release\ - TRACE true - true - pdbonly - x64 - prompt - false - - - bin\x64\Original\ - true - true - true - x64 - false - - - bin\x64\64Bit CPU%27s\ - x64 - true - false - - - true - bin\Slixel Tester\ - DEBUG;TRACE - true - true - full - AnyCPU - \rules - true - GlobalSuppressions.cs - prompt - false - - - true - bin\x64\Slixel Tester\ - DEBUG;TRACE - true - true - full - x64 - \rules - true - GlobalSuppressions.cs - prompt - false - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + False + System.Drawing.Common.dll + - - - False - .NET Framework 3.5 SP1 - true - - - \ No newline at end of file diff --git a/trunk/managed/Warp3Dmod/warp_3ds_Importer.cs b/trunk/managed/Warp3Dmod/warp_3ds_Importer.cs index f22f4c6f..f96aadc3 100644 --- a/trunk/managed/Warp3Dmod/warp_3ds_Importer.cs +++ b/trunk/managed/Warp3Dmod/warp_3ds_Importer.cs @@ -5,208 +5,209 @@ using System.Net; namespace Warp3D { - /// - /// Summary description for warp_3ds_Importer. - /// - public class warp_3ds_Importer - { - private int currentJunkId; - private int nextJunkOffset; + /// + /// Summary description for warp_3ds_Importer. + /// + public class warp_3ds_Importer + { + private int currentJunkId; + private int nextJunkOffset; - private String currentObjectName = null; - private warp_Object currentObject = null; - private bool endOfStream = false; + private string currentObjectName = null; + private warp_Object currentObject = null; + private bool endOfStream = false; - private Hashtable _objects = new Hashtable(); + private readonly Hashtable _objects = new Hashtable(); - public warp_3ds_Importer() - { - } + public warp_3ds_Importer() + { + } - public Hashtable importFromFile( string name, String path ) - { - Stream fs = null; + public Hashtable importFromFile(string name, String path) + { + Stream fs; _objects.Clear(); - if ( path.StartsWith( "http" ) ) + if (path.StartsWith("http")) { - WebRequest webrq = WebRequest.Create( path ); - fs = webrq.GetResponse().GetResponseStream() ; + WebRequest webrq = WebRequest.Create(path); + fs = webrq.GetResponse().GetResponseStream(); } else { - fs = new FileStream( path, FileMode.Open ); + fs = new FileStream(path, FileMode.Open); } - BinaryReader br = new BinaryReader( fs ); - return importFromStream( name, br ); - } + BinaryReader br = new BinaryReader(fs); + return importFromStream(name, br); + } - public Hashtable importFromStream( string name, BinaryReader inStream ) - { + public Hashtable importFromStream(string name, BinaryReader inStream) + { _objects.Clear(); - - readJunkHeader(inStream); - if (currentJunkId != 0x4D4D) - { - return null; - } - - try - { - for (;;) - { - readNextJunk( name, inStream); - } - } - catch(Exception) - { + + readJunkHeader(inStream); + if (currentJunkId != 0x4D4D) + { + return null; + } + + try + { + for (; ; ) + { + readNextJunk(name, inStream); + } + } + catch (Exception) + { // ignored - } + } inStream.Close(); return _objects; - } - - private void readJunkHeader(BinaryReader inStream) - { - currentJunkId = readShort(inStream); - nextJunkOffset = readInt(inStream); - endOfStream = currentJunkId < 0; - } + } - private int readInt(BinaryReader inStream) - { - return inStream.ReadByte() | (inStream.ReadByte() << 8) | (inStream.ReadByte() << 16) | (inStream.ReadByte() << 24); - } + private void readJunkHeader(BinaryReader inStream) + { + currentJunkId = readShort(inStream); + nextJunkOffset = readInt(inStream); + endOfStream = currentJunkId < 0; + } - private int readShort(BinaryReader inStream) - { - return (inStream.ReadByte() | (inStream.ReadByte() << 8)); - } + private int readInt(BinaryReader inStream) + { + return inStream.ReadByte() | (inStream.ReadByte() << 8) | (inStream.ReadByte() << 16) | (inStream.ReadByte() << 24); + } - private void readNextJunk(string name, BinaryReader inStream) - { - readJunkHeader(inStream); + private int readShort(BinaryReader inStream) + { + return (inStream.ReadByte() | (inStream.ReadByte() << 8)); + } - if (currentJunkId == 0x3D3D /* mesh block */) - { - return; - } - - if (currentJunkId == 0x4000 /* object block */) - { - currentObjectName = readString(inStream); - return; - } + private void readNextJunk(string name, BinaryReader inStream) + { + readJunkHeader(inStream); - if (currentJunkId == 0x4100 /* triangular polygon object */) - { - currentObject = new warp_Object(); - _objects.Add( name+"_"+currentObjectName, currentObject ); + if (currentJunkId == 0x3D3D /* mesh block */) + { + return; + } - return; - } + if (currentJunkId == 0x4000 /* object block */) + { + currentObjectName = readString(inStream); + return; + } - if (currentJunkId == 0x4110 /* vertex list */) - { - readVertexList(inStream); - return; - } + if (currentJunkId == 0x4100 /* triangular polygon object */) + { + currentObject = new warp_Object(); + _objects.Add(name + "_" + currentObjectName, currentObject); - if (currentJunkId == 0x4120 /* point list */) - { - readPointList(inStream); - return; - } + return; + } - if (currentJunkId == 0x4140 /* mapping coordinates */) - { - readMappingCoordinates(inStream); - return; - } + if (currentJunkId == 0x4110 /* vertex list */) + { + readVertexList(inStream); + return; + } - skipJunk(inStream); - } + if (currentJunkId == 0x4120 /* point list */) + { + readPointList(inStream); + return; + } - private string readString(BinaryReader inStream) - { - string result = ""; - byte inByte; - while ( (inByte = (byte) inStream.ReadByte()) != 0) - { - result += (char) inByte; - } + if (currentJunkId == 0x4140 /* mapping coordinates */) + { + readMappingCoordinates(inStream); + return; + } - return result; - } + skipJunk(inStream); + } - private float readFloat(BinaryReader inStream) - { - int bits = readInt(inStream); + private string readString(BinaryReader inStream) + { + string result = ""; + byte inByte; + while ((inByte = (byte)inStream.ReadByte()) != 0) + { + result += (char)inByte; + } - int s = ((bits >> 31) == 0) ? 1 : -1; - int e = ((bits >> 23) & 0xff); - int m = (e == 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000; + return result; + } - double v = (double)s*(double)m*(Math.Pow(2, e-150)); - - return (float)v; - } + private float readFloat(BinaryReader inStream) + { + int bits = readInt(inStream); - private void skipJunk(BinaryReader inStream) - { - try - { - for (int i = 0; (i < nextJunkOffset - 6) && (!endOfStream); i++) - { - endOfStream = inStream.ReadByte() < 0; - } - } - catch(Exception){endOfStream=true;}; - } + int s = ((bits >> 31) == 0) ? 1 : -1; + int e = ((bits >> 23) & 0xff); + int m = (e == 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000; - private void readVertexList(BinaryReader inStream) - { - float x, y, z; - int vertices = readShort(inStream); - for (int i = 0; i < vertices; i++) - { - x = readFloat(inStream); - y = readFloat(inStream); - z = readFloat(inStream); + double v = (double)s * (double)m * (Math.Pow(2, e - 150)); - currentObject.addVertex(x, -y, z); - } - } + return (float)v; + } - private void readPointList(BinaryReader inStream) - { - int v1, v2, v3; - int triangles = readShort(inStream); - for (int i = 0; i < triangles; i++) - { - v1 = readShort(inStream); - v2 = readShort(inStream); - v3 = readShort(inStream); + private void skipJunk(BinaryReader inStream) + { + try + { + for (int i = 0; (i < nextJunkOffset - 6) && (!endOfStream); i++) + { + endOfStream = inStream.ReadByte() < 0; + } + } + catch (Exception) { endOfStream = true; }; + } - readShort(inStream); + private void readVertexList(BinaryReader inStream) + { + float x, y, z; + int vertices = readShort(inStream); + for (int i = 0; i < vertices; i++) + { + x = readFloat(inStream); + y = readFloat(inStream); + z = readFloat(inStream); - currentObject.addTriangle(currentObject.vertex(v1), - currentObject.vertex(v2), - currentObject.vertex(v3)); - } - } + currentObject.addVertex(x, -y, z); + } + } - private void readMappingCoordinates(BinaryReader inStream) - { - int vertices = readShort(inStream); - for (int i = 0; i < vertices; i++) - { - currentObject.vertex(i).u = readFloat(inStream); - currentObject.vertex(i).v = readFloat(inStream); - } - } - } + private void readPointList(BinaryReader inStream) + { + int v1, v2, v3; + int triangles = readShort(inStream); + for (int i = 0; i < triangles; i++) + { + v1 = readShort(inStream); + v2 = readShort(inStream); + v3 = readShort(inStream); + + readShort(inStream); + + currentObject.addTriangle(currentObject.vertex(v1), + currentObject.vertex(v2), + currentObject.vertex(v3)); + } + } + + private void readMappingCoordinates(BinaryReader inStream) + { + int vertices = readShort(inStream); + for (int i = 0; i < vertices; i++) + { + float u = readFloat(inStream); + float v = readFloat(inStream); + currentObject.vertex(i).setUV(u, v); + } + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Camera.cs b/trunk/managed/Warp3Dmod/warp_Camera.cs index 3c840459..27c33e71 100644 --- a/trunk/managed/Warp3Dmod/warp_Camera.cs +++ b/trunk/managed/Warp3Dmod/warp_Camera.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Warp3D { @@ -8,15 +9,15 @@ namespace Warp3D /// public class warp_Camera { - public warp_Matrix matrix = new warp_Matrix(); - public warp_Matrix projmatrix = new warp_Matrix(); - public warp_Matrix normalmatrix = new warp_Matrix(); + public warp_Matrix matrix = new(); + public warp_Matrix projmatrix = new(); + public warp_Matrix normalmatrix = new(); bool needsRebuild = true; // Flag indicating changes on matrix // Camera settings - public warp_Vector pos = new warp_Vector(0f, 0f, 0f); - public warp_Vector lookat = new warp_Vector(0f, 0f, 0f); + public warp_Vector pos = warp_Vector.Zero; + public warp_Vector lookat = warp_Vector.Zero; public float rollfactor = 0f; public float fovfact; // Field of View factor @@ -39,12 +40,14 @@ namespace Warp3D setFov(fov); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Matrix getMatrix() { rebuildMatrices(); return matrix; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Matrix getNormalMatrix() { rebuildMatrices(); @@ -59,20 +62,19 @@ namespace Warp3D warp_Vector forward, up, right; - forward = warp_Vector.sub(lookat, pos); - if(Math.Abs(forward.x) < 0.001f && Math.Abs(forward.z) < 0.001f) + forward = warp_Vector.sub(ref lookat, ref pos); + if(MathF.Abs(forward.x) < 0.001f && MathF.Abs(forward.z) < 0.001f) { right = new warp_Vector(1f, 0f, 0f); - if(forward.y < 0) - up = new warp_Vector(0f, 0f, 1f); - else - up = new warp_Vector(0f, 0f, -1f); + up = new warp_Vector(0f, 0f, forward.y < 0 ? 1f : -1f); } else { up = new warp_Vector(0f, 1f, 0f); - right = warp_Vector.getNormal(up, forward); - up = warp_Vector.getNormal(forward, right); + right = warp_Vector.vectorProduct(ref up, ref forward); + right.normalize(); + up = warp_Vector.vectorProduct(ref forward, ref right); + up.normalize(); } forward.normalize(); @@ -104,9 +106,10 @@ namespace Warp3D matrix = warp_Matrix.multiply(projmatrix, matrix); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void setFov(float fov) { - fovfact = (float)Math.Tan(warp_Math.deg2rad(fov) / 2); + fovfact = MathF.Tan(warp_Math.deg2rad(fov) / 2); isOrthographic = false; needsRebuild = true; } @@ -126,36 +129,42 @@ namespace Warp3D needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void roll(float angle) { rollfactor += angle; needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void setPos(float px, float py, float pz) { pos = new warp_Vector(px, py, pz); needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void setPos(warp_Vector p) { pos = p; needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void lookAt(float px, float py, float pz) { lookat = new warp_Vector(px, py, pz); needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void lookAt(warp_Vector p) { lookat = p; needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void setScreensize(int w, int h) { screenwidth = w; @@ -166,6 +175,7 @@ namespace Warp3D needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void shift(float dx, float dy, float dz) { pos.x += dx; @@ -177,46 +187,53 @@ namespace Warp3D needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void shift(warp_Vector v) { shift(v.x, v.y, v.z); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void rotate(float dx, float dy, float dz) { pos = pos.transform(warp_Matrix.rotateMatrix(dx, dy, dz)); needsRebuild = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void rotate(warp_Vector v) { rotate(v.x, v.y, v.z); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static warp_Camera FRONT() { - warp_Camera cam = new warp_Camera(); + warp_Camera cam = new(); cam.setPos(0, 0, -2f); return cam; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static warp_Camera LEFT() { - warp_Camera cam = new warp_Camera(); + warp_Camera cam = new(); cam.setPos(2f, 0, 0); return cam; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static warp_Camera RIGHT() { - warp_Camera cam = new warp_Camera(); + warp_Camera cam = new(); cam.setPos(-2f, 0, 0); return cam; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static warp_Camera TOP() { - warp_Camera cam = new warp_Camera(); + warp_Camera cam = new(); cam.setPos(0, -2f, 0); return cam; } diff --git a/trunk/managed/Warp3Dmod/warp_Color.cs b/trunk/managed/Warp3Dmod/warp_Color.cs index c6f4140c..b68f5639 100644 --- a/trunk/managed/Warp3Dmod/warp_Color.cs +++ b/trunk/managed/Warp3Dmod/warp_Color.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Warp3D @@ -8,66 +9,65 @@ namespace Warp3D /// public class warp_Color { - public static int MASKALPHA = unchecked((int)0xFF000000); // alpha mask - public static int MASKHALFALPHA = unchecked((int)0x7F000000); // alpha mask - public static int MASKRED = unchecked((int)0xFF0000); // red mask - public static int MASKGREEN = unchecked((int)0xFF00); // green mask - public static int MASKBLUE = unchecked((int)0xFF); // blue mask - public static int MASKRB = unchecked((int)0x00FF00FF); // mask for additive/subtractive shading - public static int MASKAG = unchecked((int)0xFF00FF00); // mask for additive/subtractive shading - public static int RBHALF = unchecked((int)0x00800080); - public static int MASKRBHALF = unchecked((int)0xFF80FF80); - public static int MASKRBOVER = unchecked((int)0x01000100); - public static int MASK7Bit = unchecked((int)0x00FEFEFF); // mask for additive/subtractive shading - public static int MASK6Bit = unchecked((int)0x00FCFCFC); // mask for additive/subtractive shading - public static int MASK7BitA = unchecked((int)0x00FEFEFF); // mask for additive/subtractive shading - public static int MASK6BitA = unchecked((int)0x00FCFCFC); // mask for additive/subtractive shading - public static int MASKRGB = unchecked((int)0x00FFFFFF); // rgb mask + public const int MASKALPHA = unchecked((int)0xFF000000); // alpha mask + public const int MASKHALFALPHA = unchecked((int)0x7F000000); // alpha mask + public const int MASKRED = unchecked((int)0xFF0000); // red mask + public const int MASKGREEN = unchecked((int)0xFF00); // green mask + public const int MASKBLUE = unchecked((int)0xFF); // blue mask + public const int MASKRB = unchecked((int)0x00FF00FF); // mask for additive/subtractive shading + public const int MASKAG = unchecked((int)0xFF00FF00); // mask for additive/subtractive shading + public const int RBHALF = unchecked((int)0x00800080); + public const int MASKRBHALF = unchecked((int)0xFF80FF80); + public const int MASKRBOVER = unchecked((int)0x01000100); + public const int MASK7Bit = unchecked((int)0x00FEFEFF); // mask for additive/subtractive shading + public const int MASK6Bit = unchecked((int)0x00FCFCFC); // mask for additive/subtractive shading + public const int MASK7BitA = unchecked((int)0x00FEFEFF); // mask for additive/subtractive shading + public const int MASK6BitA = unchecked((int)0x00FCFCFC); // mask for additive/subtractive shading + public const int MASKRGB = unchecked((int)0x00FFFFFF); // rgb mask - public static int White = unchecked((int)0xFFFFFFFF); - public static int Grey = unchecked((int)0xFF7F7F7F); - public static int Black = unchecked((int)0xFF000000); + public const int White = unchecked((int)0xFFFFFFFF); + public const int Grey = unchecked((int)0xFF7F7F7F); + public const int Black = unchecked((int)0xFF000000); public static int random(int color, int delta) { - Random rnd = new Random(); - int r = (color >> 16) & 0xff; int g = (color >> 8) & 0xff; int b = color & 0xff; - r += (int)(rnd.NextDouble() * (float)delta); - g += (int)(rnd.NextDouble() * (float)delta); - b += (int)(rnd.NextDouble() * (float)delta); + float df = delta; + r += (int)(Random.Shared.NextSingle() * df); + g += (int)(Random.Shared.NextSingle() * df); + b += (int)(Random.Shared.NextSingle() * df); return getCropColor(r, g, b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int random() { - Random rnd = new Random(); - return (int)(rnd.NextDouble() * 16777216); + return (int)(Random.Shared.NextSingle() * 16777216f); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getAlpha(int c) { return (c >> 24) & 0xff; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getRed(int c) { return (c >> 16) & 0xff; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getGreen(int c) { return (c >> 8) & 0xff; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getBlue(int c) { return c & 0xff; @@ -77,49 +77,50 @@ namespace Warp3D { int[] pal = new int[size]; int c1, c2, pos1, pos2, range; - int r, g, b, r1, g1, b1, r2, g2, b2, dr, dg, db; - if(colors.GetLength(0) == 1) + int r1, g1, b1, r2, g2, b2, dr, dg, db; + int clen = colors.GetLength(0) - 1; + if (clen == 0) { c1 = colors[0]; - for(int i = 0; i < size; i++) + for(int i = 0; i < pal.Length; i++) { pal[i] = c1; } return pal; } - - for(int c = 0; c < colors.GetLength(0) - 1; c++) + ref int cref = ref MemoryMarshal.GetArrayDataReference(colors); + ref int palref = ref MemoryMarshal.GetArrayDataReference(pal); + int invclenm1 = 1 / clen; + for (int c = 0; c < clen; c++) { - c1 = colors[c]; - c2 = colors[c + 1]; - pos1 = size * c / (colors.GetLength(0) - 1); - pos2 = size * (c + 1) / (colors.GetLength(0) - 1); + c1 = Unsafe.As(ref Unsafe.Add(ref cref, c)); + c2 = Unsafe.As(ref Unsafe.Add(ref cref, c + 1)); + pos1 = size * c * invclenm1; + pos2 = size * (c + 1) * invclenm1; range = pos2 - pos1; + range = 1 / range; r1 = warp_Color.getRed(c1) << 16; g1 = warp_Color.getGreen(c1) << 16; b1 = warp_Color.getBlue(c1) << 16; r2 = warp_Color.getRed(c2) << 16; g2 = warp_Color.getGreen(c2) << 16; b2 = warp_Color.getBlue(c2) << 16; - dr = (r2 - r1) / range; - dg = (g2 - g1) / range; - db = (b2 - b1) / range; - r = r1; - g = g1; - b = b1; + dr = (r2 - r1) * range; + dg = (g2 - g1) * range; + db = (b2 - b1) * range; for(int i = pos1; i < pos2; i++) { - pal[i] = getColor(r >> 16, g >> 16, b >> 16); - r += dr; - g += dg; - b += db; + Unsafe.As(ref Unsafe.Add(ref palref, i)) = getColor(r1 >> 16, g1 >> 16, b1 >> 16); + r1 += dr; + g1 += dg; + b1 += db; } } return pal; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int add(int color1, int color2) { int t1,t2,t3,t4; @@ -139,57 +140,24 @@ namespace Warp3D return (t4 << 8) | t3; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int avg2c(int color1, int color2) { - int t1,t2,t3,t4; + int t1 = (color1 & MASKRB) + (color2 & MASKRB); + t1 >>= 1; + t1 &= MASKRB; - t1 = color1 & MASKRB; - t2 = color2 & MASKRB; - t3 = t1 + t2 + 0x00010001; - t3 >>= 1; - t3 &= MASKRB; + long t2 = (color1 & MASKAG) + (color2 & MASKAG); + t2 >>= 1; + t2 &= MASKAG; - t1 = (color1 >> 8) & MASKRB; - t2 = (color2 >> 8) & MASKRB; - t4 = t1 + t2 + 0x00010001; - t4 >>= 1; - t4 &= MASKRB; - - return (t4 << 8) | t3; + return (int)t2 | t1; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static int avg4c(int color1, int color2, int color3, int color4) - { - int t1,t2,t3,t4; - - t1 = color1 & MASKRB; - t2 = color2 & MASKRB; - t3 = t1 + t2; - t1 = color3 & MASKRB; - t2 = color4 & MASKRB; - t3 += t1 + t2 + 0x00020002; - t3 >>= 2; - t3 &= MASKRB; - - t1 = (color1 >> 8) & MASKRB; - t2 = (color2 >> 8) & MASKRB; - t4 = t1 + t2 + 0x00020002;; - t1 = (color3 >> 8) & MASKRB; - t2 = (color4 >> 8) & MASKRB; - t4 += t1 + t2; - t4 >>= 2; - t4 &= MASKRB; - - return (t4 << 8) | t3; - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int sub(int color1, int color2) // Substracts color2 from color1 { - int t1,t2,t3,t4; t1 = color1 & MASKRB; @@ -205,15 +173,8 @@ namespace Warp3D t4 &= MASKRB; return (t4 << 8) | t3; - -/* - int pixel = (color1 & MASK7Bit) + (~color2 & MASK7Bit); - int overflow = ~pixel & 0x1010100; - overflow = overflow - (overflow >> 8); - return ALPHA | (~overflow & pixel); -*/ } -/* + /* public static int subneg(int color1, int color2) // Substracts the negative of color2 from color1 { @@ -229,15 +190,15 @@ namespace Warp3D color = (~color) & RGB; return ALPHA | color; } -*/ - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int mix(int color1, int color2) // Returns the averidge color from 2 colors { return MASKALPHA | (((color1 & MASK7Bit) >> 1) + ((color2 & MASK7Bit) >> 1)); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int scale(int color, int factor) { if(factor == 255) @@ -256,7 +217,7 @@ namespace Warp3D return a | RB | b; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int multiply(int color1, int color2) { int t1, t2, t3, t4; @@ -276,46 +237,25 @@ namespace Warp3D t4 = (t1 & 0xff) * (t2 & 0xff); // g on g t4 |= (t1 & MASKRED) * ((t2 >> 16) & 0xff); // a on a t4 += RBHALF; // add halfbit - t4 = (t4 + ((t4 >> 8) & MASKRB)); + t4 += (t4 >> 8) & MASKRB; t4 &= MASKAG; return t3 | t4; -/* - int a; - int r = (color1 >> 24 ) & 0xff; - int g = (color2 >> 24 ) & 0xff; - if( r == 0xff && g == 0xff) - a = ALPHA; - else - { - a = ((r * g)) & 0xff00; - a <<= 16; - } - - if((color1 & RGB) == 0 && (color2 & RGB) == 0) - return a; - - r = unchecked((((color1 >> 16) & 0xff) * ((color2 >> 16) & 0xff)) & 0xff00); - g = (((color1 >> 8) & 0xff) * ((color2 >> 8) & 0xff)) & 0xff00; - int b = ((color1 & 0xff) * (color2 & 0xff)) >> 8; - return a | r << 8 | g | b; -*/ } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int transparency(int bkgrd, int color, int alpha) { - if(alpha == 0) + switch(alpha) { - return bkgrd; - } - if(alpha == 255) - { - return color; - } - if(alpha == 127) - { - return mix(bkgrd, color); + case 0: + return bkgrd; + case 255: + return color; + case 127: + return mix(bkgrd, color); + default: + break; } int colorRB = bkgrd & 0xff00ff; @@ -329,22 +269,21 @@ namespace Warp3D return MASKALPHA | colorRB | colorg; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] // blends color alpha, but result image is solid public static int overSolid(int bkgrd, int color) { int alpha = color & MASKALPHA; - if(alpha == 0) + switch(alpha) { - return bkgrd; - } - if(alpha == Black) - { - return color; - } - if(alpha == MASKHALFALPHA) - { - return mix(bkgrd, color); + case 0: + return bkgrd; + case Black: + return color; + case MASKHALFALPHA: + return mix(bkgrd, color); + default: + break; } alpha = (alpha >> 24) & 0xff; int colorRB = bkgrd & MASKRB; @@ -358,34 +297,31 @@ namespace Warp3D return MASKALPHA | colorRB | colorg; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getCropColor(int r, int g, int b) { return MASKALPHA | (warp_Math.crop(r, 0, 255) << 16) | (warp_Math.crop(g, 0, 255) << 8) | warp_Math.crop(b, 0, 255); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getCropColor(int r, int g, int b, int a) { - return (warp_Math.crop(a , 0, 255) << 24) | - (warp_Math.crop(r, 0, 255) << 16) | - (warp_Math.crop(g, 0, 255) << 8) | - warp_Math.crop(b, 0, 255); + return (warp_Math.crop(a, 0, 255) << 24) | (warp_Math.crop(r, 0, 255) << 16) | (warp_Math.crop(g, 0, 255) << 8) | warp_Math.crop(b, 0, 255); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getColor(int r, int g, int b) { return MASKALPHA | (r << 16) | (g << 8) | b; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getColor(int r, int g, int b, int a) { return (a << 24) | (r << 16) | (g << 8) | b; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getGray(int color) { int r = ((color & MASKRED) >> 16); @@ -395,7 +331,7 @@ namespace Warp3D return (color & MASKALPHA) | (Y << 16) | (Y << 8) | Y; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int getAverage(int color) { return (((color & MASKRED) >> 16) + ((color & MASKGREEN) >> 8) + (color & MASKBLUE)) / 3; diff --git a/trunk/managed/Warp3Dmod/warp_CoreObject.cs b/trunk/managed/Warp3Dmod/warp_CoreObject.cs index 4f121f49..ee41d03d 100644 --- a/trunk/managed/Warp3Dmod/warp_CoreObject.cs +++ b/trunk/managed/Warp3Dmod/warp_CoreObject.cs @@ -1,115 +1,134 @@ using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace Warp3D { - /// - /// Summary description for warp_CoreObject. - /// - /// + /// + /// Summary description for warp_CoreObject. + /// + /// - public class warp_CoreObject - { - public warp_Matrix matrix=new warp_Matrix(); - public warp_Matrix normalmatrix=new warp_Matrix(); + public class warp_CoreObject + { + public warp_Matrix matrix = new(); + public warp_Matrix normalmatrix = new(); - public void transform(warp_Matrix m) - { - matrix.transform(m); - normalmatrix.transform(m); - } - - public void shift(float dx, float dy, float dz) - { - matrix.shift(dx,dy,dz); - } - - public void shift( warp_Vector v ) - { - matrix.shift(v.x,v.y,v.z); - } - - public void scale(float d) - { - matrix.scale(d); - } - - public void scale(float dx, float dy, float dz) - { - matrix.scale(dx,dy,dz); - } - - public void scaleSelf(float d) - { - matrix.scaleSelf(d); - } - - public void scaleSelf(float dx, float dy, float dz) - { - matrix.scaleSelf(dx,dy,dz); - } - - public void rotate(warp_Vector d) - { - rotateSelf(d.x,d.y,d.z); - } - - public void rotateSelf(warp_Vector d) - { - rotateSelf(d.x,d.y,d.z); - } - - public void rotate(float dx, float dy, float dz) - { - matrix.rotate(dx,dy,dz); - normalmatrix.rotate(dx,dy,dz); - } - - public void rotate( warp_Quaternion quat, float x, float y, float z ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void transform(warp_Matrix m) { - matrix.rotate( quat , x, y, z); - normalmatrix.rotate( quat , x, y, z); + matrix.transform(m); + normalmatrix.transform(m); } - public void rotate( warp_Matrix m ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void shift(float dx, float dy, float dz) { - matrix.rotate( m ); - normalmatrix.rotate( m ); + matrix.shift(dx, dy, dz); } - - public void rotateSelf(float dx, float dy, float dz) - { - matrix.rotateSelf(dx,dy,dz); - normalmatrix.rotateSelf(dx,dy,dz); - } - - public void rotateSelf( warp_Quaternion quat ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void shift(warp_Vector v) { - matrix.rotateSelf( quat ); - normalmatrix.rotateSelf( quat ); + matrix.shift(v.x, v.y, v.z); } - public void rotateSelf( warp_Matrix m ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void scale(float d) { - matrix.rotateSelf( m ); - normalmatrix.rotateSelf( m ); + matrix.scale(d); } - public void setPos( float x, float y, float z ) - { - matrix.m03=x; - matrix.m13=y; - matrix.m23=z; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void scale(float dx, float dy, float dz) + { + matrix.scale(dx, dy, dz); + } - public void setPos(warp_Vector v) - { - setPos(v.x,v.y,v.z); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void scaleSelf(float d) + { + matrix.scaleSelf(d); + } - public warp_Vector getPos() - { - return new warp_Vector(matrix.m03,matrix.m13,matrix.m23); - } - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void scaleSelf(float dx, float dy, float dz) + { + matrix.scaleSelf(dx, dy, dz); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotate(warp_Vector d) + { + rotateSelf(d.x, d.y, d.z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotateSelf(warp_Vector d) + { + rotateSelf(d.x, d.y, d.z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotate(float dx, float dy, float dz) + { + matrix.rotate(dx, dy, dz); + normalmatrix.rotate(dx, dy, dz); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotate(warp_Quaternion quat, float x, float y, float z) + { + matrix.rotate(quat, x, y, z); + normalmatrix.rotate(quat, x, y, z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotate(warp_Matrix m) + { + matrix.rotate(m); + normalmatrix.rotate(m); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotateSelf(float dx, float dy, float dz) + { + matrix.rotateSelf(dx, dy, dz); + normalmatrix.rotateSelf(dx, dy, dz); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotateSelf(warp_Quaternion quat) + { + matrix.rotateSelf(quat); + normalmatrix.rotateSelf(quat); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void rotateSelf(warp_Matrix m) + { + matrix.rotateSelf(m); + normalmatrix.rotateSelf(m); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setPos(float x, float y, float z) + { + matrix.m03 = x; + matrix.m13 = y; + matrix.m23 = z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setPos(warp_Vector v) + { + setPos(v.x, v.y, v.z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public warp_Vector getPos() + { + return new warp_Vector(matrix.m03, matrix.m13, matrix.m23); + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Edge.cs b/trunk/managed/Warp3Dmod/warp_Edge.cs index caa0d69f..fad6fe36 100644 --- a/trunk/managed/Warp3Dmod/warp_Edge.cs +++ b/trunk/managed/Warp3Dmod/warp_Edge.cs @@ -2,32 +2,32 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_Edge. - /// - /// - public class warp_Edge - { - warp_Vertex a,b; + /// + /// Summary description for warp_Edge. + /// + /// + public class warp_Edge + { + warp_Vertex a, b; - public warp_Edge() - { - } + public warp_Edge() + { + } - public warp_Edge(warp_Vertex v1, warp_Vertex v2) - { - a=v1; - b=v2; - } + public warp_Edge(warp_Vertex v1, warp_Vertex v2) + { + a = v1; + b = v2; + } - public warp_Vertex start() - { - return a; - } - - public warp_Vertex end() - { - return b; - } - } + public warp_Vertex start() + { + return a; + } + + public warp_Vertex end() + { + return b; + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Environment.cs b/trunk/managed/Warp3Dmod/warp_Environment.cs index 02931996..9add65bb 100644 --- a/trunk/managed/Warp3Dmod/warp_Environment.cs +++ b/trunk/managed/Warp3Dmod/warp_Environment.cs @@ -2,21 +2,21 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_Environment. - /// - public class warp_Environment - { - public int ambient=0; - public int fogcolor=0; - public int fogfact=0; - public int bgcolor= warp_Color.White; + /// + /// Summary description for warp_Environment. + /// + public class warp_Environment + { + public int ambient = 0; + public int fogcolor = 0; + public int fogfact = 0; + public int bgcolor = warp_Color.White; - public warp_Texture background=null; + public warp_Texture background = null; - public void setBackground(warp_Texture t) - { - background=t; - } - } + public void setBackground(warp_Texture t) + { + background = t; + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_FXLensFlare .cs b/trunk/managed/Warp3Dmod/warp_FXLensFlare .cs index b6e1b64b..0b66fad2 100644 --- a/trunk/managed/Warp3Dmod/warp_FXLensFlare .cs +++ b/trunk/managed/Warp3Dmod/warp_FXLensFlare .cs @@ -6,347 +6,346 @@ namespace Warp3D { public class warp_FXLensFlare : warp_FXPlugin { - public warp_Object flareObject; - - private int flares=0; - private bool zBufferSensitive=true; - private warp_Texture[] flare; - private float[] flareDist; - - private warp_FXLensFlare(warp_Scene scene) : base( scene ) - { - } + public warp_Object flareObject; - public warp_FXLensFlare( String name, warp_Scene scene, bool zBufferSensitive ) : base( scene ) - { + private int flares = 0; + private bool zBufferSensitive = true; + private warp_Texture[] flare; + private float[] flareDist; + + private warp_FXLensFlare(warp_Scene scene) : base(scene) + { + } + + public warp_FXLensFlare(string name, warp_Scene scene, bool zBufferSensitive) : base(scene) + { this.zBufferSensitive = zBufferSensitive; - flareObject=new warp_Object(); - flareObject.addVertex(new warp_Vertex(1f,1f,1f)); - flareObject.rebuild(); + flareObject = new(); + flareObject.addVertex(new warp_Vertex(1f, 1f, 1f)); + flareObject.rebuild(); - scene.addObject(name, flareObject); - } - - public void preset1() - { - clear(); + scene.addObject(name, flareObject); + } - addGlow(144,0x330099); - addStrike(320,48,0x003366); - addStrike(48,240,0x660033); - addRing(120,0x660000); - addRays(320, 32, 20, 0x111111); - addSecs(12,100,64,0x6633cc,64); - } - - public void preset2() - { - clear(); - addGlow(144,0x995500); - addStrike(640,64,0xCC0000); - addStrike(32,480,0x0000FF); - addStrike(64,329,0x330099); - addRing(160,0x990000); - addRays(320, 24, 32, 0x332211); - addSecs(12,100,64,0x333333,100); - addSecs(12,60,40,0x336699,80); - } - - public void preset3() - { - clear(); - addGlow(144,0x993322); - addStrike(400,200,0xCC00FF); - addStrike(480,32,0x0000FF); - addRing(180,0x990000); - addRays(240, 32, 48, 0x332200); - addSecs(12,80,64,0x555555,50); - addSecs(12,60,40,0x336699,80); - } - - public void setPos(warp_Vector pos) - { - flareObject.fastvertex[0].pos=pos; - } - - public void clear() - { - flares=0; - flare=null; - flareDist=null; - } - - public void addGlow(int size, int color) - { - addFlare(createGlow(size,size,color,256),0f); - } - - public void addStrike(int width, int height, int color) - { - addFlare(createGlow(width,height,color,48),0f); - } - - public void addRing(int size, int color) - { - addFlare(createRing(size,color),0f); - } - - public void addRays(int size, int num, int rad, int color) - { - addFlare(createRays(size, num, rad, color),0f); - } - - public void addSecs( int count, int averidgeSize, int sizeDelta, int averidgeColor, int colorDelta ) + public void preset1() { - for ( int i = 0; i < count; i++ ) + clear(); + + addGlow(144, 0x330099); + addStrike(320, 48, 0x003366); + addStrike(48, 240, 0x660033); + addRing(120, 0x660000); + addRays(320, 32, 20, 0x111111); + addSecs(12, 100, 64, 0x6633cc, 64); + } + + public void preset2() + { + clear(); + addGlow(144, 0x995500); + addStrike(640, 64, 0xCC0000); + addStrike(32, 480, 0x0000FF); + addStrike(64, 329, 0x330099); + addRing(160, 0x990000); + addRays(320, 24, 32, 0x332211); + addSecs(12, 100, 64, 0x333333, 100); + addSecs(12, 60, 40, 0x336699, 80); + } + + public void preset3() + { + clear(); + addGlow(144, 0x993322); + addStrike(400, 200, 0xCC00FF); + addStrike(480, 32, 0x0000FF); + addRing(180, 0x990000); + addRays(240, 32, 48, 0x332200); + addSecs(12, 80, 64, 0x555555, 50); + addSecs(12, 60, 40, 0x336699, 80); + } + + public void setPos(warp_Vector pos) + { + flareObject.vertexData[0].pos = pos; + } + + public void clear() + { + flares = 0; + flare = null; + flareDist = null; + } + + public void addGlow(int size, int color) + { + addFlare(createGlow(size, size, color, 256), 0f); + } + + public void addStrike(int width, int height, int color) + { + addFlare(createGlow(width, height, color, 48), 0f); + } + + public void addRing(int size, int color) + { + addFlare(createRing(size, color), 0f); + } + + public void addRays(int size, int num, int rad, int color) + { + addFlare(createRays(size, num, rad, color), 0f); + } + + public void addSecs(int count, int averidgeSize, int sizeDelta, int averidgeColor, int colorDelta) + { + for (int i = 0; i < count; i++) { - addFlare( createSec( averidgeSize, sizeDelta, averidgeColor, colorDelta ), warp_Math.random( -0.5f, 3f ) ); + addFlare(createSec(averidgeSize, sizeDelta, averidgeColor, colorDelta), warp_Math.random(-0.5f, 3f)); } } - - override public void apply() - { - int px = flareObject.fastvertex[ 0 ].x; - int py = flareObject.fastvertex[ 0 ].y; - if ( !flareObject.fastvertex[ 0 ].visible ) - { + override public void apply() + { + if (flareObject.vertexData[0].clipcode > 0) return; - } - if ( zBufferSensitive && ( flareObject.fastvertex[ 0 ].z > scene.renderPipeline.zBuffer[ px + py * screen.width ] ) ) + + int px = flareObject.vertexData[0].x; + int py = flareObject.vertexData[0].y; + + if (zBufferSensitive && (flareObject.vertexData[0].z > scene.renderPipeline.zBuffer[px + py * screen.width])) { return; } - int cx=screen.width/2; - int cy=screen.height/2; + int cx = screen.width / 2; + int cy = screen.height / 2; - float dx=(float)(cx-px); - float dy=(float)(cy-py); - int posx,posy,xsize,ysize; - float zoom; - - for (int i=0; i=0;y--) - { - offset=y*w; - for (int x=w-1;x>=0;x--) - { - relX=(float)(x-(w>>1))/(float)(w>>1); - relY=(float)(y-(h>>1))/(float)(h>>1); - newTexture.pixel[offset+x]=palette[warp_Math.crop((int)(255*Math.Sqrt(relX*relX+relY*relY)),0,255)]; - } - } + for (int i = 0; i < flares; i++) + { + zoom = warp_Math.pythagoras(dx, dy) / warp_Math.pythagoras(cx, cy); + zoom = (1 - zoom) / 2 + 1; + xsize = flare[i].width; + ysize = flare[i].height; + posx = px + (int)(dx * flareDist[i]); + posy = py + (int)(dy * flareDist[i]); - return newTexture; - } - - private int[] getPalette(int[] color, int[] alpha) - { - int r,g,b; - int[] palette=new int[256]; - for (int i=255;i>=0;i--) - { - r=(((color[i]>>16)&255)*alpha[i])>>8; - g=(((color[i]>>8)&255)*alpha[i])>>8; - b=((color[i]&255)*alpha[i])>>8; - palette[i]=warp_Color.getColor(r,g,b); - } - return palette; - } - - private warp_Texture createGlow(int w, int h, int color, int alpha) - { - return createRadialTexture(w,h,getGlowPalette(color),getConstantAlpha(alpha)); - } - - private warp_Texture createRing(int size, int color) - { - return createRadialTexture(size,size,getColorPalette(color,color),getRingAlpha(40)); - } - - private warp_Texture createSec(int size, int sizedelta, int color, int colordelta) - { - int s=(int)warp_Math.randomWithDelta(size,sizedelta); - int c1=warp_Color.random(color,colordelta); - int c2=warp_Color.random(color,colordelta); - return createRadialTexture(s,s,getColorPalette(c1,c2),getSecAlpha()); - } - - private warp_Texture createRays(int size, int rays, int rad, int color) - { - int pos; - float relPos; - warp_Texture texture=new warp_Texture(size,size); - int[] radialMap=new int [1024]; - warp_Math.clearBuffer(radialMap, 0); + screen.add(flare[i], posx - xsize / 2, posy - ysize / 2, xsize, ysize); + } + } - for (int i=0;i=0;y--) - { - offset=y*size; - for (int x=size-1;x>=0;x--) - { - xrel=(float)(2*x-size)/(float)size; - yrel=(float)(2*y-size)/(float)size; - angle=(int)(1023*Math.Atan2(xrel,yrel)/3.14159/2)&1023; - reldist=Math.Max((int)(255-255*warp_Math.pythagoras(xrel,yrel)),0); - texture.pixel[x+offset]=warp_Color.scale(color, radialMap[angle]*reldist/255); - } - } + if (flares == 1) + { + flare = new warp_Texture[1]; + flareDist = new float[1]; + } + else + { + warp_Texture[] temp1 = new warp_Texture[flares]; + System.Array.Copy(flare, 0, temp1, 0, flares - 1); // check this + flare = temp1; - return texture; - } - - private int[] getGlowPalette(int color) - { - int r,g,b; - float relDist,diffuse,specular; - int[] palette=new int[256]; - int cr=(color>>16)&255; - int cg=(color>>8)&255; - int cb=color&255; - for (int i=255;i>=0;i--) - { - relDist=(float)i/255; - diffuse=(float)Math.Cos(relDist*1.57); - specular=(float)(255/Math.Pow(2.718,relDist*2.718)-(float)i/16); - r=(int)((float)cr*diffuse+specular); - g=(int)((float)cg*diffuse+specular); - b=(int)((float)cb*diffuse+specular); - palette[i]=warp_Color.getCropColor(r,g,b); - } - + float[] temp2 = new float[flares]; + System.Array.Copy(flareDist, 0, temp2, 0, flares - 1); + flareDist = temp2; + } + + flare[flares - 1] = texture; + flareDist[flares - 1] = relPos; + } + + private warp_Texture createRadialTexture(int w, int h, int[] colormap, int[] alphamap) + { + int offset; + float relX, relY; + warp_Texture newTexture = new warp_Texture(w, h); + int[] palette = getPalette(colormap, alphamap); + + for (int y = h - 1; y >= 0; y--) + { + offset = y * w; + for (int x = w - 1; x >= 0; x--) + { + relX = (float)(x - (w >> 1)) / (float)(w >> 1); + relY = (float)(y - (h >> 1)) / (float)(h >> 1); + newTexture.pixel[offset + x] = palette[warp_Math.crop((int)(255 * Math.Sqrt(relX * relX + relY * relY)), 0, 255)]; + } + } + + return newTexture; + } + + private int[] getPalette(int[] color, int[] alpha) + { + int r, g, b; + int[] palette = new int[256]; + for (int i = 255; i >= 0; i--) + { + r = (((color[i] >> 16) & 255) * alpha[i]) >> 8; + g = (((color[i] >> 8) & 255) * alpha[i]) >> 8; + b = ((color[i] & 255) * alpha[i]) >> 8; + palette[i] = warp_Color.getColor(r, g, b); + } return palette; - } - - private int[] getConstantAlpha(int alpha) - { - int[] alphaPalette=new int[256]; - for (int i=255;i>=0;i--) alphaPalette[i]=alpha; - - return alphaPalette; - } - - private int[] getLinearAlpha() - { - int[] alphaPalette=new int[256]; - for ( int i = 255; i >= 0; i-- ) + } + + private warp_Texture createGlow(int w, int h, int color, int alpha) + { + return createRadialTexture(w, h, getGlowPalette(color), getConstantAlpha(alpha)); + } + + private warp_Texture createRing(int size, int color) + { + return createRadialTexture(size, size, getColorPalette(color, color), getRingAlpha(40)); + } + + private warp_Texture createSec(int size, int sizedelta, int color, int colordelta) + { + int s = (int)warp_Math.randomWithDelta(size, sizedelta); + int c1 = warp_Color.random(color, colordelta); + int c2 = warp_Color.random(color, colordelta); + return createRadialTexture(s, s, getColorPalette(c1, c2), getSecAlpha()); + } + + private warp_Texture createRays(int size, int rays, int rad, int color) + { + int pos; + float relPos; + warp_Texture texture = new warp_Texture(size, size); + int[] radialMap = new int[1024]; + warp_Math.clearBuffer(radialMap, 0); + + for (int i = 0; i < rays; i++) { - alphaPalette[ i ] = 255 - i; - } - - return alphaPalette; - } - - private int[] getRingAlpha(int ringsize) - { - int[] alphaPalette=new int[256]; - float angle; - for ( int i = 0; i < 256; i++ ) - { - alphaPalette[ i ] = 0; + pos = (int)warp_Math.random(rad, 1023 - rad); + for (int k = pos - rad; k <= pos + rad; k++) + { + relPos = (float)(k - pos + rad) / (float)(rad * 2); + radialMap[k] += (int)(255 * (1f + MathF.Sin((relPos - 0.25f) * 3.14159f * 2f)) / 2f); + } } - for (int i=0;i= 0; y--) { - alphaPalette[ i ] = ( alphaPalette[ i ] + 255 - i ) >> 2; + offset = y * size; + for (int x = size - 1; x >= 0; x--) + { + xrel = (float)(2 * x - size) / (float)size; + yrel = (float)(2 * y - size) / (float)size; + angle = (int)(1023 * MathF.Atan2(xrel, yrel) / 3.14159 / 2) & 1023; + reldist = Math.Max((int)(255 - 255 * warp_Math.pythagoras(xrel, yrel)), 0); + texture.pixel[x + offset] = warp_Color.scale(color, radialMap[angle] * reldist / 255); + } } - + + return texture; + } + + private int[] getGlowPalette(int color) + { + int r, g, b; + float relDist, diffuse, specular; + int[] palette = new int[256]; + int cr = (color >> 16) & 255; + int cg = (color >> 8) & 255; + int cb = color & 255; + for (int i = 255; i >= 0; i--) + { + relDist = (float)i / 255f; + diffuse = MathF.Cos(relDist * 1.57f); + specular = (float)(255f / MathF.Pow(2.718f, relDist * 2.718f) - (float)i / 16f); + r = (int)((float)cr * diffuse + specular); + g = (int)((float)cg * diffuse + specular); + b = (int)((float)cb * diffuse + specular); + palette[i] = warp_Color.getCropColor(r, g, b); + } + + return palette; + } + + private int[] getConstantAlpha(int alpha) + { + int[] alphaPalette = new int[256]; + for (int i = 255; i >= 0; i--) alphaPalette[i] = alpha; + return alphaPalette; - } - - - private int[] getColorPalette(int color1, int color2) - { - int[] palette=new int[256]; + } - int r1=(color1>>16)&255; - int g1=(color1>>8)&255; - int b1=color1&255; - int r2=(color2>>16)&255; - int g2=(color2>>8)&255; - int b2=color2&255; - int dr=r2-r1; - int dg=g2-g1; - int db=b2-b1; - int r=r1<<8; - int g=g1<<8; - int b=b1<<8; + private int[] getLinearAlpha() + { + int[] alphaPalette = new int[256]; + for (int i = 255; i >= 0; i--) + { + alphaPalette[i] = 255 - i; + } - for (int i=0;i<256;i++) - { - palette[i]=warp_Color.getColor(r>>8,g>>8,b>>8); - r+=dr; g+=dg; b+=db; - } - return palette; - } + return alphaPalette; + } + + private int[] getRingAlpha(int ringsize) + { + int[] alphaPalette = new int[256]; + float angle; + for (int i = 0; i < 256; i++) + { + alphaPalette[i] = 0; + } + + for (int i = 0; i < ringsize; i++) + { + angle = 3.14159f / 180 * (float)(180 * i / ringsize); + alphaPalette[255 - ringsize + i] = (int)(64 * MathF.Sin(angle)); + } + return alphaPalette; + } + + private int[] getSecAlpha() + { + int[] alphaPalette = getRingAlpha((int)warp_Math.random(0, 255)); + for (int i = 0; i < 256; i++) + { + alphaPalette[i] = (alphaPalette[i] + 255 - i) >> 2; + } + + return alphaPalette; + } + + + private int[] getColorPalette(int color1, int color2) + { + int[] palette = new int[256]; + + int r1 = (color1 >> 16) & 255; + int g1 = (color1 >> 8) & 255; + int b1 = color1 & 255; + int r2 = (color2 >> 16) & 255; + int g2 = (color2 >> 8) & 255; + int b2 = color2 & 255; + int dr = r2 - r1; + int dg = g2 - g1; + int db = b2 - b1; + int r = r1 << 8; + int g = g1 << 8; + int b = b1 << 8; + + for (int i = 0; i < 256; i++) + { + palette[i] = warp_Color.getColor(r >> 8, g >> 8, b >> 8); + r += dr; g += dg; b += db; + } + return palette; + } } } diff --git a/trunk/managed/Warp3Dmod/warp_FXPlugin.cs b/trunk/managed/Warp3Dmod/warp_FXPlugin.cs index a43febe4..3fc08413 100644 --- a/trunk/managed/Warp3Dmod/warp_FXPlugin.cs +++ b/trunk/managed/Warp3Dmod/warp_FXPlugin.cs @@ -9,12 +9,12 @@ namespace Warp3D public warp_Scene scene = null; public warp_Screen screen = null; - public warp_FXPlugin( warp_Scene scene ) - { - this.scene = scene; - screen = scene.renderPipeline.screen; - } - - public abstract void apply(); + public warp_FXPlugin(warp_Scene scene) + { + this.scene = scene; + screen = scene.renderPipeline.screen; + } + + public abstract void apply(); } } diff --git a/trunk/managed/Warp3Dmod/warp_Light.cs b/trunk/managed/Warp3Dmod/warp_Light.cs index cd5bf350..af0dbacb 100644 --- a/trunk/managed/Warp3Dmod/warp_Light.cs +++ b/trunk/managed/Warp3Dmod/warp_Light.cs @@ -2,65 +2,65 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_Light. - /// - public class warp_Light - { - // F I E L D S + /// + /// Summary description for warp_Light. + /// + public class warp_Light + { + // F I E L D S - public warp_Vector v; //Light direction - public warp_Vector v2; //projected Light direction - public int diffuse = 0; - public int specular = 0; - public int highlightSheen = 0; - public int highlightSpread = 0; + public warp_Vector v; //Light direction + public warp_Vector v2; //projected Light direction + public int diffuse = 0; + public int specular = 0; + public int highlightSheen = 0; + public int highlightSpread = 0; - warp_Matrix matrix2; + warp_Matrix matrix2; - private warp_Light() - // Default constructor not accessible - { - } + private warp_Light() + // Default constructor not accessible + { + } - public warp_Light(warp_Vector direction) - { - v = direction.getClone(); - v.normalize(); - } + public warp_Light(warp_Vector direction) + { + v = direction.getClone(); + v.normalize(); + } - public warp_Light(warp_Vector direction, int diffuse) - { - v = direction.getClone(); - v.normalize(); - this.diffuse = diffuse; - } + public warp_Light(warp_Vector direction, int diffuse) + { + v = direction.getClone(); + v.normalize(); + this.diffuse = diffuse; + } - public warp_Light(warp_Vector direction, int color, int highlightSheen, int highlightSpread) - { - v = direction.getClone(); - v.normalize(); - this.diffuse = color; - this.specular = color; - this.highlightSheen = highlightSheen; - this.highlightSpread = highlightSpread; - } + public warp_Light(warp_Vector direction, int color, int highlightSheen, int highlightSpread) + { + v = direction.getClone(); + v.normalize(); + diffuse = color; + specular = color; + this.highlightSheen = highlightSheen; + this.highlightSpread = highlightSpread; + } - public warp_Light(warp_Vector direction, int diffuse, int specular,int highlightSheen, int highlightSpread) - { - v = direction.getClone(); - v.normalize(); - this.diffuse = diffuse; - this.specular = specular; - this.highlightSheen = highlightSheen; - this.highlightSpread = highlightSpread; - } + public warp_Light(warp_Vector direction, int diffuse, int specular, int highlightSheen, int highlightSpread) + { + v = direction.getClone(); + v.normalize(); + this.diffuse = diffuse; + this.specular = specular; + this.highlightSheen = highlightSheen; + this.highlightSpread = highlightSpread; + } - public void project(warp_Matrix m) - { - matrix2 = m.getClone(); - matrix2.transform(m); - v2 = v.transform(matrix2); - } - } + public void project(warp_Matrix m) + { + matrix2 = m.getClone(); + matrix2.transform(m); + v2 = v.transform(matrix2); + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Lightmap.cs b/trunk/managed/Warp3Dmod/warp_Lightmap.cs index 0d725abe..685c9e36 100644 --- a/trunk/managed/Warp3Dmod/warp_Lightmap.cs +++ b/trunk/managed/Warp3Dmod/warp_Lightmap.cs @@ -1,4 +1,6 @@ using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Warp3D { @@ -9,21 +11,20 @@ namespace Warp3D { public int[] diffuse = new int[65536]; public int[] specular = new int[65536]; - private float[] sphere = new float[65536]; - private warp_Light[] light; - private int lights; - private int ambient; + private readonly float[] sphere = new float[65536]; + private readonly warp_Light[] light; + private readonly int ambient; private int pos; - const float inv128 = 1.0f/128f; - const float inv255 = 1.0f/255f; + const float inv128 = 1.0f / 128f; + const float inv255 = 1.0f / 255f; + const float inv4096 = 1.0f / 4096f; public warp_Lightmap(warp_Scene scene) { scene.rebuild(); light = scene.light; - lights = scene.lights; ambient = scene.environment.ambient; buildSphereMap(); @@ -35,30 +36,36 @@ namespace Warp3D float fnx, fny, fnz; float fnySQ; int pos; - for(int ny = -128; ny < 128; ny++) + ref float sphereref = ref MemoryMarshal.GetArrayDataReference(sphere); + + for (int ny = -128; ny < 128; ny++) { fny = ny * inv128; fnySQ = fny * fny; pos = (ny + 128) << 8; - for(int nx = -128; nx < 128; nx++, pos++) + for (int nx = -128; nx < 128; nx++, pos++) { fnx = nx * inv128; - fnz = (float)(1 - Math.Sqrt(fnx * fnx + fnySQ)); - sphere[pos] = (fnz > 0) ? fnz : 0; + fnz = 1f - MathF.Sqrt(fnx * fnx + fnySQ); + Unsafe.As(ref Unsafe.Add(ref sphereref, pos)) = (fnz > 0) ? fnz : 0; } } } public void rebuildLightmap() { - warp_Vector l; float fnx, fny, phongfact, sheen, spread; int diffuse, specular, cos, dr, dg, db, sr, sg, sb; - for(int ny = -128; ny < 128; ny++) + + ref int difuseref = ref MemoryMarshal.GetArrayDataReference(this.diffuse); + ref int specularref = ref MemoryMarshal.GetArrayDataReference(this.specular); + ref float sphereref = ref MemoryMarshal.GetArrayDataReference(sphere); + + for (int ny = -128; ny < 128; ny++) { fny = ny * inv128; pos = (ny + 128) << 8; - for(int nx = -128; nx < 128; nx++, pos++) + for (int nx = -128; nx < 128; nx++, pos++) { fnx = nx * inv128; sr = sg = sb = 0; @@ -67,26 +74,26 @@ namespace Warp3D dg = warp_Color.getGreen(ambient); db = warp_Color.getBlue(ambient); - for(int i = 0; i < lights; i++) + for (int i = 0; i < light.Length; i++) { - l = light[i].v; diffuse = light[i].diffuse; specular = light[i].specular; - sheen = (float)light[i].highlightSheen * inv255; - spread = (float)light[i].highlightSpread / 4096; + sheen = light[i].highlightSheen * inv255; + spread = light[i].highlightSpread * inv4096; spread = (spread < 0.01f) ? 0.01f : spread; - cos = (int)(255 * warp_Vector.vectorsCos(light[i].v, new warp_Vector(fnx, fny, sphere[pos]))); + warp_Vector tmp = new warp_Vector(fnx, fny, Unsafe.As(ref Unsafe.Add(ref sphereref, pos))); + cos = (int)(255 * warp_Vector.vectorsCos(ref light[i].v, ref tmp)); cos = (cos > 0) ? cos : 0; dr += (warp_Color.getRed(diffuse) * cos) >> 8; dg += (warp_Color.getGreen(diffuse) * cos) >> 8; db += (warp_Color.getBlue(diffuse) * cos) >> 8; - phongfact = sheen * (float)Math.Pow((float)cos * inv255, 1 / spread); + phongfact = sheen * (float)MathF.Pow((float)cos * inv255, 1 / spread); sr += (int)((float)warp_Color.getRed(specular) * phongfact); sg += (int)((float)warp_Color.getGreen(specular) * phongfact); sb += (int)((float)warp_Color.getBlue(specular) * phongfact); } - this.diffuse[pos] = warp_Color.getCropColor(dr, dg, db); - this.specular[pos] = warp_Color.getCropColor(sr, sg, sb); + Unsafe.As(ref Unsafe.Add(ref difuseref, pos)) = warp_Color.getCropColor(dr, dg, db); + Unsafe.As(ref Unsafe.Add(ref specularref, pos)) = warp_Color.getCropColor(sr, sg, sb); } } } diff --git a/trunk/managed/Warp3Dmod/warp_Material.cs b/trunk/managed/Warp3Dmod/warp_Material.cs index 3191a461..3c023d7c 100644 --- a/trunk/managed/Warp3Dmod/warp_Material.cs +++ b/trunk/managed/Warp3Dmod/warp_Material.cs @@ -1,241 +1,256 @@ using System; using System.Drawing; using System.IO; +using System.Runtime.CompilerServices; + namespace Warp3D { - /// - /// Summary description for warp_Material. - /// - public class warp_Material - { - public int color = warp_Color.White; - public int reflectivity=255; - public int type=0; - public warp_Texture texture=null; - public warp_Texture envmap=null; - public bool flat=false; - public bool wireframe=false; - public String texturePath=null; - public String envmapPath=null; - public warp_TextureSettings textureSettings=null; - public warp_TextureSettings envmapSettings=null; - public double rwx = 500.00; - public double rwy = 500.00; - public Point pt = new Point(0,0); - public int offset = 0; + /// + /// Summary description for warp_Material. + /// + public class warp_Material + { + public int color = warp_Color.White; + public int reflectivity = 255; + public int type = 0; + public warp_Texture texture = null; + public warp_Texture envmap = null; + public bool flat = false; + public bool wireframe = false; + public string texturePath = null; + public string envmapPath = null; + public warp_TextureSettings textureSettings = null; + public warp_TextureSettings envmapSettings = null; + public double rwx = 500.00; + public double rwy = 500.00; + public Point pt = new(0, 0); + public int offset = 0; - public warp_Material() - { - // - // TODO: Add constructor logic here - // - } - public warp_Material(int c) - { - color = c; - } - - public warp_Material(warp_Texture t) - { - setTexture(t); - reflectivity=255; - } - - public warp_Material(String path) - { - using(FileStream fs = new FileStream(path,FileMode.Open)) - { - using(BinaryReader br = new BinaryReader(fs)) - importFromStream(br); - } - } - - private void importFromStream(BinaryReader inStream) - { - readSettings(inStream); - readTexture(inStream, true); - readTexture(inStream, false); - } - - private void readSettings(BinaryReader inStream) - { - setColor(readInt(inStream)); - setReflectivity(inStream.ReadByte()); - setFlat(inStream.ReadBoolean()); - } - - private int readInt(BinaryReader inStream) - { - return (inStream.ReadByte() << 24) | (inStream.ReadByte() << 16) | (inStream.ReadByte() << 8) | inStream.ReadByte(); - } - - private string readString(BinaryReader inStream) - { - string result = ""; - byte inByte; - while ( (inByte = (byte) inStream.ReadByte()) != 60) - { - result += (char) inByte; - } - - return result; - } - - private void readTexture(BinaryReader inStream, bool textureId) - { - warp_Texture t = null; - int id = inStream.ReadSByte(); - if (id == 1) - { - t = new warp_Texture( "c:/source/Warp3D/materials/skymap.jpg" ); - - if (t != null && textureId) - { - texturePath = t.path; - textureSettings = null; - setTexture(t); - } - if (t != null && !textureId) - { - envmapPath = t.path; - envmapSettings = null; - setEnvmap(t); - } - } - - if (id == 2) - { - int w = readInt(inStream); - int h = readInt(inStream); - int type = inStream.ReadSByte(); - - float persistency = readInt(inStream); - float density = readInt(inStream); - - persistency = .5f; - density = .5f; - - int samples = inStream.ReadByte(); - int numColors = inStream.ReadByte(); - int[] colors = new int[numColors]; - for (int i = 0; i < numColors; i++) - { - colors[i] = readInt(inStream); - - } - if (type == 1) - { - t = warp_TextureFactory.PERLIN(w, h, persistency, density, samples, 1024).colorize(warp_Color.makeGradient(colors, 1024)); - } - if (type == 2) - { - t = warp_TextureFactory.WAVE(w, h, persistency, density, samples, 1024).colorize(warp_Color.makeGradient(colors, 1024)); - } - if (type == 3) - { - t = warp_TextureFactory.GRAIN(w, h, persistency, density, samples, 20, 1024).colorize(warp_Color.makeGradient(colors, 1024)); - - } - - if (textureId) - { - texturePath = null; - textureSettings = new warp_TextureSettings(t, w, h, type, persistency, density, samples, colors); - setTexture(t); - } - else - { - envmapPath = null; - envmapSettings = new warp_TextureSettings(t, w, h, type, persistency, density, samples, colors); - setEnvmap(t); - } - } - } - - public void setTexture(warp_Texture t) - { - texture=t; - if (texture!=null) - texture.resize(); - } - - public void setEnvmap(warp_Texture env) - { - envmap=env; - env.resize(256,256); - } - - public void setColor(int c) - { - color = c; - } - - public bool opaque + public warp_Material() { + // + // TODO: Add constructor logic here + // + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public warp_Material(int c) + { + color = c; + } + + public warp_Material(warp_Texture t) + { + setTexture(t); + reflectivity = 255; + } + + public warp_Material(string path) + { + using FileStream fs = new(path, FileMode.Open); + using BinaryReader br = new(fs); + importFromStream(br); + } + + private void importFromStream(BinaryReader inStream) + { + readSettings(inStream); + readTexture(inStream, true); + readTexture(inStream, false); + } + + private void readSettings(BinaryReader inStream) + { + setColor(readInt(inStream)); + setReflectivity(inStream.ReadByte()); + setFlat(inStream.ReadBoolean()); + } + + private int readInt(BinaryReader inStream) + { + return (inStream.ReadByte() << 24) | (inStream.ReadByte() << 16) | (inStream.ReadByte() << 8) | inStream.ReadByte(); + } + + private string readString(BinaryReader inStream) + { + string result = ""; + byte inByte; + while ((inByte = (byte)inStream.ReadByte()) != 60) + { + result += (char)inByte; + } + + return result; + } + + private void readTexture(BinaryReader inStream, bool textureId) + { + warp_Texture t = null; + int id = inStream.ReadSByte(); + if (id == 1) + { + t = new warp_Texture("c:/source/Warp3D/materials/skymap.jpg"); + + if (t != null && textureId) + { + texturePath = t.path; + textureSettings = null; + setTexture(t); + } + if (t != null && !textureId) + { + envmapPath = t.path; + envmapSettings = null; + setEnvmap(t); + } + } + + if (id == 2) + { + int w = readInt(inStream); + int h = readInt(inStream); + int type = inStream.ReadSByte(); + + float persistency = readInt(inStream); + float density = readInt(inStream); + + persistency = .5f; + density = .5f; + + int samples = inStream.ReadByte(); + int numColors = inStream.ReadByte(); + int[] colors = new int[numColors]; + for (int i = 0; i < numColors; i++) + { + colors[i] = readInt(inStream); + + } + if (type == 1) + { + t = warp_TextureFactory.PERLIN(w, h, persistency, density, samples, 1024).colorize(warp_Color.makeGradient(colors, 1024)); + } + if (type == 2) + { + t = warp_TextureFactory.WAVE(w, h, persistency, density, samples, 1024).colorize(warp_Color.makeGradient(colors, 1024)); + } + if (type == 3) + { + t = warp_TextureFactory.GRAIN(w, h, persistency, density, samples, 20, 1024).colorize(warp_Color.makeGradient(colors, 1024)); + + } + + if (textureId) + { + texturePath = null; + textureSettings = new warp_TextureSettings(t, w, h, type, persistency, density, samples, colors); + setTexture(t); + } + else + { + envmapPath = null; + envmapSettings = new warp_TextureSettings(t, w, h, type, persistency, density, samples, colors); + setEnvmap(t); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setTexture(warp_Texture t) + { + texture = t; + texture?.resize(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setEnvmap(warp_Texture env) + { + envmap = env; + envmap?.resize(256, 256); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setColor(int c) + { + color = c; + } + + public bool opaque + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if((color & warp_Color.MASKALPHA) != warp_Color.MASKALPHA) + if ((color & warp_Color.MASKALPHA) != warp_Color.MASKALPHA) return false; - if(texture != null) + if (texture is not null) return texture.opaque; return true; } } - public void setReflectivity(int factor) - { - reflectivity=warp_Math.crop(factor,0,255); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setReflectivity(int factor) + { + reflectivity = warp_Math.crop(factor, 0, 255); + } - public void setFlat(bool flat) - { - this.flat=flat; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setFlat(bool flat) + { + this.flat = flat; + } - public void setWireframe(bool wireframe) - { - this.wireframe=wireframe; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setWireframe(bool wireframe) + { + this.wireframe = wireframe; + } - public void setType(int type) - { - this.type = type; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void setType(int type) + { + this.type = type; + } - public warp_Texture getTexture() - { - return texture; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public warp_Texture getTexture() + { + return texture; + } - public warp_Texture getEnvmap() - { - return envmap; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public warp_Texture getEnvmap() + { + return envmap; + } - public int getColor() - { - return color; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int getColor() + { + return color; + } - public int getReflectivity() - { - return reflectivity; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int getReflectivity() + { + return reflectivity; + } - public int getType() - { - return type; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int getType() + { + return type; + } - public bool isFlat() - { - return flat; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool isFlat() + { + return flat; + } - public bool isWireframe() - { - return wireframe; - } - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool isWireframe() + { + return wireframe; + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Math.cs b/trunk/managed/Warp3Dmod/warp_Math.cs index 9b7b6e59..1269caca 100644 --- a/trunk/managed/Warp3Dmod/warp_Math.cs +++ b/trunk/managed/Warp3Dmod/warp_Math.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Warp3D { @@ -7,218 +8,111 @@ namespace Warp3D /// public class warp_Math { - // private static float[] sinus; - // private static float[] cosinus; - // private static bool trig=false; - public static float pi = 3.1415926535f; - // private static float rad2scale=4096f/3.14159265f/2f; - // private static float pad=256*3.14159265f; - - private static int[] fastRandoms; - private static int fastRndPointer = 0; - private static bool fastRndInit = false; - - private static Random _rnd = new Random(); - - public warp_Math() { - // - // TODO: Add constructor logic here - // } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float interpolate(float a, float b, float d) { - float f = (1 - cos(d * pi)) * 0.5f; + float f = (1f - MathF.Cos(d * MathF.PI)) * 0.5f; return a + f * (b - a); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float random() { - Random rnd = new Random(); - return (float)(rnd.NextDouble() * 2 - 1); + return Random.Shared.NextSingle(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float random(float min, float max) { - //Random rnd = new Random(); - return (float)(_rnd.NextDouble() * (max - min) + min); + return Random.Shared.NextSingle() * (max - min) + min; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float randomWithDelta(float averidge, float delta) { return averidge + random() * delta; } - public static int fastRnd(int bits) - { - if (bits < 1) - { - return 0; - } - fastRndPointer = (fastRndPointer + 1) & 31; - if (!fastRndInit) - { - fastRandoms = new int[32]; - for (int i = 0; i < 32; i++) - { - fastRandoms[i] = (int)random(0, 0xFFFFFF); - } - fastRndInit = true; - } - return fastRandoms[fastRndPointer] & (1 << (bits - 1)); - } - - public static int fastRndBit() - { - return fastRnd(1); - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float deg2rad(float deg) { return deg * 0.0174532925194f; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float rad2deg(float rad) { return rad * 57.295779514719f; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float sin(float angle) { - return (float)Math.Sin(angle); - // if(!trig) buildTrig(); - // return sinus[(int)((angle+pad)*rad2scale)&0xFFF]; + return MathF.Sin(angle); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float cos(float angle) { - return (float)Math.Cos(angle); - // if(!trig) buildTrig(); - // return cosinus[(int)((angle+pad)*rad2scale)&0xFFF]; + return MathF.Cos(angle); } - /* - private static void buildTrig() - { - sinus=new float[4096]; - cosinus=new float[4096]; - for (int i=0;i<4096;i++) - { - sinus[i]=(float)Math.Sin((float)i/rad2scale); - cosinus[i]=(float)Math.Cos((float)i/rad2scale); - } - - trig=true; - } - */ - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float pythagoras(float a, float b) { - return (float)Math.Sqrt(a * a + b * b); + return MathF.Sqrt(a * a + b * b); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int pythagoras(int a, int b) { - return (int)Math.Sqrt(a * a + b * b); + return (int)MathF.Sqrt(a * a + b * b); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int crop(int num, int min, int max) { return (num < min) ? min : (num > max) ? max : num; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float crop(float num, float min, float max) { return (num < min) ? min : (num > max) ? max : num; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool inrange(int num, int min, int max) { return ((num >= min) && (num < max)); } - unsafe public static void clearBuffer(int[] buffer, int c) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void clearBuffer(int[] buffer, int c) { - /* - for (int idx = 0; idx < buffer.Length; idx++) - { - buffer[idx] = c; - } - */ - - fixed (int* pC = buffer) - { - int* pClear = pC; - for (int i = 0; i < buffer.GetLength(0) / 4; i++) - { - *pClear++ = c; - *pClear++ = c; - *pClear++ = c; - *pClear++ = c; - } - } + if (c == 0) + Array.Clear(buffer); + else + Array.Fill(buffer, c); } - unsafe public static void clearBuffer(long[] buffer, int c) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void clearBuffer(byte[] buffer, byte c) { - /* for (int idx = 0; idx < buffer.Length; idx++) - { - buffer[idx] = c; - }*/ - fixed (long* pC = buffer) - { - long* pClear = pC; - for (int i = 0; i < buffer.GetLength(0) / 4; i++) - { - *pClear++ = c; - *pClear++ = c; - *pClear++ = c; - *pClear++ = c; - } - } - } - - public static void clearBuffer(byte[] buffer, byte value) - { - System.Array.Clear(buffer, 0, buffer.GetLength(0)); - } - - public static void cropBuffer(int[] buffer, int min, int max) - { - for (int i = buffer.GetLength(0) - 1; i >= 0; i--) buffer[i] = crop(buffer[i], min, max); + if (c == 0) + Array.Clear(buffer); + else + Array.Fill(buffer, c); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void copyBuffer(int[] source, int[] target) { - System.Array.Copy(source, 0, target, 0, crop(source.GetLength(0), 0, target.GetLength(0))); + Array.Copy(source, target, crop(source.GetLength(0), 0, target.GetLength(0))); } - - /* - public static float random() - { - return (float)(Math.r.random()*2-1); - } - - public static float random(float min, float max) - { - return (float)(Math.random()*(max-min)+min); - } - - public static float randomWithDelta(float averidge, float delta) - { - return averidge+random()*delta; - } - */ } } diff --git a/trunk/managed/Warp3Dmod/warp_Matrix.cs b/trunk/managed/Warp3Dmod/warp_Matrix.cs index 7ca123bd..9432bb17 100644 --- a/trunk/managed/Warp3Dmod/warp_Matrix.cs +++ b/trunk/managed/Warp3Dmod/warp_Matrix.cs @@ -2,57 +2,55 @@ using System; namespace Warp3D { - public class warp_Matrix - { - public float m00=1, m01=0, m02=0, m03=0; - public float m10=0, m11=1, m12=0, m13=0; - public float m20=0, m21=0, m22=1, m23=0; - public float m30=0, m31=0, m32=0, m33=1; + public class warp_Matrix + { + public float m00 = 1, m01 = 0, m02 = 0, m03 = 0; + public float m10 = 0, m11 = 1, m12 = 0, m13 = 0; + public float m20 = 0, m21 = 0, m22 = 1, m23 = 0; + public float m30 = 0, m31 = 0, m32 = 0, m33 = 1; - public warp_Matrix(warp_Vector right, warp_Vector up, warp_Vector forward) - { - m00=right.x; - m10=right.y; - m20=right.z; - m01=up.x; - m11=up.y; - m21=up.z; - m02=forward.x; - m12=forward.y; - m22=forward.z; - } + public warp_Matrix(warp_Vector right, warp_Vector up, warp_Vector forward) + { + m00 = right.x; + m10 = right.y; + m20 = right.z; + m01 = up.x; + m11 = up.y; + m21 = up.z; + m02 = forward.x; + m12 = forward.y; + m22 = forward.z; + } - - - public float this[ int column, int row ] + public float this[int column, int row] { get { - switch ( row ) + switch (row) { case 0: - if ( column == 0 ) return m00; - if ( column == 1 ) return m01; - if ( column == 2 ) return m02; - if ( column == 3 ) return m03; + if (column == 0) return m00; + if (column == 1) return m01; + if (column == 2) return m02; + if (column == 3) return m03; break; case 1: - if ( column == 0 ) return m11; - if ( column == 1 ) return m11; - if ( column == 2 ) return m12; - if ( column == 3 ) return m13; + if (column == 0) return m11; + if (column == 1) return m11; + if (column == 2) return m12; + if (column == 3) return m13; break; case 2: - if ( column == 0 ) return m20; - if ( column == 1 ) return m21; - if ( column == 2 ) return m22; - if ( column == 3 ) return m23; + if (column == 0) return m20; + if (column == 1) return m21; + if (column == 2) return m22; + if (column == 3) return m23; break; case 3: - if ( column == 0 ) return m30; - if ( column == 1 ) return m31; - if ( column == 2 ) return m32; - if ( column == 3 ) return m33; + if (column == 0) return m30; + if (column == 1) return m31; + if (column == 2) return m32; + if (column == 3) return m33; break; default: @@ -63,343 +61,398 @@ namespace Warp3D } } - public warp_Matrix() - { - // - // TODO: Add constructor logic here - // - } - - public static warp_Matrix shiftMatrix(float dx, float dy, float dz) - // matrix for shifting - { - warp_Matrix m=new warp_Matrix(); - m.m03=dx; - m.m13=dy; - m.m23=dz; - return m; - } - - public static warp_Matrix quaternionMatrix( warp_Quaternion quat ) + public warp_Matrix() { - warp_Matrix m = new warp_Matrix(); + // + // TODO: Add constructor logic here + // + } - - float xx = quat.X * quat.X; - float xy = quat.X * quat.Y; - float xz = quat.X * quat.Z; - float xw = quat.X * quat.W; - float yy = quat.Y * quat.Y; - float yz = quat.Y * quat.Z; - float yw = quat.Y * quat.W; - float zz = quat.Z * quat.Z; - float zw = quat.Z * quat.W; - - m.m00 = 1 - 2 * ( yy + zz ); - m.m01 = 2 * ( xy - zw) ; - m.m02 = 2*(xz + yw); - m.m10 = 2*(xy + zw); - m.m11 = 1 - 2* ( xx + zz ); - m.m12 = 2*(yz - xw); - m.m20 = 2*(xz - yw); - m.m21 = 2*(yz + xw); - m.m22 = 1 - 2 * ( xx + yy ); - - m.m03 = m.m13 = m.m23 = m.m30 = m.m31 = m.m32 = 0; - m.m33 = 1; - + public static warp_Matrix shiftMatrix(float dx, float dy, float dz) + // matrix for shifting + { + warp_Matrix m = new() + { + m03 = dx, + m13 = dy, + m23 = dz + }; return m; } - public warp_Matrix rotateMatrix( warp_Quaternion quat ) + public static warp_Matrix quaternionMatrix(warp_Quaternion quat) { + float x2 = quat.X + quat.X; + float y2 = quat.Y + quat.Y; + float z2 = quat.Z + quat.Z; + float wx2 = quat.W * x2; + float wy2 = quat.W * y2; + float wz2 = quat.W * z2; + float xx2 = quat.X * x2; + float xy2 = quat.X * y2; + float xz2 = quat.X * z2; + float yy2 = quat.Y * y2; + float yz2 = quat.Y * z2; + float zz2 = quat.Z * z2; + + return new() + { + m00 = 1.0f - yy2 - zz2, + m01 = xy2 - wz2, + m02 = xz2 + wy2, + m03 = 0f, + + m10 = xy2 + wz2, + m11 = 1.0f - xx2 - zz2, + m12 = yz2 - wx2, + m13 = 0f, + + m20 = xz2 - wy2, + m21 = yz2 + wx2, + m22 = 1.0f - xx2 - yy2, + m23 = 0f, + + m30 = 0f, + m31 = 0f, + m32 = 0f, + m33 = 1f + }; + } + + public warp_Matrix rotateMatrix(warp_Quaternion quat) + { reset(); - warp_Matrix temp = warp_Matrix.quaternionMatrix( quat ); - warp_Matrix result = warp_Matrix.multiply( this, temp ); + warp_Matrix temp = warp_Matrix.quaternionMatrix(quat); + warp_Matrix result = warp_Matrix.multiply(this, temp); - return result; + return result; } - public static warp_Matrix scaleMatrix(float dx, float dy, float dz) - { - warp_Matrix m=new warp_Matrix(); - - m.m00=dx; - m.m11=dy; - m.m22=dz; - - return m; - } - - public static warp_Matrix scaleMatrix(float d) - { - return warp_Matrix.scaleMatrix(d,d,d); - } - - public static warp_Matrix rotateMatrix(float dx, float dy, float dz) - { - warp_Matrix res=new warp_Matrix(); - - float SIN; - float COS; - - if (dx!=0) - { - warp_Matrix m = new warp_Matrix(); - SIN=warp_Math.sin(dx); - COS=warp_Math.cos(dx); - m.m11=COS; - m.m12=SIN; - m.m21=-SIN; - m.m22=COS; - - res.transform(m); - } - if (dy!=0) - { - warp_Matrix m =new warp_Matrix(); - SIN=warp_Math.sin(dy); - COS=warp_Math.cos(dy); - m.m00=COS; - m.m02=SIN; - m.m20=-SIN; - m.m22=COS; - - res.transform(m); - } - if (dz!=0) - { - warp_Matrix m =new warp_Matrix(); - SIN=warp_Math.sin(dz); - COS=warp_Math.cos(dz); - m.m00=COS; - m.m01=SIN; - m.m10=-SIN; - m.m11=COS; - - res.transform(m); - } - - return res; - } - - public void shift(float dx, float dy, float dz) - { - m03 += dx; - m13 += dy; - m23 += dz; - } - - public void scale(float dx, float dy, float dz) - { -// transform(scaleMatrix(dx,dy,dz)); - - m00 *= dx; - m01 *= dx; - m02 *= dx; - m03 *= dx; - m10 *= dy; - m11 *= dy; - m12 *= dy; - m13 *= dy; - m20 *= dz; - m21 *= dz; - m22 *= dz; - m23 *= dz; - } - - public void scale(float d) - { - m00 *= d; - m01 *= d; - m02 *= d; - m03 *= d; - m10 *= d; - m11 *= d; - m12 *= d; - m13 *= d; - m20 *= d; - m21 *= d; - m22 *= d; - m23 *= d; - } - - public void rotate(float dx, float dy, float dz) - { - transform(rotateMatrix(dx,dy,dz)); - } - - public void rotate( warp_Quaternion quat, float x, float y, float z ) + public static warp_Matrix scaleMatrix(float dx, float dy, float dz) { - transform( rotateMatrix( quat ) ); + warp_Matrix m = new() + { + m00 = dx, + m11 = dy, + m22 = dz + }; + + return m; } - public void rotate( warp_Matrix m ) + public static warp_Matrix scaleMatrix(float d) { - transform( m ); + return warp_Matrix.scaleMatrix(d, d, d); } - public void scaleSelf( float dx, float dy, float dz ) - { - m00 *= dx; - m01 *= dy; - m02 *= dz; - m10 *= dx; - m11 *= dy; - m12 *= dz; - m20 *= dx; - m21 *= dy; - m22 *= dz; - } - - public void scaleSelf(float d) - { - m00 *= d; - m01 *= d; - m02 *= d; - m10 *= d; - m11 *= d; - m12 *= d; - m20 *= d; - m21 *= d; - m22 *= d; - } - - public void shiftSelf( float dx, float dy, float dz ) + public static warp_Matrix rotateMatrix(float dx, float dy, float dz) { - m03 += m00 * dx + m01 * dy + m02 * dz; - m13 += m10 * dx + m11 * dy + m12 * dz; - m23 += m20 * dx + m21 * dx + m22 * dx; + warp_Matrix res = new warp_Matrix(); + + float SIN; + float COS; + + if (dx != 0) + { + warp_Matrix m = new warp_Matrix(); + SIN = warp_Math.sin(dx); + COS = warp_Math.cos(dx); + m.m11 = COS; + m.m12 = SIN; + m.m21 = -SIN; + m.m22 = COS; + + res.transform(m); + } + if (dy != 0) + { + warp_Matrix m = new warp_Matrix(); + SIN = warp_Math.sin(dy); + COS = warp_Math.cos(dy); + m.m00 = COS; + m.m02 = SIN; + m.m20 = -SIN; + m.m22 = COS; + + res.transform(m); + } + if (dz != 0) + { + warp_Matrix m = new warp_Matrix(); + SIN = warp_Math.sin(dz); + COS = warp_Math.cos(dz); + m.m00 = COS; + m.m01 = SIN; + m.m10 = -SIN; + m.m11 = COS; + + res.transform(m); + } + + return res; } - public void rotateSelf( float dx, float dy, float dz ) + public void shift(float dx, float dy, float dz) { - preTransform( rotateMatrix( dx, dy, dz ) ); + m03 += dx; + m13 += dy; + m23 += dz; } - public void rotateSelf( warp_Matrix m ) - { - preTransform( m ); - } - public void rotateSelf( warp_Quaternion quat ) + public void scale(float dx, float dy, float dz) { - preTransform(rotateMatrix( quat) ); + // transform(scaleMatrix(dx,dy,dz)); + + m00 *= dx; + m01 *= dx; + m02 *= dx; + m03 *= dx; + m10 *= dy; + m11 *= dy; + m12 *= dy; + m13 *= dy; + m20 *= dz; + m21 *= dz; + m22 *= dz; + m23 *= dz; } + public void scale(float d) + { + m00 *= d; + m01 *= d; + m02 *= d; + m03 *= d; + m10 *= d; + m11 *= d; + m12 *= d; + m13 *= d; + m20 *= d; + m21 *= d; + m22 *= d; + m23 *= d; + } - public void transform(warp_Matrix n) - { - warp_Matrix m=this.getClone(); + public void rotate(float dx, float dy, float dz) + { + transform(rotateMatrix(dx, dy, dz)); + } - m00 = n.m00*m.m00 + n.m01*m.m10 + n.m02*m.m20; - m01 = n.m00*m.m01 + n.m01*m.m11 + n.m02*m.m21; - m02 = n.m00*m.m02 + n.m01*m.m12 + n.m02*m.m22; - m03 = n.m00*m.m03 + n.m01*m.m13 + n.m02*m.m23 + n.m03; - m10 = n.m10*m.m00 + n.m11*m.m10 + n.m12*m.m20; - m11 = n.m10*m.m01 + n.m11*m.m11 + n.m12*m.m21; - m12 = n.m10*m.m02 + n.m11*m.m12 + n.m12*m.m22; - m13 = n.m10*m.m03 + n.m11*m.m13 + n.m12*m.m23 + n.m13; - m20 = n.m20*m.m00 + n.m21*m.m10 + n.m22*m.m20; - m21 = n.m20*m.m01 + n.m21*m.m11 + n.m22*m.m21; - m22 = n.m20*m.m02 + n.m21*m.m12 + n.m22*m.m22; - m23 = n.m20*m.m03 + n.m21*m.m13 + n.m22*m.m23 + n.m23; - } + public void rotate(warp_Quaternion quat, float x, float y, float z) + { + transform(rotateMatrix(quat)); + } - public void preTransform(warp_Matrix n) - { - warp_Matrix m=this.getClone(); + public void rotate(warp_Matrix m) + { + transform(m); + } - m00 = m.m00*n.m00 + m.m01*n.m10 + m.m02*n.m20; - m01 = m.m00*n.m01 + m.m01*n.m11 + m.m02*n.m21; - m02 = m.m00*n.m02 + m.m01*n.m12 + m.m02*n.m22; - m03 = m.m00*n.m03 + m.m01*n.m13 + m.m02*n.m23 + m.m03; - m10 = m.m10*n.m00 + m.m11*n.m10 + m.m12*n.m20; - m11 = m.m10*n.m01 + m.m11*n.m11 + m.m12*n.m21; - m12 = m.m10*n.m02 + m.m11*n.m12 + m.m12*n.m22; - m13 = m.m10*n.m03 + m.m11*n.m13 + m.m12*n.m23 + m.m13; - m20 = m.m20*n.m00 + m.m21*n.m10 + m.m22*n.m20; - m21 = m.m20*n.m01 + m.m21*n.m11 + m.m22*n.m21; - m22 = m.m20*n.m02 + m.m21*n.m12 + m.m22*n.m22; - m23 = m.m20*n.m03 + m.m21*n.m13 + m.m22*n.m23 + m.m23; - } + public void scaleSelf(float dx, float dy, float dz) + { + m00 *= dx; + m01 *= dy; + m02 *= dz; + m10 *= dx; + m11 *= dy; + m12 *= dz; + m20 *= dx; + m21 *= dy; + m22 *= dz; + } - public static warp_Matrix multiply(warp_Matrix m1, warp_Matrix m2) - { - warp_Matrix m=new warp_Matrix(); + public void scaleSelf(float d) + { + m00 *= d; + m01 *= d; + m02 *= d; + m10 *= d; + m11 *= d; + m12 *= d; + m20 *= d; + m21 *= d; + m22 *= d; + } - m.m00 = m1.m00*m2.m00 + m1.m01*m2.m10 + m1.m02*m2.m20; - m.m01 = m1.m00*m2.m01 + m1.m01*m2.m11 + m1.m02*m2.m21; - m.m02 = m1.m00*m2.m02 + m1.m01*m2.m12 + m1.m02*m2.m22; - m.m03 = m1.m00*m2.m03 + m1.m01*m2.m13 + m1.m02*m2.m23 + m1.m03; - m.m10 = m1.m10*m2.m00 + m1.m11*m2.m10 + m1.m12*m2.m20; - m.m11 = m1.m10*m2.m01 + m1.m11*m2.m11 + m1.m12*m2.m21; - m.m12 = m1.m10*m2.m02 + m1.m11*m2.m12 + m1.m12*m2.m22; - m.m13 = m1.m10*m2.m03 + m1.m11*m2.m13 + m1.m12*m2.m23 + m1.m13; - m.m20 = m1.m20*m2.m00 + m1.m21*m2.m10 + m1.m22*m2.m20; - m.m21 = m1.m20*m2.m01 + m1.m21*m2.m11 + m1.m22*m2.m21; - m.m22 = m1.m20*m2.m02 + m1.m21*m2.m12 + m1.m22*m2.m22; - m.m23 = m1.m20*m2.m03 + m1.m21*m2.m13 + m1.m22*m2.m23 + m1.m23; - - return m; - } + public void shiftSelf(float dx, float dy, float dz) + { + m03 += m00 * dx + m01 * dy + m02 * dz; + m13 += m10 * dx + m11 * dy + m12 * dz; + m23 += m20 * dx + m21 * dx + m22 * dx; + } -/* - public string toString() - { - // todo - } -*/ - public warp_Matrix getClone() - { - warp_Matrix m=new warp_Matrix(); + public void rotateSelf(float dx, float dy, float dz) + { + preTransform(rotateMatrix(dx, dy, dz)); + } + public void rotateSelf(warp_Matrix m) + { + preTransform(m); + } - m.m00=m00; m.m01=m01; m.m02=m02; m.m03=m03; - m.m10=m10; m.m11=m11; m.m12=m12; m.m13=m13; - m.m20=m20; m.m21=m21; m.m22=m22; m.m23=m23; - m.m30=m30; m.m31=m31; m.m32=m32; m.m33=m33; - - return m; - } + public void rotateSelf(warp_Quaternion quat) + { + preTransform(rotateMatrix(quat)); + } - public warp_Matrix inverse() - { - warp_Matrix m=new warp_Matrix(); + public void transform(warp_Matrix n) + { + warp_Matrix m = getClone(); - float q1 = m12; float q6 = m10*m01; float q7 = m10*m21; float q8 = m02; - float q13 = m20*m01; float q14 = m20*m11; float q21 = m02*m21; float q22 = m03*m21; - float q25 = m01*m12; float q26 = m01*m13; float q27 = m02*m11; float q28 = m03*m11; - float q29 = m10*m22; float q30 = m10*m23; float q31 = m20*m12; float q32 = m20*m13; - float q35 = m00*m22; float q36 = m00*m23; float q37 = m20*m02; float q38 = m20*m03; - float q41 = m00*m12; float q42 = m00*m13; float q43 = m10*m02; float q44 = m10*m03; - float q45 = m00*m11; float q48 = m00*m21; - float q49 = q45*m22-q48*q1-q6*m22+q7*q8; - float q50 = q13*q1-q14*q8; - float q51 = 1/(q49+q50); + m00 = n.m00 * m.m00 + n.m01 * m.m10 + n.m02 * m.m20; + m01 = n.m00 * m.m01 + n.m01 * m.m11 + n.m02 * m.m21; + m02 = n.m00 * m.m02 + n.m01 * m.m12 + n.m02 * m.m22; + m03 = n.m00 * m.m03 + n.m01 * m.m13 + n.m02 * m.m23 + n.m03; + m10 = n.m10 * m.m00 + n.m11 * m.m10 + n.m12 * m.m20; + m11 = n.m10 * m.m01 + n.m11 * m.m11 + n.m12 * m.m21; + m12 = n.m10 * m.m02 + n.m11 * m.m12 + n.m12 * m.m22; + m13 = n.m10 * m.m03 + n.m11 * m.m13 + n.m12 * m.m23 + n.m13; + m20 = n.m20 * m.m00 + n.m21 * m.m10 + n.m22 * m.m20; + m21 = n.m20 * m.m01 + n.m21 * m.m11 + n.m22 * m.m21; + m22 = n.m20 * m.m02 + n.m21 * m.m12 + n.m22 * m.m22; + m23 = n.m20 * m.m03 + n.m21 * m.m13 + n.m22 * m.m23 + n.m23; + } - m.m00 = (m11*m22*m33-m11*m23*m32-m21*m12*m33+m21*m13*m32+m31*m12*m23-m31*m13*m22)*q51; - m.m01 = -(m01*m22*m33-m01*m23*m32-q21*m33+q22*m32)*q51; - m.m02 = (q25*m33-q26*m32-q27*m33+q28*m32)*q51; - m.m03 = -(q25*m23-q26*m22-q27*m23+q28*m22+q21*m13-q22*m12)*q51; - m.m10 = -(q29*m33-q30*m32-q31*m33+q32*m32)*q51; - m.m11 = (q35*m33-q36*m32-q37*m33+q38*m32)*q51; - m.m12 = -(q41*m33-q42*m32-q43*m33+q44*m32)*q51; - m.m13 = (q41*m23-q42*m22-q43*m23+q44*m22+q37*m13-q38*m12)*q51; - m.m20 = (q7*m33-q30*m31-q14*m33+q32*m31)*q51; - m.m21 = -(q48*m33-q36*m31-q13*m33+q38*m31)*q51; - m.m22 = (q45*m33-q42*m31-q6*m33+q44*m31)*q51; - m.m23 = -(q45*m23-q42*m21-q6*m23+q44*m21+q13*m13-q38*m11)*q51; + public static warp_Matrix transform(warp_Matrix m, warp_Matrix n) + { + return new warp_Matrix + { + m00 = n.m00 * m.m00 + n.m01 * m.m10 + n.m02 * m.m20, + m01 = n.m00 * m.m01 + n.m01 * m.m11 + n.m02 * m.m21, + m02 = n.m00 * m.m02 + n.m01 * m.m12 + n.m02 * m.m22, + m03 = n.m00 * m.m03 + n.m01 * m.m13 + n.m02 * m.m23 + n.m03, + m10 = n.m10 * m.m00 + n.m11 * m.m10 + n.m12 * m.m20, + m11 = n.m10 * m.m01 + n.m11 * m.m11 + n.m12 * m.m21, + m12 = n.m10 * m.m02 + n.m11 * m.m12 + n.m12 * m.m22, + m13 = n.m10 * m.m03 + n.m11 * m.m13 + n.m12 * m.m23 + n.m13, + m20 = n.m20 * m.m00 + n.m21 * m.m10 + n.m22 * m.m20, + m21 = n.m20 * m.m01 + n.m21 * m.m11 + n.m22 * m.m21, + m22 = n.m20 * m.m02 + n.m21 * m.m12 + n.m22 * m.m22, + m23 = n.m20 * m.m03 + n.m21 * m.m13 + n.m22 * m.m23 + n.m23, + }; + } - return m; - } + public void SetFromtransform(warp_Matrix m, warp_Matrix n) + { + m00 = n.m00 * m.m00 + n.m01 * m.m10 + n.m02 * m.m20; + m01 = n.m00 * m.m01 + n.m01 * m.m11 + n.m02 * m.m21; + m02 = n.m00 * m.m02 + n.m01 * m.m12 + n.m02 * m.m22; + m03 = n.m00 * m.m03 + n.m01 * m.m13 + n.m02 * m.m23 + n.m03; + m10 = n.m10 * m.m00 + n.m11 * m.m10 + n.m12 * m.m20; + m11 = n.m10 * m.m01 + n.m11 * m.m11 + n.m12 * m.m21; + m12 = n.m10 * m.m02 + n.m11 * m.m12 + n.m12 * m.m22; + m13 = n.m10 * m.m03 + n.m11 * m.m13 + n.m12 * m.m23 + n.m13; + m20 = n.m20 * m.m00 + n.m21 * m.m10 + n.m22 * m.m20; + m21 = n.m20 * m.m01 + n.m21 * m.m11 + n.m22 * m.m21; + m22 = n.m20 * m.m02 + n.m21 * m.m12 + n.m22 * m.m22; + m23 = n.m20 * m.m03 + n.m21 * m.m13 + n.m22 * m.m23 + n.m23; + } - public void reset() - { - m00=1; m01=0; m02=0; m03=0; - m10=0; m11=1; m12=0; m13=0; - m20=0; m21=0; m22=1; m23=0; - m30=0; m31=0; m32=0; m33=1; - } - } + public void preTransform(warp_Matrix n) + { + warp_Matrix m = this.getClone(); + + m00 = m.m00 * n.m00 + m.m01 * n.m10 + m.m02 * n.m20; + m01 = m.m00 * n.m01 + m.m01 * n.m11 + m.m02 * n.m21; + m02 = m.m00 * n.m02 + m.m01 * n.m12 + m.m02 * n.m22; + m03 = m.m00 * n.m03 + m.m01 * n.m13 + m.m02 * n.m23 + m.m03; + m10 = m.m10 * n.m00 + m.m11 * n.m10 + m.m12 * n.m20; + m11 = m.m10 * n.m01 + m.m11 * n.m11 + m.m12 * n.m21; + m12 = m.m10 * n.m02 + m.m11 * n.m12 + m.m12 * n.m22; + m13 = m.m10 * n.m03 + m.m11 * n.m13 + m.m12 * n.m23 + m.m13; + m20 = m.m20 * n.m00 + m.m21 * n.m10 + m.m22 * n.m20; + m21 = m.m20 * n.m01 + m.m21 * n.m11 + m.m22 * n.m21; + m22 = m.m20 * n.m02 + m.m21 * n.m12 + m.m22 * n.m22; + m23 = m.m20 * n.m03 + m.m21 * n.m13 + m.m22 * n.m23 + m.m23; + } + + public static warp_Matrix multiply(warp_Matrix m1, warp_Matrix m2) + { + return new warp_Matrix + { + m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20, + m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21, + m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22, + m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03, + m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20, + m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21, + m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22, + m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13, + m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20, + m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21, + m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22, + m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 + }; + } + + /* + public string toString() + { + // todo + } + */ + public warp_Matrix getClone() + { + return new warp_Matrix() + { + m00 = m00, + m01 = m01, + m02 = m02, + m03 = m03, + m10 = m10, + m11 = m11, + m12 = m12, + m13 = m13, + m20 = m20, + m21 = m21, + m22 = m22, + m23 = m23, + m30 = m30, + m31 = m31, + m32 = m32, + m33 = m33 + }; + } + + public warp_Matrix inverse() + { + warp_Matrix m = new warp_Matrix(); + + float q1 = m12; float q6 = m10 * m01; float q7 = m10 * m21; float q8 = m02; + float q13 = m20 * m01; float q14 = m20 * m11; float q21 = m02 * m21; float q22 = m03 * m21; + float q25 = m01 * m12; float q26 = m01 * m13; float q27 = m02 * m11; float q28 = m03 * m11; + float q29 = m10 * m22; float q30 = m10 * m23; float q31 = m20 * m12; float q32 = m20 * m13; + float q35 = m00 * m22; float q36 = m00 * m23; float q37 = m20 * m02; float q38 = m20 * m03; + float q41 = m00 * m12; float q42 = m00 * m13; float q43 = m10 * m02; float q44 = m10 * m03; + float q45 = m00 * m11; float q48 = m00 * m21; + float q49 = q45 * m22 - q48 * q1 - q6 * m22 + q7 * q8; + float q50 = q13 * q1 - q14 * q8; + float q51 = 1 / (q49 + q50); + + m.m00 = (m11 * m22 * m33 - m11 * m23 * m32 - m21 * m12 * m33 + m21 * m13 * m32 + m31 * m12 * m23 - m31 * m13 * m22) * q51; + m.m01 = -(m01 * m22 * m33 - m01 * m23 * m32 - q21 * m33 + q22 * m32) * q51; + m.m02 = (q25 * m33 - q26 * m32 - q27 * m33 + q28 * m32) * q51; + m.m03 = -(q25 * m23 - q26 * m22 - q27 * m23 + q28 * m22 + q21 * m13 - q22 * m12) * q51; + m.m10 = -(q29 * m33 - q30 * m32 - q31 * m33 + q32 * m32) * q51; + m.m11 = (q35 * m33 - q36 * m32 - q37 * m33 + q38 * m32) * q51; + m.m12 = -(q41 * m33 - q42 * m32 - q43 * m33 + q44 * m32) * q51; + m.m13 = (q41 * m23 - q42 * m22 - q43 * m23 + q44 * m22 + q37 * m13 - q38 * m12) * q51; + m.m20 = (q7 * m33 - q30 * m31 - q14 * m33 + q32 * m31) * q51; + m.m21 = -(q48 * m33 - q36 * m31 - q13 * m33 + q38 * m31) * q51; + m.m22 = (q45 * m33 - q42 * m31 - q6 * m33 + q44 * m31) * q51; + m.m23 = -(q45 * m23 - q42 * m21 - q6 * m23 + q44 * m21 + q13 * m13 - q38 * m11) * q51; + + return m; + } + + public void reset() + { + m00 = 1f; m01 = 0f; m02 = 0f; m03 = 0f; + m10 = 0f; m11 = 1f; m12 = 0f; m13 = 0f; + m20 = 0f; m21 = 0f; m22 = 1f; m23 = 0f; + m30 = 0f; m31 = 0f; m32 = 0f; m33 = 1f; + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Object.cs b/trunk/managed/Warp3Dmod/warp_Object.cs index 109a75ff..8723c681 100644 --- a/trunk/managed/Warp3Dmod/warp_Object.cs +++ b/trunk/managed/Warp3Dmod/warp_Object.cs @@ -1,60 +1,49 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Warp3D { /// /// Summary description for warp_Object. /// - public class warp_Object: warp_CoreObject + public class warp_Object : warp_CoreObject { - public Object userData = null; // Can be freely used - public String user = null; // Can be freely used - - public List vertexData = new List(1024); - public List triangleData = new List(1024); + public List vertexData; + public List triangleData; public int id; // This object's index - public String name = ""; // This object's name + public string name = ""; // This object's name public bool visible = true; // Visibility tag public warp_Scene parent = null; private bool dirty = true; // Flag for dirty handling - public warp_Vertex[] fastvertex; - public warp_Triangle[] fasttriangle; - - public int vertices = 0; - public int triangles = 0; + public warp_Vector fastMin; + public warp_Vector fastMax; public warp_Material material = null; public int projectedmaxMips = 0; - public int fastcolor; - public int fastreflectivity; - public int[] fastenvmappixels; - public warp_Texture fasttexture; - public int[] fasttpixels; - public int fasttw; - public int fastth; - public int fasttbitw; - public int fasttbith; - public int fastmode; - public warp_Object() { + vertexData = new List(256); + triangleData = new(256); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vertex vertex(int id) { - return (warp_Vertex)vertexData[id]; + return vertexData[id]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Triangle triangle(int id) { - return (warp_Triangle)triangleData[id]; + return triangleData[id]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void addVertex(warp_Vertex newVertex) { newVertex.parent = this; @@ -62,6 +51,7 @@ namespace Warp3D dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void addTriangle(warp_Triangle newTriangle) { newTriangle.parent = this; @@ -69,31 +59,42 @@ namespace Warp3D dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void addTriangle(int v1, int v2, int v3) { addTriangle(vertex(v1), vertex(v2), vertex(v3)); + dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void removeVertex(warp_Vertex v) { vertexData.Remove(v); + dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void removeTriangle(warp_Triangle t) { triangleData.Remove(t); + dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void removeVertexAt(int pos) { vertexData.RemoveAt(pos); + dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void removeTriangleAt(int pos) { triangleData.RemoveAt(pos); + dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void setMaterial(warp_Material m) { material = m; @@ -101,27 +102,46 @@ namespace Warp3D public void rebuild() { - if(!dirty) + if (!dirty) return; - dirty = false; - vertices = vertexData.Count; - fastvertex = vertexData.ToArray(); + float minX = 0; + float maxX = minX; + float minY = 0; + float maxY = minY; + float minZ = 0; + float maxZ = minZ; + float t; - // Generate faster structure for triangles - triangles = triangleData.Count; - fasttriangle = triangleData.ToArray(); - - for(int i = vertices - 1; i >= 0; i--) + for (int i = 0; i < vertexData.Count; i++) { - fastvertex[i].id = i; - fastvertex[i].resetNeighbors(); + vertexData[i].id = i; + vertexData[i].resetNeighbors(); + + t = vertexData[i].pos.x; + if (t < minX) + minX = t; + if (t > maxX) + maxX = t; + + t = vertexData[i].pos.y; + if (t < minY) + minY = t; + if (t > maxY) + maxY = t; + + t = vertexData[i].pos.z; + if (t < minZ) + minZ = t; + if (t > maxZ) + maxZ = t; } + fastMin = new warp_Vector(minX, minY, minZ); + fastMax = new warp_Vector(maxX, maxY, maxZ); - warp_Triangle tri; - for(int i = triangles - 1; i >= 0; i--) + for (int i = 0; i < triangleData.Count; i++) { - tri = fasttriangle[i]; + warp_Triangle tri = triangleData[i]; tri.id = i; tri.p1.registerNeighbor(tri); tri.p2.registerNeighbor(tri); @@ -129,92 +149,54 @@ namespace Warp3D } regenerate(); + dirty = false; } - /* - public void rebuild() - { - if (!dirty) return; - dirty=false; - - Enumeration enum; - - // Generate faster structure for vertices - vertices=vertexData.size(); - vertex=new warp_Vertex[vertices]; - enum=vertexData.elements(); - for (int i=vertices-1;i>=0;i--) vertex[i]=(warp_Vertex)enum.nextElement(); - - // Generate faster structure for triangles - triangles=triangleData.size(); - triangle=new warp_Triangle[triangles]; - enum=triangleData.elements(); - for (int i=triangles-1;i>=0;i--) - { - triangle[i]=(warp_Triangle)enum.nextElement(); - triangle[i].id=i; - } - - for (int i=vertices-1;i>=0;i--) - { - vertex[i].id=i; - vertex[i].resetNeighbors(); - } - - warp_Triangle tri; - for (int i=triangles-1;i>=0;i--) - { - tri=triangle[i]; - tri.p1.registerNeighbor(tri); - tri.p2.registerNeighbor(tri); - tri.p3.registerNeighbor(tri); - } - - regenerate(); - } - */ - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void addVertex(float x, float y, float z) { - addVertex(new warp_Vertex(x, y, z)); + addVertex(new(x, y, z)); + dirty = true; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void addVertex(float x, float y, float z, float u, float v) { - warp_Vertex vert = new warp_Vertex(x, y, z); - vert.setUV(u, v); - addVertex(vert); + addVertex(new warp_Vertex(x, y, z, u, v)); + dirty = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void addTriangle(warp_Vertex a, warp_Vertex b, warp_Vertex c) { addTriangle(new warp_Triangle(a, b, c)); + dirty = true; } public void regenerate() // Regenerates the vertex normals { - for(int i = 0; i < triangles; i++) - fasttriangle[i].regenerateNormal(); - for(int i = 0; i < vertices; i++) - fastvertex[i].regenerateNormal(); + for (int i = 0; i < triangleData.Count; i++) + triangleData[i].regenerateNormal(); + for (int i = 0; i < vertexData.Count; i++) + vertexData[i].regenerateNormal(); } + /* public void remapUV(int w, int h, float sx, float sy) { rebuild(); - for(int j = 0, p = 0; j < h; j++) + for (int j = 0, p = 0; j < h; j++) { float v = ((float)j / (float)(h - 1)) * sy; - for(int i = 0; i < w; i++) + for (int i = 0; i < w; i++) { float u = ((float)i / (float)(w - 1)) * sx; - fastvertex[p++].setUV(u, v); + vertexData[p++].setUV(u, v); } } } - + */ /* public void tilt(float fact) { @@ -226,46 +208,6 @@ namespace Warp3D } */ - public warp_Vector minimum() - { - if(vertices == 0) - return new warp_Vector(0f, 0f, 0f); - float minX = fastvertex[0].pos.x; - float minY = fastvertex[0].pos.y; - float minZ = fastvertex[0].pos.z; - for(int i = 1; i < vertices; i++) - { - if(fastvertex[i].pos.x < minX) - minX = fastvertex[i].pos.x; - if(fastvertex[i].pos.y < minY) - minY = fastvertex[i].pos.y; - if(fastvertex[i].pos.z < minZ) - minZ = fastvertex[i].pos.z; - } - - return new warp_Vector(minX, minY, minZ); - } - - public warp_Vector maximum() - { - if(vertices == 0) - return new warp_Vector(0f, 0f, 0f); - float maxX = fastvertex[0].pos.x; - float maxY = fastvertex[0].pos.y; - float maxZ = fastvertex[0].pos.z; - for(int i = 1; i < vertices; i++) - { - if(fastvertex[i].pos.x > maxX) - maxX = fastvertex[i].pos.x; - if(fastvertex[i].pos.y > maxY) - maxY = fastvertex[i].pos.y; - if(fastvertex[i].pos.z > maxZ) - maxZ = fastvertex[i].pos.z; - } - return new warp_Vector(maxX, maxY, maxZ); - } - - public void detach() // Centers the object in its coordinate system // The offset from origin to object center will be transfered to the matrix, @@ -274,11 +216,11 @@ namespace Warp3D { warp_Vector center = getCenter(); - for(int i = 0; i < vertices; i++) + for (int i = 0; i < vertexData.Count; i++) { - fastvertex[i].pos.x -= center.x; - fastvertex[i].pos.y -= center.y; - fastvertex[i].pos.z -= center.z; + vertexData[i].pos.x -= center.x; + vertexData[i].pos.y -= center.y; + vertexData[i].pos.z -= center.z; } shift(center); @@ -287,19 +229,13 @@ namespace Warp3D public warp_Vector getCenter() // Returns the center of this object { - warp_Vector max = maximum(); - warp_Vector min = minimum(); - - return new warp_Vector((max.x + min.x) / 2, (max.y + min.y) / 2, (max.z + min.z) / 2); + return new warp_Vector((fastMax.x + fastMin.x) / 2, (fastMax.y + fastMin.y) / 2, (fastMax.z + fastMin.z) / 2); } public warp_Vector getDimension() // Returns the x,y,z - Dimension of this object { - warp_Vector max = maximum(); - warp_Vector min = minimum(); - - return new warp_Vector(max.x - min.x, max.y - min.y, max.z - min.z); + return new warp_Vector(fastMax.x - fastMin.x, fastMax.y - fastMin.y, fastMax.z - fastMin.z); } public void matrixMeltdown() @@ -307,14 +243,13 @@ namespace Warp3D // and resets the matrix to untransformed. { rebuild(); - for(int i = vertices - 1; i >= 0; i--) - fastvertex[i].pos = fastvertex[i].pos.transform(matrix); + for (int i = 0; i < vertexData.Count; i++) + vertexData[i].pos = vertexData[i].pos.transform(matrix); regenerate(); matrix.reset(); normalmatrix.reset(); } - public void destroy() { name = null; @@ -323,22 +258,17 @@ namespace Warp3D normalmatrix = null; parent = null; - fastvertex = null; - fasttriangle = null; - vertexData.Clear(); - triangleData.Clear(); vertexData = null; triangleData = null; } - public warp_Object getClone() { - warp_Object obj = new warp_Object(); + warp_Object obj = new(); rebuild(); - for(int i = 0; i < vertices; i++) - obj.addVertex(fastvertex[i].getClone()); - for(int i = 0; i < triangles; i++) - obj.addTriangle(fasttriangle[i].getClone()); + for (int i = 0; i < vertexData.Count; i++) + obj.addVertex(vertexData[i].getClone()); + for (int i = 0; i < triangleData.Count; i++) + obj.addTriangle(triangleData[i].getClone()); obj.name = name + " [cloned]"; obj.material = material; obj.matrix = matrix.getClone(); @@ -347,66 +277,6 @@ namespace Warp3D return obj; } - private const int F = 0; // FLAT - private const int W = 1; // WIREFRAME - private const int P = 2; // PHONG - private const int E = 4; // ENVMAP - private const int T = 8; // TEXTURED - - public void cacheMaterialData() - { - fastcolor = material.color; - fastreflectivity = material.reflectivity; - fasttexture = material.texture; - if(material.envmap != null) - fastenvmappixels = material.envmap.pixel; - else - fastenvmappixels = null; - - if(fasttexture != null) - { - if(projectedmaxMips < 2) - { - fastcolor = warp_Color.multiply(fastcolor, fasttexture.averageColor); - fasttexture = null; - } - else - { - int mip = projectedmaxMips - 2; -// int mip = fasttexture.maxmips; - if(mip < fasttexture.maxmips) - { - if(!fasttexture.hasmips) - fasttexture.GenMips(); - - fasttpixels = fasttexture.mips[mip]; - fasttbitw = fasttexture.mipsBitWidth[mip]; - fasttbith = fasttexture.mipsBitHeight[mip]; - fasttw = fasttexture.mipstw[mip] - 1; - fastth = fasttexture.mipsth[mip] - 1; - } - else - { - fasttpixels = fasttexture.pixel; - fasttw = fasttexture.width - 1; - fastth = fasttexture.height - 1; - fasttbitw = fasttexture.bitWidth; - fasttbith = fasttexture.bitHeight; - } - } - } - - int m = 0; - if(!material.flat) - m |= P; - if(fastenvmappixels != null) - m |= E; - if(fasttexture != null) - m |= T; - if(material.wireframe) - m |= W; - fastmode = m; - } /* public void removeDuplicateVertices() diff --git a/trunk/managed/Warp3Dmod/warp_ObjectFactory.cs b/trunk/managed/Warp3Dmod/warp_ObjectFactory.cs index 4a66e518..1ff1701e 100644 --- a/trunk/managed/Warp3Dmod/warp_ObjectFactory.cs +++ b/trunk/managed/Warp3Dmod/warp_ObjectFactory.cs @@ -2,308 +2,311 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_ObjectFactory. - /// - public class warp_ObjectFactory - { - public static double pi = 3.1415926535; - public static double deg2rad = pi / 180; + /// + /// Summary description for warp_ObjectFactory. + /// + public class warp_ObjectFactory + { + public static warp_Object SIMPLEPLANE(float size, bool doubleSided) + { + warp_Object newObject = new(); - public static warp_Object SIMPLEPLANE(float size, bool doubleSided) - { - warp_Object newObject = new warp_Object(); + newObject.addVertex(new(-size, 0f, size, 0, 0)); + newObject.addVertex(new(size, 0f, size, 1f, 0)); + newObject.addVertex(new(size, 0f, -size, 1f, 1f)); + newObject.addVertex(new(-size, 0f, -size, 0, 1f)); - newObject.addVertex( new warp_Vertex( -size, 0f, size, 0, 0 ) ); - newObject.addVertex( new warp_Vertex( size, 0f, size, 1f, 0 ) ); - newObject.addVertex( new warp_Vertex( size, 0f, -size, 1f, 1f ) ); - newObject.addVertex( new warp_Vertex( -size, 0f, -size, 0, 1f ) ); + newObject.addTriangle(0, 3, 2); + newObject.addTriangle(0, 2, 1); - newObject.addTriangle(0, 3, 2); - newObject.addTriangle(0, 2, 1); + if (doubleSided) + { + newObject.addTriangle(0, 2, 3); + newObject.addTriangle(0, 1, 2); + } - if(doubleSided) - { - newObject.addTriangle(0, 2, 3); - newObject.addTriangle(0, 1, 2); - } + return newObject; + } - return newObject; - } + public static warp_Object CUBE(float size) + { + return BOX(size, size, size); + } - public static warp_Object CUBE(float size) - { - return BOX(size, size, size); - } + public static warp_Object BOX(warp_Vector size) + { + return BOX(size.x, size.y, size.z); + } - public static warp_Object BOX(warp_Vector size) - { - return BOX(size.x, size.y, size.z); - } + public static warp_Object BOX(float xsize, float ysize, float zsize) + { + float x = MathF.Abs(xsize) * 0.5f; + float y = MathF.Abs(ysize) * 0.5f; + float z = MathF.Abs(zsize) * 0.5f; - public static warp_Object BOX(float xsize, float ysize, float zsize) - { - float x = (float) Math.Abs(xsize / 2); - float y = (float) Math.Abs(ysize / 2); - float z = (float) Math.Abs(zsize / 2); + float xx, yy, zz; - float xx, yy, zz; + warp_Object n = new(); + int[] xflag = new int[6]; + int[] yflag = new int[6]; + int[] zflag = new int[6]; - warp_Object n = new warp_Object(); - int[] xflag = new int[6]; - int[] yflag = new int[6]; - int[] zflag = new int[6]; + xflag[0] = 10; + yflag[0] = 3; + zflag[0] = 0; + xflag[1] = 10; + yflag[1] = 15; + zflag[1] = 3; + xflag[2] = 15; + yflag[2] = 3; + zflag[2] = 10; + xflag[3] = 10; + yflag[3] = 0; + zflag[3] = 12; + xflag[4] = 0; + yflag[4] = 3; + zflag[4] = 5; + xflag[5] = 5; + yflag[5] = 3; + zflag[5] = 15; - xflag[0] = 10; - yflag[0] = 3; - zflag[0] = 0; - xflag[1] = 10; - yflag[1] = 15; - zflag[1] = 3; - xflag[2] = 15; - yflag[2] = 3; - zflag[2] = 10; - xflag[3] = 10; - yflag[3] = 0; - zflag[3] = 12; - xflag[4] = 0; - yflag[4] = 3; - zflag[4] = 5; - xflag[5] = 5; - yflag[5] = 3; - zflag[5] = 15; + for (int side = 0; side < 6; side++) + { + for (int i = 0; i < 4; i++) + { + xx = ((xflag[side] & (1 << i)) > 0) ? x : -x; + yy = ((yflag[side] & (1 << i)) > 0) ? y : -y; + zz = ((zflag[side] & (1 << i)) > 0) ? z : -z; + n.addVertex(xx, yy, zz, i & 1, (i & 2) >> 1); + } + int t = side << 2; + n.addTriangle(t, t + 2, t + 3); + n.addTriangle(t, t + 3, t + 1); + } - for (int side = 0; side < 6; side++) - { - for (int i = 0; i < 4; i++) - { - xx = ( (xflag[side] & (1 << i)) > 0) ? x : -x; - yy = ( (yflag[side] & (1 << i)) > 0) ? y : -y; - zz = ( (zflag[side] & (1 << i)) > 0) ? z : -z; - n.addVertex(xx, yy, zz, i & 1, (i & 2) >> 1); - } - int t = side << 2; - n.addTriangle(t, t + 2, t + 3); - n.addTriangle(t, t + 3, t + 1); - } + return n; + } - return n; - } + public static warp_Object CONE(float height, float radius, int segments) + { + warp_Vector[] path = new warp_Vector[4]; + float h = height * 0.5f; + path[0] = new warp_Vector(0, h, 0); + path[1] = new warp_Vector(radius, -h, 0); + path[2] = new warp_Vector(radius, -h, 0); + path[3] = new warp_Vector(0, -h, 0); - public static warp_Object CONE(float height, float radius, int segments) - { - warp_Vector[] path = new warp_Vector[4]; - float h = height / 2; - path[0] = new warp_Vector(0, h, 0); - path[1] = new warp_Vector(radius, -h, 0); - path[2] = new warp_Vector(radius, -h, 0); - path[3] = new warp_Vector(0, -h, 0); + return ROTATIONOBJECT(path, segments); + } - return ROTATIONOBJECT(path, segments); - } + public static warp_Object CYLINDER(float height, float radius, int segments) + { + warp_Vector[] path = new warp_Vector[6]; + float h = height * 0.5f; + path[0] = new warp_Vector(0, h, 0); + path[1] = new warp_Vector(radius, h, 0); + path[2] = new warp_Vector(radius, h, 0); + path[3] = new warp_Vector(radius, -h, 0); + path[4] = new warp_Vector(radius, -h, 0); + path[5] = new warp_Vector(0, -h, 0); - public static warp_Object CYLINDER(float height, float radius,int segments) - { - warp_Vector[] path = new warp_Vector[6]; - float h = height / 2; - path[0] = new warp_Vector(0, h, 0); - path[1] = new warp_Vector(radius, h, 0); - path[2] = new warp_Vector(radius, h, 0); - path[3] = new warp_Vector(radius, -h, 0); - path[4] = new warp_Vector(radius, -h, 0); - path[5] = new warp_Vector(0, -h, 0); + return ROTATIONOBJECT(path, segments); + } - return ROTATIONOBJECT(path, segments); - } + public static warp_Object SPHERE(float radius, int segments) + { + warp_Vector[] path = new warp_Vector[segments]; - public static warp_Object SPHERE(float radius, int segments) - { - warp_Vector[] path = new warp_Vector[segments]; + float x, y, angle; - float x, y, angle; + path[0] = new warp_Vector(0, radius, 0); + path[segments - 1] = new warp_Vector(0, -radius, 0); - path[0] = new warp_Vector(0, radius, 0); - path[segments - 1] = new warp_Vector(0, -radius, 0); + for (int i = 1; i < segments - 1; i++) + { + angle = (((float)i / (float)(segments - 2)) - 0.5f) * -3.14159265f; + x = MathF.Cos(angle) * radius; + y = MathF.Sin(angle) * radius; + path[i] = new warp_Vector(x, y, 0); + } - for (int i = 1; i < segments - 1; i++) - { - angle = - ( ( (float) i / (float) (segments - 2)) - 0.5f) * - 3.14159265f; - x = (float) Math.Cos(angle) * radius; - y = (float) Math.Sin(angle) * radius; - path[i] = new warp_Vector(x, y, 0); - } + return ROTATIONOBJECT(path, segments); + } - return ROTATIONOBJECT(path, segments); - } + public static warp_Object ROTATIONOBJECT(warp_Vector[] path, int sides) + { + int steps = sides + 1; + warp_Object newObject = new(); + double alpha = 2 * Math.PI / ((double)steps - 1); + float qx, qz; + int nodes = path.GetLength(0); + warp_Vertex vertex; + float u, v; // Texture coordinates - public static warp_Object ROTATIONOBJECT(warp_Vector[] path, int sides) - { - int steps = sides + 1; - warp_Object newObject = new warp_Object(); - double alpha = 2 * pi / ( (double) steps - 1); - float qx, qz; - int nodes = path.GetLength(0); - warp_Vertex vertex = null; - float u, v; // Texture coordinates + for (int j = 0; j < steps; j++) + { + u = (float)(steps - j - 1) / (float)(steps - 1); + for (int i = 0; i < nodes; i++) + { + v = (float)i / (float)(nodes - 1); + qx = (float)(path[i].x * Math.Cos(j * alpha) + + path[i].z * Math.Sin(j * alpha)); + qz = (float)(path[i].z * Math.Cos(j * alpha) - + path[i].x * Math.Sin(j * alpha)); + vertex = new warp_Vertex(qx, path[i].y, qz) + { + u = u, + v = v + }; + newObject.addVertex(vertex); + } + } - for (int j = 0; j < steps; j++) - { - u = (float) (steps - j - 1) / (float) (steps - 1); - for (int i = 0; i < nodes; i++) - { - v = (float) i / (float) (nodes - 1); - qx = (float) (path[i].x * Math.Cos(j * alpha) + - path[i].z * Math.Sin(j * alpha)); - qz = (float) (path[i].z * Math.Cos(j * alpha) - - path[i].x * Math.Sin(j * alpha)); - vertex = new warp_Vertex(qx, path[i].y, qz); - vertex.u = u; - vertex.v = v; - newObject.addVertex(vertex); - } - } + for (int j = 0; j < steps - 1; j++) + { + for (int i = 0; i < nodes - 1; i++) + { + newObject.addTriangle(i + nodes * j, i + nodes * (j + 1), + i + 1 + nodes * j); + newObject.addTriangle(i + nodes * (j + 1), + i + 1 + nodes * (j + 1), + i + 1 + nodes * j); + } + } - for (int j = 0; j < steps - 1; j++) - { - for (int i = 0; i < nodes - 1; i++) - { - newObject.addTriangle(i + nodes * j, i + nodes * (j + 1), - i + 1 + nodes * j); - newObject.addTriangle(i + nodes * (j + 1), - i + 1 + nodes * (j + 1), - i + 1 + nodes * j); + for (int i = 0; i < nodes - 1; i++) + { + newObject.addTriangle(i + nodes * (steps - 1), i, + i + 1 + nodes * (steps - 1)); + newObject.addTriangle(i, i + 1, i + 1 + nodes * (steps - 1)); + } + return newObject; - } - } + } - for (int i = 0; i < nodes - 1; i++) - { - newObject.addTriangle(i + nodes * (steps - 1), i, - i + 1 + nodes * (steps - 1)); - newObject.addTriangle(i, i + 1, i + 1 + nodes * (steps - 1)); - } - return newObject; + /* + public static warp_Object TORUSKNOT(float p, float q, float r_tube, float r_out, float r_in, float h, int segments, int steps) + { + float x, y, z, r, t, theta; - } + warp_Vector[] path = new warp_Vector[segments + 1]; + for (int i = 0; i < segments + 1; i++) + { + t = 2f * 3.14159265f * i / (float)segments; + r = r_out + r_in * MathF.Cos(p * t); + z = h * MathF.Sin(p * t); + theta = q * t; + x = r * MathF.Cos(theta); + y = r * MathF.Sin(theta); + path[i] = new warp_Vector(x, y, z); + } + return TUBE(path, r_tube, steps, true); + } + */ + /* + public static warp_Object SPIRAL(float h, float r_out, float r_in, + float r_tube, float w, float f, + int segments, int steps) + { + float x, y, z, r, t, theta; - public static warp_Object TORUSKNOT(float p, float q, float r_tube,float r_out, float r_in, float h,int segments, int steps) - { - float x, y, z, r, t, theta; + warp_Vector[] path = new warp_Vector[segments + 1]; + for (int i = 0; i < segments + 1; i++) + { + t = (float)i / (float)segments; + r = r_out + r_in * MathF.Sin(2f * 3.14159265f * f * t); + z = (h * 0.5f) + h * t; + theta = 2f * 3.14159265f * w * t; + x = r * MathF.Cos(theta); + y = r * MathF.Sin(theta); + path[i] = new warp_Vector(x, y, z); + } + return TUBE(path, r_tube, steps, false); + } + */ - warp_Vector[] path = new warp_Vector[segments + 1]; - for (int i = 0; i < segments + 1; i++) - { - t = 2 * 3.14159265f * i / (float) segments; - r = r_out + r_in * warp_Math.cos(p * t); - z = h * warp_Math.sin(p * t); - theta = q * t; - x = r * warp_Math.cos(theta); - y = r * warp_Math.sin(theta); - path[i] = new warp_Vector(x, y, z); - } - return TUBE(path, r_tube, steps, true); - } + /* + public static warp_Object TUBE(warp_Vector[] path, float r, int steps, bool closed) + { + warp_Vector[] circle = new warp_Vector[steps]; + float angle; + for (int i = 0; i < steps; i++) + { + angle = 2f * 3.14159265f * (float)i / (float)steps; + circle[i] = new warp_Vector(r * MathF.Cos(angle), r * MathF.Sin(angle), 0f); + } - public static warp_Object SPIRAL(float h, float r_out, float r_in, - float r_tube, float w, float f, - int segments, int steps) - { - float x, y, z, r, t, theta; + warp_Object newObject = new(); + int segments = path.GetLength(0); + warp_Vector forward, up, right; + warp_Matrix frenetmatrix; + warp_Vertex tempvertex; + float relx, rely; + int a, b, c, d; - warp_Vector[] path = new warp_Vector[segments + 1]; - for (int i = 0; i < segments + 1; i++) - { - t = (float) i / (float) segments; - r = r_out + r_in * warp_Math.sin(2 * 3.14159265f * f * t); - z = (h / 2) + h * t; - theta = 2 * 3.14159265f * w * t; - x = r * warp_Math.cos(theta); - y = r * warp_Math.sin(theta); - path[i] = new warp_Vector(x, y, z); - } - return TUBE(path, r_tube, steps, false); - } + for (int i = 0; i < segments; i++) + { + // Calculate frenet frame matrix - public static warp_Object TUBE(warp_Vector[] path, float r, int steps, bool closed) - { - warp_Vector[] circle = new warp_Vector[steps]; - float angle; - for (int i = 0; i < steps; i++) - { - angle = 2 * 3.14159265f * (float) i / (float) steps; - circle[i] = new warp_Vector(r * warp_Math.cos(angle), - r * warp_Math.sin(angle), 0f); - } + if (i != segments - 1) + { + forward = warp_Vector.sub(ref path[i + 1], ref path[i]); + } + else + { + if (!closed) + { + forward = warp_Vector.sub(ref path[i], ref path[i - 1]); + } + else + { + forward = warp_Vector.sub(ref path[1], ref path[0]); + } + } - warp_Object newObject = new warp_Object(); - int segments = path.GetLength(0); - warp_Vector forward, up, right; - warp_Matrix frenetmatrix; - warp_Vertex tempvertex; - float relx, rely; - int a, b, c, d; + forward.normalize(); + up = new warp_Vector(0f, 0f, 1f); + right = warp_Vector.getPlaneNormal(ref forward, ref up); + up = warp_Vector.getPlaneNormal(ref forward, ref right); + frenetmatrix = new warp_Matrix(right, up, forward); + frenetmatrix.shift(path[i].x, path[i].y, path[i].z); - for (int i = 0; i < segments; i++) - { - // Calculate frenet frame matrix + // Add nodes - if (i != segments - 1) - { - forward = warp_Vector.sub(path[i + 1], path[i]); - } - else - { - if (!closed) - { - forward = warp_Vector.sub(path[i], path[i - 1]); - } - else - { - forward = warp_Vector.sub(path[1], path[0]); - } - } + relx = (float)i / (float)(segments - 1); + for (int k = 0; k < steps; k++) + { + rely = (float)k / (float)steps; + tempvertex = new warp_Vertex(circle[k].transform(frenetmatrix)) + { + u = relx, + v = rely + }; + newObject.addVertex(tempvertex); + } + } - forward.normalize(); - up = new warp_Vector(0f, 0f, 1f); - right = warp_Vector.getNormal(forward, up); - up = warp_Vector.getNormal(forward, right); - frenetmatrix = new warp_Matrix(right, up, forward); - frenetmatrix.shift(path[i].x, path[i].y, path[i].z); + for (int i = 0; i < segments - 1; i++) + { + for (int k = 0; k < steps - 1; k++) + { + a = i * steps + k; + b = a + 1; + c = a + steps; + d = b + steps; + newObject.addTriangle(a, c, b); + newObject.addTriangle(b, c, d); + } + a = (i + 1) * steps - 1; + b = a + 1 - steps; + c = a + steps; + d = b + steps; + newObject.addTriangle(a, c, b); + newObject.addTriangle(b, c, d); + } - // Add nodes - - relx = (float) i / (float) (segments - 1); - for (int k = 0; k < steps; k++) - { - rely = (float) k / (float) steps; - tempvertex = new warp_Vertex(circle[k].transform(frenetmatrix)); - tempvertex.u = relx; - tempvertex.v = rely; - newObject.addVertex(tempvertex); - } - } - - for (int i = 0; i < segments - 1; i++) - { - for (int k = 0; k < steps - 1; k++) - { - a = i * steps + k; - b = a + 1; - c = a + steps; - d = b + steps; - newObject.addTriangle(a, c, b); - newObject.addTriangle(b, c, d); - } - a = (i + 1) * steps - 1; - b = a + 1 - steps; - c = a + steps; - d = b + steps; - newObject.addTriangle(a, c, b); - newObject.addTriangle(b, c, d); - } - - return newObject; - } - } + return newObject; + } + */ + } } diff --git a/trunk/managed/Warp3Dmod/warp_Quaternion.cs b/trunk/managed/Warp3Dmod/warp_Quaternion.cs index c560855c..fa8b14b6 100644 --- a/trunk/managed/Warp3Dmod/warp_Quaternion.cs +++ b/trunk/managed/Warp3Dmod/warp_Quaternion.cs @@ -1,49 +1,44 @@ using System; using System.Collections; using System.Text; -using System.Diagnostics; +using System.Diagnostics; namespace Warp3D { - public class warp_Quaternion + public struct warp_Quaternion { - public float X; - public float Y; - public float Z; - public float W; + public float X = 0; + public float Y = 0; + public float Z = 0; + public float W = 1; public warp_Quaternion() { } - public warp_Quaternion( float x, float y, float z, float w ) + public warp_Quaternion(float x, float y, float z, float w) { - this.X = x; - this.Y = y; - this.Z = z; - this.W = w; + X = x; + Y = y; + Z = z; + W = w; } - public warp_Quaternion getClone() + static public warp_Quaternion matrix(warp_Matrix xfrm) { - return new warp_Quaternion( this.X, this.Y, this.Z, this.W ); - } - - static public warp_Quaternion matrix( warp_Matrix xfrm ) - { - warp_Quaternion quat = new warp_Quaternion(); + warp_Quaternion quat = new(); // Check the sum of the diagonal - float tr = xfrm[ 0, 0 ] + xfrm[ 1, 1 ] + xfrm[ 2, 2 ]; - if ( tr > 0.0f ) + float tr = xfrm[0, 0] + xfrm[1, 1] + xfrm[2, 2]; + if (tr > 0.0f) { // The sum is positive // 4 muls, 1 div, 6 adds, 1 trig function call - float s = ( float )Math.Sqrt( tr + 1.0f ); + float s = MathF.Sqrt(tr + 1.0f); quat.W = s * 0.5f; s = 0.5f / s; - quat.X = ( xfrm[ 1, 2 ] - xfrm[ 2, 1 ] ) * s; - quat.Y = ( xfrm[ 2, 0 ] - xfrm[ 0, 2 ] ) * s; - quat.Z = ( xfrm[ 0, 1 ] - xfrm[ 1, 0 ] ) * s; + quat.X = (xfrm[1, 2] - xfrm[2, 1]) * s; + quat.Y = (xfrm[2, 0] - xfrm[0, 2]) * s; + quat.Z = (xfrm[0, 1] - xfrm[1, 0]) * s; } else { @@ -52,70 +47,57 @@ namespace Warp3D int[] nIndex = { 1, 2, 0 }; int i, j, k; i = 0; - if ( xfrm[ 1, 1 ] > xfrm[ i, i ] ) + if (xfrm[1, 1] > xfrm[i, i]) i = 1; - if ( xfrm[ 2, 2 ] > xfrm[ i, i ] ) + if (xfrm[2, 2] > xfrm[i, i]) i = 2; - j = nIndex[ i ]; - k = nIndex[ j ]; + j = nIndex[i]; + k = nIndex[j]; - float s = ( float )Math.Sqrt( ( xfrm[ i, i ] - ( xfrm[ j, j ] + xfrm[ k, k ] ) ) + 1.0f ); - quat[ i ] = s * 0.5f; - if ( s != 0.0 ) + float s = MathF.Sqrt((xfrm[i, i] - (xfrm[j, j] + xfrm[k, k])) + 1.0f); + quat[i] = s * 0.5f; + if (s != 0.0) { s = 0.5f / s; } - quat[ j ] = ( xfrm[ i, j ] + xfrm[ j, i ] ) * s; - quat[ k ] = ( xfrm[ i, k ] + xfrm[ k, i ] ) * s; - quat[ 3 ] = ( xfrm[ j, k ] - xfrm[ k, j ] ) * s; + quat[j] = (xfrm[i, j] + xfrm[j, i]) * s; + quat[k] = (xfrm[i, k] + xfrm[k, i]) * s; + quat[3] = (xfrm[j, k] - xfrm[k, j]) * s; } return quat; } - public float this[ int index ] + public float this[int index] { get { - Debug.Assert( 0 <= index && index <= 3 ); - if ( index <= 1 ) + Debug.Assert(0 <= index && index <= 3); + return index switch { - if ( index == 0 ) - { - return this.X; - } - return this.Y; - } - if ( index == 2 ) - { - return this.Z; - } - return this.W; + 0 => X, + 1 => Y, + 2 => Z, + _ => W, + }; } set { - Debug.Assert( 0 <= index && index <= 3 ); - if ( index <= 1 ) + Debug.Assert(0 <= index && index <= 3); + switch (index) { - if ( index == 0 ) - { - this.X = value; - } - else - { - this.Y = value; - } - } - else - { - if ( index == 2 ) - { - this.Z = value; - } - else - { - this.W = value; - } + case 0: + X = value; + break; + case 1: + Y = value; + break; + case 2: + Z = value; + break; + case 3: + W = value; + break; } } } diff --git a/trunk/managed/Warp3Dmod/warp_Rasterizer.cs b/trunk/managed/Warp3Dmod/warp_Rasterizer.cs index 9ccac131..8a3ac348 100644 --- a/trunk/managed/Warp3Dmod/warp_Rasterizer.cs +++ b/trunk/managed/Warp3Dmod/warp_Rasterizer.cs @@ -1,4 +1,6 @@ using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Warp3D { @@ -32,15 +34,14 @@ namespace Warp3D private const int P = 2; // PHONG private const int E = 4; // ENVMAP private const int T = 8; // TEXTURED - private int SHADED = 0; + private const int OPAQUE = 16; // opace private int y, z, dx, dy, dz, offset, xL, xR, xBase, zBase, xMax, dxL, dxR, dzBase, nxBase, nyBase, dnx, dny, nx, ny, - dnxBase, dnyBase, - shademode; + dnxBase, dnyBase; private float txBase, tyBase, @@ -56,8 +57,6 @@ namespace Warp3D // Constructor public warp_Rasterizer(warp_RenderPipeline pipeline) { - SHADED = P | E | T; - rebuildReferences(pipeline); loadLightmap(pipeline.lightmap); } @@ -75,6 +74,10 @@ namespace Warp3D { screen = null; zBuffer = null; + tpixels = null; + envmap = null; + diffuse = null; + specular = null; } // Lightmap loader @@ -95,12 +98,9 @@ namespace Warp3D color = material.color; reflectivity = material.reflectivity; texture = material.texture; - if (material.envmap != null) - envmap = material.envmap.pixel; - else - envmap = null; + envmap = material.envmap is null ? null : envmap = material.envmap.pixel; - if (texture != null) + if (texture is not null) { if (obj.projectedmaxMips < 2) { @@ -109,12 +109,12 @@ namespace Warp3D } else { - int mip = obj.projectedmaxMips - 1; - if (mip > texture.maxmips) - mip = texture.maxmips; - - if (texture.mips[mip] != null) + int mip = obj.projectedmaxMips - 2; + if (mip < texture.maxmips) { + if (!texture.hasmips) + texture.GenMips(); + tpixels = texture.mips[mip]; tbitW = texture.mipsBitWidth[mip]; tbitH = texture.mipsBitHeight[mip]; @@ -135,43 +135,22 @@ namespace Warp3D mode = 0; if (!material.flat) mode |= P; - if (envmap != null) + if (envmap is not null) mode |= E; - if (texture != null) + if (texture is not null) mode |= T; if (material.wireframe) mode |= W; + if (material.opaque) + mode |= OPAQUE; - shademode = mode & SHADED; - materialLoaded = true; - ready = lightmapLoaded; - } - - public void loadFastMaterial(warp_Object obj) - { - color = obj.fastcolor; - reflectivity = obj.fastreflectivity; - texture = obj.fasttexture; - envmap = obj.fastenvmappixels; - - if (texture != null) - { - tpixels = obj.fasttpixels; - tw = obj.fasttw; - th = obj.fastth; - tbitW = obj.fasttbitw; - tbitH = obj.fasttbith; - } - - mode = obj.fastmode; - shademode = mode & SHADED; materialLoaded = true; ready = lightmapLoaded; } public void render(warp_Triangle tri) { - if (!ready || tri.parent == null) + if (!ready || tri.parent is null) return; if ((mode & W) != 0) @@ -183,30 +162,34 @@ namespace Warp3D } } + warp_Vertex p1; + warp_Vertex p2; - warp_Vertex p1 = tri.p1; - warp_Vertex p2 = tri.p2; - warp_Vertex p3 = tri.p3; - warp_Vertex tempVertex; + if (tri.p1.y > tri.p2.y) + { + p1 = tri.p2; + p2 = tri.p1; + } + else + { + p1 = tri.p1; + p2 = tri.p2; + } - if (p1.y > p2.y) + warp_Vertex p3; + if (p2.y > tri.p3.y) { - tempVertex = p1; - p1 = p2; - p2 = tempVertex; - } - if (p2.y > p3.y) - { - tempVertex = p2; - p2 = p3; - p3 = tempVertex; - } - if (p1.y > p2.y) - { - tempVertex = p1; - p1 = p2; - p2 = tempVertex; + p3 = p2; + if (p1.y > tri.p3.y) + { + p2 = p1; + p1 = tri.p3; + } + else + p2 = tri.p3; } + else + p3 = tri.p3; if (p1.y >= height) return; @@ -217,7 +200,7 @@ namespace Warp3D if (p1.y == p3.y) return; - if (mode == F) + if ((mode & 0x0f) == F) { int lutID = (int)(tri.n2.x * 127 + 127) + ((int)(tri.n2.y * 127 + 127) << 8); int c = warp_Color.multiply(color, diffuse[lutID]); @@ -236,6 +219,7 @@ namespace Warp3D int dy31 = y3 - y1; float tf = (float)dy / dy31; + int x4 = x1 + (int)((x3 - x1) * tf); dx = (x4 - x2) >> 8; @@ -270,29 +254,28 @@ namespace Warp3D int ny2 = p2.ny; dny = (ny4 - ny2) / dx; - float tx1 = p1.tx * tw; - float tx3 = p3.tx * tw; - float tx4 = tx1 + ((tx3 - tx1) * tf); - float tx2 = p2.tx * tw; - dtx = (tx4 - tx2) / dx; - - float ty1 = p1.ty * th; - float ty3 = p3.ty * th; - float ty4 = ty1 + ((ty3 - ty1) * tf); - float ty2 = p2.ty * th; - dty = (ty4 - ty2) / dx; - float sw1 = p1.invZ; float sw3 = p3.invZ; float sw4 = sw1 + ((sw3 - sw1) * tf); float sw2 = p2.invZ; dsw = (sw4 - sw2) / dx; + float tx1 = sw1 * p1.u * tw; + float tx3 = sw3 * p3.u * tw; + float tx4 = tx1 + ((tx3 - tx1) * tf); + float tx2 = sw2 * p2.u * tw; + dtx = (tx4 - tx2) / dx; + + float ty1 = sw1 * p1.v * th; + float ty3 = sw3 * p3.v * th; + float ty4 = ty1 + ((ty3 - ty1) * tf); + float ty2 = sw2 * p2.v * th; + dty = (ty4 - ty2) / dx; + + if (dx < 0) { - int temp = x2; - x2 = x4; - x4 = temp; + (x4, x2) = (x2, x4); z2 = z4; tx2 = tx4; ty2 = ty4; @@ -336,11 +319,8 @@ namespace Warp3D if (y2 > height) y2 = height; - offset = y1 * width; - for (y = y1; y < y2; y++) - { - renderLine(); - } + + renderLines(y1 * width, y2 * width); } dy = y3 - y2; @@ -379,194 +359,288 @@ namespace Warp3D if (y3 > height) y3 = height; - offset = y2 * width; - for (y = y2; y < y3; y++) - { - renderLine(); - } - } - } - - private void renderLine() - { - xL = xBase >> 16; - xR = xMax >> 16; - z = zBase; - nx = nxBase; - ny = nyBase; - tx = txBase; - ty = tyBase; - sw = swBase; - - if (xL < 0) - { - z -= xL * dz; - nx -= xL * dnx; - ny -= xL * dny; - tx -= xL * dtx; - ty -= xL * dty; - sw -= xL * dsw; - xL = 0; - } - if (xR > width) - xR = width; - - if (mode == F) - renderLineF(); - else - { - switch(shademode) - { - case P: - renderLineP(); - break; - case E: - renderLineE(); - break; - case T: - renderLineT(); - break; - case P | E: - renderLinePE(); - break; - case P | T: - renderLinePT(); - break; - case P | E | T: - renderLinePET(); - break; - } + renderLines(y2 * width, y3 * width); } - offset += width; - xBase += dxL; - xMax += dxR; - zBase += dzBase; - nxBase += dnxBase; - nyBase += dnyBase; - txBase += dtxBase; - tyBase += dtyBase; - swBase += dswBase; - } - - // Fast scanline rendering - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe void renderLineF() - { - int bkgrd; - int c; - int pos; - - fixed (int* sp = screen.pixels, zB = zBuffer) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void renderLines(int offsetStart, int offsetEnd) { - for (int x = xL; x < xR; x++) + ref int rpixels = ref MemoryMarshal.GetArrayDataReference(screen.pixels); + ref int rzbuf = ref MemoryMarshal.GetArrayDataReference(zBuffer); + ref int rdifuse = ref MemoryMarshal.GetArrayDataReference(diffuse); + ref int rspecular = ref MemoryMarshal.GetArrayDataReference(specular); + ref int rtpixels = ref MemoryMarshal.GetArrayDataReference(tpixels); + + for (offset = offsetStart; offset < offsetEnd; offset += width) { - pos = x + offset; - if (z < zBuffer[pos]) + xL = xBase >> 16; + xR = xMax >> 16; + z = zBase; + nx = nxBase; + ny = nyBase; + tx = txBase; + ty = tyBase; + sw = swBase; + + if (xL < 0) { - bkgrd = sp[pos]; - c = warp_Color.overSolid(bkgrd, currentColor); - sp[pos] = c; - zB[pos] = z; + z -= xL * dz; + nx -= xL * dnx; + ny -= xL * dny; + tx -= xL * dtx; + ty -= xL * dty; + sw -= xL * dsw; + xL = 0; } - z += dz; - } - } - } + if (xR > width) + xR = width; - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe void renderLineP() - { - int bkgrd; - int c; - int s; - int lutID; - int pos; - - fixed (int* sp = screen.pixels, zB = zBuffer, df = diffuse, spc = specular) - { - for (int x = xL; x < xR; x++) - { - pos = x + offset; - if (z < zB[pos]) + int posEnd = xR + offset; + switch (mode) { - lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); - bkgrd = sp[pos]; - c = warp_Color.multiply(color, df[lutID]); - s = spc[lutID]; - s = warp_Color.scale(s, reflectivity); - c = warp_Color.overSolid(bkgrd, c); - c = warp_Color.add(c, s); - sp[pos] = c; - zB[pos] = z; - } - z += dz; - nx += dnx; - ny += dny; + case F: + { + //renderLineF; + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + int bkgrd = Unsafe.As(ref Unsafe.Add(ref rpixels, pos)); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.overSolid(bkgrd, currentColor); + } + z += dz; + } + break; + } + + case F | OPAQUE: + { + //renderLineFO + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = currentColor; + } + z += dz; + } + break; + } + case P: + //renderLineP(); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + int c = warp_Color.multiply(color, Unsafe.As(ref Unsafe.Add(ref rdifuse, lutID))); + int s = warp_Color.scale(Unsafe.As(ref Unsafe.Add(ref rspecular, lutID)), reflectivity); + int bkgrd = Unsafe.As(ref Unsafe.Add(ref rpixels, pos)); + c = warp_Color.overSolid(bkgrd, c); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.add(c, s); + } + z += dz; + nx += dnx; + ny += dny; + } + break; + case P | OPAQUE: + //renderLinePO(); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + int c = warp_Color.multiply(color, Unsafe.As(ref Unsafe.Add(ref rdifuse, lutID))); + int s = warp_Color.scale(Unsafe.As(ref Unsafe.Add(ref rspecular, lutID)), reflectivity); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.add(c, s); + } + z += dz; + nx += dnx; + ny += dny; + } + break; + case E: + //renderLineE(); + ref int renvmap = ref MemoryMarshal.GetArrayDataReference(envmap); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + int s = Unsafe.As(ref Unsafe.Add(ref rspecular, lutID)); + s = warp_Color.add(s, Unsafe.As(ref Unsafe.Add(ref renvmap, lutID))); + s = warp_Color.scale(s, reflectivity); + + int bkgrd = Unsafe.As(ref Unsafe.Add(ref rpixels, pos)); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.overSolid(bkgrd, s); + } + + z += dz; + nx += dnx; + ny += dny; + } + break; + + case E | OPAQUE: + //renderLineEO(); + renvmap = ref MemoryMarshal.GetArrayDataReference(envmap); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + int s = Unsafe.As(ref Unsafe.Add(ref rspecular, lutID)); + s = warp_Color.add(s, Unsafe.As(ref Unsafe.Add(ref renvmap, lutID))); + s = warp_Color.scale(s, reflectivity); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.scale(s, reflectivity); + } + + z += dz; + nx += dnx; + ny += dny; + } + break; + case T: + //renderLineT(); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int tpindx = (((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW); + int c = Unsafe.As(ref Unsafe.Add(ref rtpixels, tpindx)); + c = warp_Color.multiply(color, c); + + int bkgrd = Unsafe.As(ref Unsafe.Add(ref rpixels, pos)); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.overSolid(bkgrd, c); + } + + z += dz; + tx += dtx; + ty += dty; + sw += dsw; + } + break; + + case T | OPAQUE: + //renderLineTO(); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int tpindx = (((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW); + int c = Unsafe.As(ref Unsafe.Add(ref rtpixels, tpindx)); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.multiply(color, c); + } + + z += dz; + tx += dtx; + ty += dty; + sw += dsw; + } + break; + case P | E: + renderLinePE(); + break; + case P | E | OPAQUE: + renderLinePEO(); + break; + + case P | T: + //renderLinePT(); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int tpindx = (((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW); + int c = Unsafe.As(ref Unsafe.Add(ref rtpixels, tpindx)); + c = warp_Color.multiply(color, c); + + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + c = warp_Color.multiply(c, Unsafe.As(ref Unsafe.Add(ref rdifuse, lutID))); + int s = warp_Color.scale(Unsafe.As(ref Unsafe.Add(ref rspecular, lutID)), reflectivity); + + int bkgrd = Unsafe.As(ref Unsafe.Add(ref rpixels, pos)); + c = warp_Color.overSolid(bkgrd, c); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.add(c, s); + } + + z += dz; + nx += dnx; + ny += dny; + tx += dtx; + ty += dty; + sw += dsw; + } + break; + + case P | T | OPAQUE: + //renderLinePTO(); + for (int pos = xL + offset; pos < posEnd; pos++) + { + if (z < Unsafe.As(ref Unsafe.Add(ref rzbuf, pos))) + { + Unsafe.As(ref Unsafe.Add(ref rzbuf, pos)) = z; + + int tpindx = (((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW); + int c = Unsafe.As(ref Unsafe.Add(ref rtpixels, tpindx)); + c = warp_Color.multiply(color, c); + + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + c = warp_Color.multiply(c, Unsafe.As(ref Unsafe.Add(ref rdifuse, lutID))); + int s = warp_Color.scale(Unsafe.As(ref Unsafe.Add(ref rspecular, lutID)), reflectivity); + Unsafe.As(ref Unsafe.Add(ref rpixels, pos)) = warp_Color.add(c, s); + } + + z += dz; + nx += dnx; + ny += dny; + tx += dtx; + ty += dty; + sw += dsw; + } + break; + + case P | E | T: + renderLinePET(); + break; + case P | E | T | OPAQUE: + renderLinePETO(); + break; + default: + return; + }; + + xBase += dxL; + xMax += dxR; + zBase += dzBase; + nxBase += dnxBase; + nyBase += dnyBase; + txBase += dtxBase; + tyBase += dtyBase; + swBase += dswBase; } } } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe void renderLineE() - { - int bkgrd; - int s; - int lutID; - int pos; - - fixed (int* sp = screen.pixels, zB = zBuffer, spc = specular, env = envmap) - { - for (int x = xL; x < xR; x++) - { - pos = x + offset; - if (z < zB[pos]) - { - lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); - bkgrd = sp[pos]; - s = warp_Color.add(spc[lutID], env[lutID]); - s = warp_Color.scale(s, reflectivity); - sp[pos] = warp_Color.overSolid(bkgrd, s);; - zB[pos] = z; - } - z += dz; - nx += dnx; - ny += dny; - } - } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe void renderLineT() - { - int bkgrd; - int c; - int pos; - - fixed (int* sp = screen.pixels, zB = zBuffer, tp = tpixels) - { - for (int x = xL; x < xR; x++) - { - pos = x + offset; - if (z < zB[pos]) - { - bkgrd = sp[pos]; - c = tp[(((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW)]; - c = warp_Color.multiply(color, c); - c = warp_Color.overSolid(bkgrd, c); - sp[pos] = c; - zB[pos] = z; - } - z += dz; - tx += dtx; - ty += dty; - sw += dsw; - } - } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void renderLinePE() { int bkgrd; @@ -589,8 +663,7 @@ namespace Warp3D s = warp_Color.scale(s, reflectivity); c = warp_Color.multiply(color, df[lutID]); c = warp_Color.overSolid(bkgrd, c); - c = warp_Color.add(c, s); - sp[pos] = c; + sp[pos] = warp_Color.add(c, s); zB[pos] = z; } z += dz; @@ -600,16 +673,15 @@ namespace Warp3D } } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe void renderLinePT() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void renderLinePEO() { - int bkgrd; int c; int s; int lutID; int pos; - fixed (int* sp = screen.pixels, zB = zBuffer, df = diffuse, spc = specular, tp = tpixels) + fixed (int* sp = screen.pixels, zB = zBuffer, df = diffuse, spc = specular, env = envmap) { for (int x = xL; x < xR; x++) { @@ -618,17 +690,40 @@ namespace Warp3D { lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); - bkgrd = sp[pos]; - s = warp_Color.scale(spc[lutID], reflectivity); - c = tp[(((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW)]; + s = warp_Color.add(spc[lutID], env[lutID]); + s = warp_Color.scale(s, reflectivity); + c = warp_Color.multiply(color, df[lutID]); + sp[pos] = warp_Color.add(c, s); + zB[pos] = z; + } + z += dz; + nx += dnx; + ny += dny; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void renderLinePET() + { + fixed (int* sp = screen.pixels, zB = zBuffer, df = diffuse, spc = specular, tp = tpixels, env = envmap) + { + for (int x = xL; x < xR; x++) + { + int pos = x + offset; + if (z < zB[pos]) + { + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + int bkgrd = sp[pos]; + int s = warp_Color.add(spc[lutID], env[lutID]); + s = warp_Color.scale(s, reflectivity); + int c = tp[(((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW)]; c = warp_Color.multiply(color, c); c = warp_Color.multiply(c, df[lutID]); c = warp_Color.overSolid(bkgrd, c); - c = warp_Color.add(c, s); - sp[pos] = c; + sp[pos] = warp_Color.add(c, s); zB[pos] = z; } - z += dz; nx += dnx; ny += dny; @@ -638,34 +733,23 @@ namespace Warp3D } } } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe void renderLinePET() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void renderLinePETO() { - int bkgrd; - int c; - int s; - int lutID; - int pos; - fixed (int* sp = screen.pixels, zB = zBuffer, df = diffuse, spc = specular, tp = tpixels, env = envmap) { for (int x = xL; x < xR; x++) { - pos = x + offset; + int pos = x + offset; if (z < zB[pos]) { - lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); - bkgrd = sp[pos]; - s = warp_Color.add(spc[lutID], env[lutID]); + int lutID = ((nx >> 16) & 255) + (((ny >> 16) & 255) << 8); + int s = warp_Color.add(spc[lutID], env[lutID]); s = warp_Color.scale(s, reflectivity); - c = tp[(((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW)]; + int c = tp[(((int)(tx / sw)) & tw) + ((((int)(ty / sw)) & th) << tbitW)]; c = warp_Color.multiply(color, c); c = warp_Color.multiply(c, df[lutID]); - c = warp_Color.overSolid(bkgrd, c); - c = warp_Color.add(c, s); - - sp[pos] = c; + sp[pos] = warp_Color.add(c, s); zB[pos] = z; } z += dz; @@ -692,8 +776,8 @@ namespace Warp3D if ((a.clipcode & b.clipcode) != 0) return; - dx = (int)Math.Abs(a.x - b.x); - dy = (int)Math.Abs(a.y - b.y); + dx = (int)MathF.Abs(a.x - b.x); + dy = (int)MathF.Abs(a.y - b.y); dz = 0; fixed (int* sp = screen.pixels, zB = zBuffer) diff --git a/trunk/managed/Warp3Dmod/warp_RenderPipeline.cs b/trunk/managed/Warp3Dmod/warp_RenderPipeline.cs index 2de4eb6b..f8a45522 100644 --- a/trunk/managed/Warp3Dmod/warp_RenderPipeline.cs +++ b/trunk/managed/Warp3Dmod/warp_RenderPipeline.cs @@ -1,24 +1,22 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Warp3D { /// /// Summary description for warp_RenderPipeline. /// - public class warp_RenderPipeline: IDisposable + public class warp_RenderPipeline { public warp_Screen screen; - warp_Scene scene; + readonly warp_Scene scene; public warp_Lightmap lightmap; public bool useId = false; warp_Rasterizer rasterizer; - - ArrayList transparentQueue = new ArrayList(16384); - int zFar = int.MaxValue; public int[] zBuffer; @@ -34,7 +32,7 @@ namespace Warp3D public void buildLightMap() { - if(lightmap == null) + if (lightmap == null) { lightmap = new warp_Lightmap(scene); } @@ -51,9 +49,8 @@ namespace Warp3D rasterizer.rebuildReferences(this); warp_Math.clearBuffer(zBuffer, zFar); - //System.Array.Copy(screen.zBuffer,0,zBuffer,0,zBuffer.Length); - if(scene.environment.background != null) + if (scene.environment.background is not null) { screen.drawBackground(scene.environment.background, 0, 0, screen.width, screen.height); } @@ -64,17 +61,17 @@ namespace Warp3D cam.setScreensize(screen.width, screen.height); scene.prepareForRendering(); - emptyQueues(); // Project warp_Matrix m = warp_Matrix.multiply(cam.getMatrix(), scene.matrix); warp_Matrix nm = warp_Matrix.multiply(cam.getNormalMatrix(), scene.normalmatrix); - warp_Matrix vertexProjection, normalProjection; + warp_Matrix vertexProjection = new warp_Matrix(); + warp_Matrix normalProjection = new warp_Matrix(); warp_Object obj; warp_Triangle t; warp_Vertex v; warp_Material objmaterial; - const double log2inv = 1.4426950408889634073599246810019; + int w = screen.width; int h = screen.height; int minx; @@ -82,55 +79,53 @@ namespace Warp3D int maxx; int maxy; - for(int id = 0 ; id < scene.objects; ++id) + List transparentQueue = new List(4 * scene.wobject.Length); + + for (int id = 0; id < scene.wobject.Length; ++id) { obj = scene.wobject[id]; + if (!obj.visible) + continue; objmaterial = obj.material; - if(objmaterial == null) + if (objmaterial is null) continue; - if(!obj.visible) - continue; - if(objmaterial.opaque && objmaterial.reflectivity == 0) + if (objmaterial.opaque && objmaterial.reflectivity == 0) continue; - vertexProjection = obj.matrix.getClone(); - normalProjection = obj.normalmatrix.getClone(); - vertexProjection.transform(m); - normalProjection.transform(nm); + vertexProjection.SetFromtransform(obj.matrix, m); + normalProjection.SetFromtransform(obj.normalmatrix, nm); + minx = int.MaxValue; miny = int.MaxValue; maxx = int.MinValue; maxy = int.MinValue; - for(int i = 0; i < obj.vertices; ++i) + for (int i = 0; i < obj.vertexData.Count; ++i) { - v = obj.fastvertex[i]; + v = obj.vertexData[i]; v.project(vertexProjection, normalProjection, cam); v.clipFrustrum(w, h); - if(minx > v.x) + if (minx > v.x) minx = v.x; - if(maxx < v.x) + if (maxx < v.x) maxx = v.x; - if(miny > v.y) + if (miny > v.y) miny = v.y; - if(maxy < v.y) + if (maxy < v.y) maxy = v.y; } - maxx -= minx; - maxy -= miny; - if(maxy > maxx) - maxx = maxy + 1; - else - maxx++; + maxx = Math.Abs(maxx - minx); + maxy = Math.Abs(maxy - miny); + if (maxy > maxx) + maxx = maxy; - obj.projectedmaxMips = (int)Math.Ceiling((Math.Log(maxx) * log2inv)); - obj.cacheMaterialData(); + obj.projectedmaxMips = System.Numerics.BitOperations.Log2((uint)maxx + 1); if (objmaterial.opaque) { - rasterizer.loadFastMaterial(obj); - for (int i = 0; i < obj.triangles; ++i) + rasterizer.loadMaterial(obj); + for (int i = 0; i < obj.triangleData.Count; ++i) { - t = obj.fasttriangle[i]; + t = obj.triangleData[i]; t.project(normalProjection); if (t.clipFrustrum(w, h)) rasterizer.render(t); @@ -138,9 +133,9 @@ namespace Warp3D } else { - for (int i = 0; i < obj.triangles; ++i) + for (int i = 0; i < obj.triangleData.Count; ++i) { - t = obj.fasttriangle[i]; + t = obj.triangleData[i]; t.project(normalProjection); if (t.clipFrustrum(w, h)) transparentQueue.Add(t); @@ -149,122 +144,27 @@ namespace Warp3D } //screen.lockImage(); - - warp_Triangle[] tri; obj = null; - tri = getTransparentQueue(); - if(tri != null) + if (transparentQueue.Count > 0) { - transparentQueue.Clear(); - for (int i = 0; i < tri.GetLength(0); i++) + transparentQueue.Sort(CompareTrisDistance); + for (int i = 0; i < transparentQueue.Count; i++) { - if(obj != tri[i].parent) + if (obj != transparentQueue[i].parent) { - obj = tri[i].parent; - rasterizer.loadFastMaterial(obj); + obj = transparentQueue[i].parent; + rasterizer.loadMaterial(obj); } - rasterizer.render(tri[i]); + rasterizer.render(transparentQueue[i]); } } - - //screen.unlockImage(); - } - - private void performResizing() - { - //screen.resize(requestedWidth, requestedHeight); - zBuffer = new int[screen.width * screen.height]; - } - - // Triangle sorting - private void emptyQueues() - { - transparentQueue.Clear(); - } - - private warp_Triangle[] getTransparentQueue() - { - if(transparentQueue.Count == 0) - return null; - - IComparer comp = new tridistZDescCompare(); - transparentQueue.Sort(comp); - warp_Triangle[] tri = new warp_Triangle[transparentQueue.Count]; - int id = 0; - for(int i = 0; i < transparentQueue.Count; ++i) - tri[id++] = (warp_Triangle)transparentQueue[i]; - - //return sortTriangles(tri, 0, tri.GetLength(0) - 1); - return tri; - } - - private warp_Triangle[] sortTriangles(warp_Triangle[] tri, int L, int R) - { - //FIX: Added Index bounds checking. (Was causing random exceptions) - Created by: X - if(L < 0) - L = 0; - if(L > tri.GetLength(0)) - L = tri.GetLength(0); - if(R < 0) - R = 0; - if(R > tri.GetLength(0)) - R = tri.GetLength(0); - // - Created by: X - - float m = (tri[L].distZ + tri[R].distZ) / 2; - int i = L; - int j = R; - warp_Triangle temp; - - do - { - while(tri[i].distZ > m) - i++; - while(tri[j].distZ < m) - j--; - - if(i <= j) - { - temp = tri[i]; - tri[i] = tri[j]; - tri[j] = temp; - i++; - j--; - } - } - while(j >= i); - - if(L < j) - sortTriangles(tri, L, j); - if(R > i) - sortTriangles(tri, i, R); - - return tri; - } - - public void Dispose() - { - if(screen != null) - screen.Dispose(); - screen = null; - zBuffer = null; - rasterizer.clean(); - rasterizer = null; - transparentQueue.Clear(); transparentQueue = null; - } - } - public class tridistZDescCompare : IComparer - { - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - int IComparer.Compare(object x, object y) - { - float a = ((warp_Triangle)x).distZ; - float b = ((warp_Triangle)y).distZ; - if( a == b ) - return 0; - return a < b ? 1 : -1; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + int CompareTrisDistance(warp_Triangle x, warp_Triangle y) + { + return y.minDistZ.CompareTo(x.minDistZ); + } } } } diff --git a/trunk/managed/Warp3Dmod/warp_Scene.cs b/trunk/managed/Warp3Dmod/warp_Scene.cs index f16c88c7..9a4707f7 100644 --- a/trunk/managed/Warp3Dmod/warp_Scene.cs +++ b/trunk/managed/Warp3Dmod/warp_Scene.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Drawing; using System.Timers; @@ -10,33 +11,30 @@ namespace Warp3D /// public class warp_Scene : warp_CoreObject { - public static String version = "1.0.0"; - public static String release = "0010"; + public static string version = "1.0.1"; + public static string release = "0010"; public warp_RenderPipeline renderPipeline; public int width, height; - public warp_Environment environment = new warp_Environment(); + public warp_Environment environment = new(); public warp_Camera defaultCamera = warp_Camera.FRONT(); public warp_Object[] wobject; public warp_Light[] light; - public int objects = 0; - public int lights = 0; - private bool objectsNeedRebuild = true; private bool lightsNeedRebuild = true; protected bool preparedForRendering = false; - public warp_Vector normalizedOffset = new warp_Vector(0f, 0f, 0f); + public warp_Vector normalizedOffset = new(0f, 0f, 0f); public float normalizedScale = 1f; - public Hashtable objectData = new Hashtable(); - public Hashtable lightData = new Hashtable(); - public Hashtable materialData = new Hashtable(); - public Hashtable cameraData = new Hashtable(); + public Dictionary objectData = new(); + public Dictionary lightData = new(); + public Dictionary materialData = new(); + public Dictionary cameraData = new(); public warp_Scene() { @@ -52,16 +50,13 @@ namespace Warp3D public void destroy() { - objects = objectData.Count; foreach (warp_Object o in objectData.Values) o.destroy(); - objectData.Clear(); - lightData.Clear(); - materialData.Clear(); - cameraData.Clear(); - if (renderPipeline != null) - renderPipeline.Dispose(); + objectData = null; + lightData = null; + materialData = null; + cameraData = null; renderPipeline = null; environment = null; defaultCamera = null; @@ -70,8 +65,7 @@ namespace Warp3D public void removeAllObjects() { - - objectData = new Hashtable(); + objectData = new Dictionary(); objectsNeedRebuild = true; rebuild(); } @@ -82,54 +76,53 @@ namespace Warp3D { objectsNeedRebuild = false; - objects = objectData.Count; - wobject = new warp_Object[objects]; - IDictionaryEnumerator enumerator = objectData.GetEnumerator(); + wobject = new warp_Object[objectData.Count]; - for (int i = 0; i < objects; ++i) + int i = 0; + foreach (warp_Object o in objectData.Values) { - enumerator.MoveNext(); - wobject[i] = (warp_Object)enumerator.Value; - + wobject[i] = o; wobject[i].id = i; wobject[i].rebuild(); + i++; } } if (lightsNeedRebuild) { lightsNeedRebuild = false; - lights = lightData.Count; - light = new warp_Light[lights]; - IDictionaryEnumerator enumerator = lightData.GetEnumerator(); - for (int i = lights - 1; i >= 0; i--) - { - enumerator.MoveNext(); - light[i] = (warp_Light)enumerator.Value; - - } + light = new warp_Light[lightData.Count]; + lightData.Values.CopyTo(light, 0); } } - public warp_Object sceneobject(String key) + public warp_Object sceneobject(string key) { - return (warp_Object)objectData[key]; + if (objectData.TryGetValue(key, out warp_Object obj)) + return obj; + return null; + } + public bool TryGetSceneObject(string key, out warp_Object obj) + { + return objectData.TryGetValue(key, out obj); } - public warp_Material material(String key) + public bool TryGetMaterial(string key, out warp_Material material) { - return (warp_Material)materialData[key]; + return materialData.TryGetValue(key, out material); } - public warp_Camera camera(String key) + public warp_Camera camera(string key) { - return (warp_Camera)cameraData[key]; + if (cameraData.TryGetValue(key, out warp_Camera camera)) + return camera; + return null; } - public void addObject(String key, warp_Object obj) + public void addObject(string key, warp_Object obj) { obj.name = key; - objectData.Add(key, obj); + objectData[key] = obj; obj.parent = this; objectsNeedRebuild = true; preparedForRendering = false; @@ -142,19 +135,19 @@ namespace Warp3D preparedForRendering = false; } - public void addMaterial(String key, warp_Material m) + public void addMaterial(string key, warp_Material m) { - materialData.Add(key, m); + materialData[key] = m; } - public void removeMaterial(String key) + public void removeMaterial(string key) { materialData.Remove(key); } public void addCamera(String key, warp_Camera c) { - cameraData.Add(key, c); + cameraData[key] = c; } public void removeCamera(String key) @@ -162,9 +155,9 @@ namespace Warp3D cameraData.Remove(key); } - public void addLight(String key, warp_Light l) + public void addLight(string key, warp_Light l) { - lightData.Add(key, l); + lightData[key] = l; lightsNeedRebuild = true; } @@ -213,14 +206,16 @@ namespace Warp3D public int countVertices() { int counter = 0; - for (int i = 0; i < objects; i++) counter += wobject[i].vertices; + for (int i = 0; i < wobject.Length; i++) + counter += wobject[i].vertexData.Count; return counter; } public int countTriangles() { int counter = 0; - for (int i = 0; i < objects; i++) counter += wobject[i].triangles; + for (int i = 0; i < wobject.Length; i++) + counter += wobject[i].triangleData.Count; return counter; } @@ -229,46 +224,30 @@ namespace Warp3D objectsNeedRebuild = true; rebuild(); - warp_Vector min, max, tempmax, tempmin; - if (objects == 0) - { + if (wobject.Length == 0) return; - } matrix = new warp_Matrix(); normalmatrix = new warp_Matrix(); - max = wobject[0].maximum(); - min = wobject[0].maximum(); - - for (int i = 0; i < objects; i++) + warp_Vector min = wobject[0].fastMin; + warp_Vector max = wobject[0].fastMax; + for (int i = 0; i < wobject.Length; i++) { - tempmax = wobject[i].maximum(); - tempmin = wobject[i].maximum(); + warp_Vector tempmin = wobject[i].fastMin; + warp_Vector tempmax = wobject[i].fastMax; if (tempmax.x > max.x) - { max.x = tempmax.x; - } if (tempmax.y > max.y) - { max.y = tempmax.y; - } if (tempmax.z > max.z) - { max.z = tempmax.z; - } if (tempmin.x < min.x) - { min.x = tempmin.x; - } if (tempmin.y < min.y) - { min.y = tempmin.y; - } if (tempmin.z < min.z) - { min.z = tempmin.z; - } } float xdist = max.x - min.x; float ydist = max.y - min.y; @@ -289,7 +268,7 @@ namespace Warp3D public float EstimateBoxProjectedArea(warp_Vector pos, warp_Vector size, warp_Matrix rotation) { - warp_Matrix om = new warp_Matrix(); + warp_Matrix om = new(); om.scale(size.x, size.y, size.z); om.transform(rotation); @@ -316,14 +295,12 @@ namespace Warp3D warp_Matrix m = warp_Matrix.multiply(defaultCamera.getMatrix(), matrix); om.transform(m); - float zmin; side = new warp_Vector(-1f, -1f, -1f); v = side.transform(om); xmin = v.x; xmax = xmin; ymin = v.y; ymax = ymin; - zmin = v.z; side.x = 1f; v = side.transform(om); @@ -335,8 +312,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; side.x = -1f; side.y = 1f; @@ -349,8 +324,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; side.x = 1f; v = side.transform(om); @@ -362,8 +335,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; side.x = -1f; side.y = -1f; @@ -377,8 +348,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; side.x = 1f; v = side.transform(om); @@ -390,8 +359,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; side.x = -1f; side.y = 1f; @@ -404,8 +371,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; side.x = 1f; v = side.transform(om); @@ -417,9 +382,6 @@ namespace Warp3D ymin = v.y; else if (ymax < v.y) ymax = v.y; - if (zmin > v.z) - zmin = v.z; - xmax -= xmin; ymax -= ymin; diff --git a/trunk/managed/Warp3Dmod/warp_Screen.cs b/trunk/managed/Warp3Dmod/warp_Screen.cs index a7864b80..01db82cf 100644 --- a/trunk/managed/Warp3Dmod/warp_Screen.cs +++ b/trunk/managed/Warp3Dmod/warp_Screen.cs @@ -2,20 +2,18 @@ using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; +using static System.Net.Mime.MediaTypeNames; namespace Warp3D { /// /// Summary description for warp_Screen. /// - unsafe public class warp_Screen : IDisposable + unsafe public class warp_Screen { public int width; public int height; - - Bitmap image = null; public int[] pixels; - private GCHandle handle; public warp_Screen(int w, int h) { @@ -23,11 +21,6 @@ namespace Warp3D height = h; pixels = new int[w * h]; - - handle = GCHandle.Alloc(pixels, GCHandleType.Pinned); - IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0); - - image = new Bitmap(w, h, w * 4, PixelFormat.Format32bppPArgb, pointer); } public void clear(int c) @@ -47,12 +40,18 @@ namespace Warp3D public Bitmap getImage() { - return new Bitmap(image); + GCHandle handle = GCHandle.Alloc(pixels, GCHandleType.Pinned); + IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0); + Bitmap image = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, pointer); + Bitmap ret = new Bitmap(image); //duhhhh + image.Dispose(); + handle.Free(); + return ret; } private unsafe void draw(int width, int height, warp_Texture texture, int posx, int posy, int xsize, int ysize) { - if (texture == null) + if (texture is null) { return; } @@ -74,7 +73,7 @@ namespace Warp3D xBase = warp_Math.crop(xBase, 0, width); yBase = warp_Math.crop(yBase, 0, height); - fixed(int* px = pixels, txp = texture.pixel) + fixed (int* px = pixels, txp = texture.pixel) { ty = tyBase; for (int j = yBase; j < yend; j++) @@ -99,7 +98,7 @@ namespace Warp3D private void add(int width, int height, warp_Texture texture, int posx, int posy, int xsize, int ysize) { - if (texture == null) + if (texture is null) { return; } @@ -138,16 +137,5 @@ namespace Warp3D } } } - - public void Dispose() - { - if (image != null) - { - handle.Free(); - image.Dispose(); - image = null; - pixels = null; - } - } } } diff --git a/trunk/managed/Warp3Dmod/warp_Texture.cs b/trunk/managed/Warp3Dmod/warp_Texture.cs index 77515134..53ad8b7e 100644 --- a/trunk/managed/Warp3Dmod/warp_Texture.cs +++ b/trunk/managed/Warp3Dmod/warp_Texture.cs @@ -1,7 +1,11 @@ using System; using System.Drawing; using System.Drawing.Imaging; +using System.Globalization; using System.Net; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Xml.Linq; namespace Warp3D { @@ -16,7 +20,7 @@ namespace Warp3D public int bitWidth; public int bitHeight; public int[] pixel; - public String path = null; + public string path = null; public int averageColor; public bool hasAlpha; public bool opaque; @@ -38,59 +42,66 @@ namespace Warp3D cls(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Texture(int w, int h, int[] data) { height = h; width = w; pixel = new int[width * height]; - System.Array.Copy(data, pixel, pixel.Length); + Array.Copy(data, pixel, pixel.Length); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Texture(Bitmap map, int maxBitSize = -1) { loadTexture(map, maxBitSize); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Texture(string path, int maxBitSize = -1) { - using(Bitmap map = new Bitmap(path, false)) - loadTexture(map, maxBitSize); + using Bitmap map = new(path, false); + loadTexture(map, maxBitSize); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void resize() { - double log2inv = 1 / Math.Log(2); + int w = (int)Math.Pow(2, bitWidth = (int)Math.Ceiling(MathF.Log2(width))); + int h = (int)Math.Pow(2, bitHeight = (int)Math.Ceiling(MathF.Log2(height))); - int w = (int)Math.Pow(2, bitWidth = (int)Math.Ceiling((Math.Log(width) * log2inv))); - int h = (int)Math.Pow(2, bitHeight = (int)Math.Ceiling((Math.Log(height) * log2inv))); - - if(!(w == width && h == height)) + if (w != width || h != height) resize(w, h); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void resize(int w, int h) { setSize(w, h); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void cls() { warp_Math.clearBuffer(pixel, 0); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Texture toAverage() { - for(int i = width * height - 1; i >= 0; i--) - pixel[i] = warp_Color.getAverage(pixel[i]); + ref int rpixel = ref MemoryMarshal.GetArrayDataReference(pixel); + for (int i = 0; i < pixel.Length; i++) + Unsafe.As(ref Unsafe.Add(ref rpixel, i)) = warp_Color.getAverage(Unsafe.As(ref Unsafe.Add(ref rpixel, i))); return this; } public warp_Texture toGray() { - for(int i = width * height - 1; i >= 0; i--) - pixel[i] = warp_Color.getGray(pixel[i]); + ref int rpixel = ref MemoryMarshal.GetArrayDataReference(pixel); + for (int i = 0; i < pixel.Length; i++) + Unsafe.As(ref Unsafe.Add(ref rpixel, i)) = warp_Color.getGray(Unsafe.As(ref Unsafe.Add(ref rpixel, i))); return this; } @@ -98,10 +109,11 @@ namespace Warp3D public warp_Texture valToGray() { int intensity; - for(int i = width * height - 1; i >= 0; i--) + ref int rpixel = ref MemoryMarshal.GetArrayDataReference(pixel); + for (int i = 0; i < pixel.Length; i++) { - intensity = warp_Math.crop(pixel[i], 0, 255); - pixel[i] = warp_Color.getColor(intensity, intensity, intensity); + intensity = warp_Math.crop(Unsafe.As(ref Unsafe.Add(ref rpixel, i)), 0, 255); + Unsafe.As(ref Unsafe.Add(ref rpixel, i)) = warp_Color.getColor(intensity, intensity, intensity); } return this; @@ -110,8 +122,10 @@ namespace Warp3D public warp_Texture colorize(int[] pal) { int range = pal.GetLength(0) - 1; - for(int i = width * height - 1; i >= 0; i--) - pixel[i] = pal[warp_Math.crop(pixel[i], 0, range)]; + ref int rpixel = ref MemoryMarshal.GetArrayDataReference(pixel); + for (int i = 0; i < pixel.Length; i++) + Unsafe.As(ref Unsafe.Add(ref rpixel, i)) = + pal[warp_Math.crop(Unsafe.As(ref Unsafe.Add(ref rpixel, i)), 0, range)]; return this; } @@ -126,30 +140,23 @@ namespace Warp3D height = map.Height; PixelFormat format = pmap.PixelFormat; - PixelFormat targetformat; - - switch(format) + PixelFormat targetformat = format switch { - case PixelFormat.Format16bppArgb1555: - case PixelFormat.Format32bppArgb: - case PixelFormat.Format32bppPArgb: - case PixelFormat.Format64bppArgb: - case PixelFormat.Format64bppPArgb: - targetformat = PixelFormat.Format32bppArgb; - break; - default: - targetformat = PixelFormat.Format24bppRgb; - break; - } + PixelFormat.Format16bppArgb1555 => PixelFormat.Format32bppArgb, + PixelFormat.Format32bppArgb => PixelFormat.Format32bppArgb, + PixelFormat.Format32bppPArgb => PixelFormat.Format32bppArgb, + PixelFormat.Format64bppArgb => PixelFormat.Format32bppArgb, + PixelFormat.Format64bppPArgb => PixelFormat.Format32bppArgb, + _ => PixelFormat.Format24bppRgb + }; - // make sure sizes are power of 2 - const double log2inv = 1.4426950408889634073599246810019; - bitWidth = (int)Math.Ceiling((Math.Log(width) * log2inv)); - bitHeight = (int)Math.Ceiling((Math.Log(height) * log2inv)); + // make sure sizes are power of 2 + bitWidth = (int)MathF.Ceiling(MathF.Log2(width)); + bitHeight = (int)MathF.Ceiling(MathF.Log2(height)); - if(maxBitSize > 3) + if (maxBitSize > 3) { - if(bitWidth > bitHeight) + if (bitWidth > bitHeight) { if (bitWidth > maxBitSize) { @@ -173,10 +180,10 @@ namespace Warp3D } } - int w = (int)Math.Pow(2, bitWidth); - int h = (int)Math.Pow(2, bitHeight); + int w = (int)MathF.Pow(2, bitWidth); + int h = (int)MathF.Pow(2, bitHeight); - if(width != w || height != h || format != targetformat) + if (width != w || height != h || format != targetformat) { map = ResizeBitmap(pmap, w, h, targetformat); width = w; @@ -193,7 +200,7 @@ namespace Warp3D byte green; byte red; byte alpha; - + pixel = new int[width * height]; hasAlpha = targetformat == PixelFormat.Format32bppArgb; @@ -202,26 +209,26 @@ namespace Warp3D unsafe { byte* p = (byte*)bmData.Scan0; - fixed(int* px = pixel) + fixed (int* px = pixel) { int nPixel = 0; - if(hasAlpha) + if (hasAlpha) { int nOffset = bmData.Stride - width * 4; - for(int i = 0; i < height; i++) + for (int i = 0; i < height; i++) { - for(int j = 0; j < width; j++) + for (int j = 0; j < width; j++) { blue = *(p++); - avgB += blue; + avgB += blue; green = *(p++); - avgG += green; + avgG += green; red = *(p++); avgR += red; alpha = *(p++); avgA += alpha; - if(opaque && alpha != 0xff) + if (opaque && alpha != 0xff) opaque = false; px[nPixel++] = alpha << 24 | red << 16 | green << 8 | blue; } @@ -231,9 +238,9 @@ namespace Warp3D else { int nOffset = bmData.Stride - width * 3; - for(int i = 0; i < height; i++) + for (int i = 0; i < height; i++) { - for(int j = 0; j < width; j++) + for (int j = 0; j < width; j++) { blue = *(p++); avgB += blue; @@ -251,7 +258,7 @@ namespace Warp3D } map.UnlockBits(bmData); - if(disposemap) + if (disposemap) map.Dispose(); ulong np = (ulong)(width * height); @@ -259,16 +266,16 @@ namespace Warp3D blue = (byte)(avgB / np); green = (byte)(avgG / np); red = (byte)(avgR / np); - if(hasAlpha) + if (hasAlpha) { - alpha= (byte)(avgA / np); + alpha = (byte)(avgA / np); averageColor = alpha << 24 | red << 16 | green << 8 | blue; } else averageColor = ALPHA | red << 16 | green << 8 | blue; maxmips = bitHeight; - if(maxmips > bitWidth) + if (maxmips > bitWidth) maxmips = bitWidth; maxmips -= 3; // less 1x1 2x2 and max @@ -285,51 +292,33 @@ namespace Warp3D mipsBitHeight = new int[maxmips + 1]; mipstw = new int[maxmips + 1]; mipsth = new int[maxmips + 1]; - + int w = width; int h = height; int bw = bitWidth; int bh = bitHeight; int[] src; - int[] dst = null; - for(int n = maxmips; n >= 0; --n) + int[] dst = mips[maxmips + 1]; + for (int n = maxmips; n >= 0; --n) { - src = mips[n + 1]; - if(w > 1) - { - w >>= 1; - --bw; - if(h > 1) - { - h >>= 1; - --bh; - dst = dec2XY(src, w, h); - } - else - dst = dec2X(src, w, h); - } - else - { - if(h > 1) - { - h >>= 1; - --bh; - dst = dec2Y(src, w, h); - } - } + src = dst; + w >>= 1; + h >>= 1; + dst = dec2XY(src, w, h); mips[n] = dst; - mipsBitWidth[n] = bw; - mipsBitHeight[n] = bh; + mipsBitWidth[n] = --bw; + mipsBitHeight[n] = --bh; mipstw[n] = w; mipsth[n] = h; } hasmips = true; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe int[] dec2XY(int[] src, int w, int h) { + /* int[] dst = new int[w * h]; int sw = 2 * w; int stot = 2 * h * sw; @@ -341,9 +330,9 @@ namespace Warp3D int dx = 0; fixed (int* s = src, d = dst) { - for (int sx = 0 ; sx < stot; sx += sw, sxl2 += sw) + for (int sx = 0; sx < stot; sx += sw, sxl2 += sw) { - for(int i = 0; i < w ; i++, sx += 2, sxl2 += 2) + for (int i = 0; i < w; i++, sx += 2, sxl2 += 2) { c1 = s[sx]; c2 = s[sx + 1]; @@ -354,48 +343,37 @@ namespace Warp3D } } return dst; - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private int[] dec2X(int[] src, int w, int h) - { - int dstsize = w * h; - int[] dst = new int[dstsize]; - int c1; - int c2; - int sx = 0; - - for(int dx = 0 ; dx < dstsize; ++dx) - { - c1 = src[sx]; - c2 = src[sx + 1]; - - dst[dx] = warp_Color.avg2c(c1, c2); - sx += 2; - } - - return dst; - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private unsafe int[] dec2Y(int[] src, int w, int h) - { + */ int[] dst = new int[w * h]; - int stot = 2 * h * w; - int c1; - int c2; - int sxl2 = w; - int dx = 0; - fixed (int* s = src, d = dst) + int sw = 8 * w; // 4 bytes * 2 * w + + fixed (int* si = src, di = dst) { - for (int sx = 0 ; sx < stot; sx += w, sxl2 += w) + byte* d = (byte*)di; + byte* s = (byte*)si; + byte* s2 = s + sw; + for (int y = 0; y < h; y++) { - for(int i = 0; i < w ; i++, ++sx, ++sxl2) + for (int x = 0; x < w; x++) { - c1 = s[sx]; - c2 = s[sxl2]; - d[dx++] = warp_Color.avg2c(c1, c2); + int t = s[0] + s[4] + s2[0] + s2[4] + 2; + d[0] = (byte)(t >> 2); + + t = s[1] + s[5] + s2[1] + s2[5] + 2; + d[1] = (byte)(t >> 2); + + t = s[2] + s[6] + s2[2] + s2[6] + 2; + d[2] = (byte)(t >> 2); + + t = s[3] + s[7] + s2[3] + s2[7] + 2; + d[3] = (byte)(t >> 2); + + d += 4; + s += 8; + s2 += 8; } + s += sw; + s2 += sw; } } return dst; @@ -405,16 +383,16 @@ namespace Warp3D { int offset = w * h; int offset2; - if(w * h != 0) + if (w * h != 0) { int[] newpixels = new int[w * h]; - fixed(int* np = newpixels, p = pixel) + fixed (int* np = newpixels, p = pixel) { - for(int j = h - 1; j >= 0; j--) + for (int j = h - 1; j >= 0; j--) { offset -= w; offset2 = (j * height / h) * width; - for(int i = w - 1; i >= 0; i--) + for (int i = w - 1; i >= 0; i--) np[i + offset] = p[(i * width / w) + offset2]; } } @@ -424,7 +402,7 @@ namespace Warp3D } } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool inrange(int a, int b, int c) { return (a >= b) & (a < c); @@ -432,16 +410,16 @@ namespace Warp3D public warp_Texture getClone() { - warp_Texture t = new warp_Texture(width, height); + warp_Texture t = new(width, height); warp_Math.copyBuffer(pixel, t.pixel); return t; } public static Bitmap ResizeBitmap(Image image, int width, int height, PixelFormat format) { - Bitmap result = new Bitmap(width, height, format); + Bitmap result = new(width, height, format); - using (ImageAttributes atrib = new ImageAttributes()) + using (ImageAttributes atrib = new()) using (Graphics graphics = Graphics.FromImage(result)) { atrib.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY); @@ -451,7 +429,7 @@ namespace Warp3D graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None; - graphics.DrawImage(image,new Rectangle(0,0, result.Width, result.Height), + graphics.DrawImage(image, new Rectangle(0, 0, result.Width, result.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, atrib); } diff --git a/trunk/managed/Warp3Dmod/warp_TextureFactory.cs b/trunk/managed/Warp3Dmod/warp_TextureFactory.cs index ce1c9244..b700012f 100644 --- a/trunk/managed/Warp3Dmod/warp_TextureFactory.cs +++ b/trunk/managed/Warp3Dmod/warp_TextureFactory.cs @@ -2,231 +2,231 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_TextureFactory. - /// - public class warp_TextureFactory - { - public static float pi = 3.1415926535f; - public static float deg2rad = pi / 180; - private static float[,] noiseBuffer; - private static bool noiseBufferInitialized = false; - public static int ALPHA = unchecked((int)0xFF000000); // alpha mask + /// + /// Summary description for warp_TextureFactory. + /// + public class warp_TextureFactory + { + public static float pi = 3.1415926535f; + public static float deg2rad = pi / 180; + private static float[,] noiseBuffer; + private static bool noiseBufferInitialized = false; + public static int ALPHA = unchecked((int)0xFF000000); // alpha mask - private warp_TextureFactory() - { - } + private warp_TextureFactory() + { + } - public static warp_Texture SKY(int w, int h, float density) - { - int[] colors = new int[2]; - colors[0] = 0x003399; - colors[1] = 0xFFFFFF; - return PERLIN(w, h, 0.5f, 2.8f * density, 8, 1024).colorize(warp_Color.makeGradient(colors, 1024)); - } + public static warp_Texture SKY(int w, int h, float density) + { + int[] colors = new int[2]; + colors[0] = 0x003399; + colors[1] = 0xFFFFFF; + return PERLIN(w, h, 0.5f, 2.8f * density, 8, 1024).colorize(warp_Color.makeGradient(colors, 1024)); + } - public static warp_Texture MARBLE(int w, int h, float density) - { - int[] colors = new int[3]; - colors[0] = 0x111111; - colors[1] = 0x696070; - colors[2] = 0xFFFFFF; - return WAVE(w, h, 0.5f, 0.64f * density, 6, 1024).colorize(warp_Color.makeGradient(colors, 1024)); - } + public static warp_Texture MARBLE(int w, int h, float density) + { + int[] colors = new int[3]; + colors[0] = 0x111111; + colors[1] = 0x696070; + colors[2] = 0xFFFFFF; + return WAVE(w, h, 0.5f, 0.64f * density, 6, 1024).colorize(warp_Color.makeGradient(colors, 1024)); + } - public static warp_Texture WOOD(int w, int h, float density) - { - int[] colors = new int[3]; - colors[0] = 0x332211; - colors[1] = 0x523121; - colors[2] = 0x996633; + public static warp_Texture WOOD(int w, int h, float density) + { + int[] colors = new int[3]; + colors[0] = 0x332211; + colors[1] = 0x523121; + colors[2] = 0x996633; - return GRAIN(w, h, 0.5f, 3f * density, 3, 8, 1024).colorize(warp_Color.makeGradient(colors, 1024)); - } + return GRAIN(w, h, 0.5f, 3f * density, 3, 8, 1024).colorize(warp_Color.makeGradient(colors, 1024)); + } - public static warp_Texture PERLIN(int w, int h, float persistency, - float density, int samples, int scale) - { - initNoiseBuffer(); - warp_Texture t = new warp_Texture(w, h); - int pos = 0; - float wavelength = (float) ( (w > h) ? w : h) / density; + public static warp_Texture PERLIN(int w, int h, float persistency, + float density, int samples, int scale) + { + initNoiseBuffer(); + warp_Texture t = new(w, h); + int pos = 0; + float wavelength = (float)((w > h) ? w : h) / density; - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - t.pixel[pos++] = (int) ( (float) scale * - perlin2d(x, y, wavelength, persistency, - samples)); - } - } - return t; - } + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + t.pixel[pos++] = (int)((float)scale * + perlin2d(x, y, wavelength, persistency, + samples)); + } + } + return t; + } - public static warp_Texture WAVE(int w, int h, float persistency, - float density, int samples, int scale) - { - initNoiseBuffer(); - warp_Texture t = new warp_Texture(w, h); - int pos = 0; - float wavelength = (float) ( (w > h) ? w : h) / density; + public static warp_Texture WAVE(int w, int h, float persistency, + float density, int samples, int scale) + { + initNoiseBuffer(); + warp_Texture t = new(w, h); + int pos = 0; + float wavelength = (float)((w > h) ? w : h) / density; - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - t.pixel[pos++] = (int) ( (double) scale * - (Math.Sin(32 * - perlin2d(x, y, wavelength, - persistency, samples)) * 0.5 + 0.5)); - } - } - return t; - } + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + t.pixel[pos++] = (int)(scale * + (MathF.Sin(32f * + perlin2d(x, y, wavelength, + persistency, samples)) * 0.5f + 0.5f)); + } + } + return t; + } - public static warp_Texture GRAIN(int w, int h, float persistency, - float density, int samples, int levels, - int scale) - // TIP: For wooden textures - { - initNoiseBuffer(); - warp_Texture t = new warp_Texture(w, h); - int pos = 0; - float wavelength = (float) ( (w > h) ? w : h) / density; - float perlin; + public static warp_Texture GRAIN(int w, int h, float persistency, + float density, int samples, int levels, + int scale) + // TIP: For wooden textures + { + initNoiseBuffer(); + warp_Texture t = new(w, h); + int pos = 0; + float wavelength = (float)((w > h) ? w : h) / density; + float perlin; - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - perlin = (float) levels * - perlin2d(x, y, wavelength, persistency, samples); - t.pixel[pos++] = (int) ( (float) scale * - (perlin - (float) (int) perlin)); - } - } - return t; - } + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + perlin = (float)levels * + perlin2d(x, y, wavelength, persistency, samples); + t.pixel[pos++] = (int)((float)scale * + (perlin - (float)(int)perlin)); + } + } + return t; + } - // Perlin noise functions + // Perlin noise functions - private static float perlin2d(float x, float y, float wavelength, - float persistence, int samples) - { - float sum = 0; - float freq = 1f / wavelength; - float amp = persistence; - float range = 0; + private static float perlin2d(float x, float y, float wavelength, + float persistence, int samples) + { + float sum = 0; + float freq = 1f / wavelength; + float amp = persistence; + float range = 0; - for (int i = 0; i < samples; i++) - { - sum += amp * interpolatedNoise(x * freq, y * freq, i); - range += amp; - amp *= persistence; - freq *= 2; - } - return warp_Math.crop(sum / persistence * 0.5f + 0.5f, 0, 1); - } + for (int i = 0; i < samples; i++) + { + sum += amp * interpolatedNoise(x * freq, y * freq, i); + range += amp; + amp *= persistence; + freq *= 2; + } + return warp_Math.crop(sum / persistence * 0.5f + 0.5f, 0, 1); + } - // Helper methods + // Helper methods - private static float interpolatedNoise(float x, float y, int octave) - { - int intx = (int) x; - int inty = (int) y; - float fracx = x - (float) intx; - float fracy = y - (float) inty; + private static float interpolatedNoise(float x, float y, int octave) + { + int intx = (int)x; + int inty = (int)y; + float fracx = x - (float)intx; + float fracy = y - (float)inty; - float i1 = warp_Math.interpolate(noise(intx, inty, octave), - noise(intx + 1, inty, octave), fracx); - float i2 = warp_Math.interpolate(noise(intx, inty + 1, octave), - noise(intx + 1, inty + 1, octave), - fracx); + float i1 = warp_Math.interpolate(noise(intx, inty, octave), + noise(intx + 1, inty, octave), fracx); + float i2 = warp_Math.interpolate(noise(intx, inty + 1, octave), + noise(intx + 1, inty + 1, octave), + fracx); - return warp_Math.interpolate(i1, i2, fracy); - } + return warp_Math.interpolate(i1, i2, fracy); + } - private static float smoothNoise(int x, int y, int o) - { - return (noise(x - 1, y - 1, o) + noise(x + 1, y - 1, o) + - noise(x - 1, y + 1, o) + noise(x + 1, y + 1, o)) / 16 - + - (noise(x - 1, y, o) + noise(x + 1, y, o) + noise(x, y - 1, o) + - noise(x, y + 1, o)) / 8 - + noise(x, y, o) / 4; - } + private static float smoothNoise(int x, int y, int o) + { + return (noise(x - 1, y - 1, o) + noise(x + 1, y - 1, o) + + noise(x - 1, y + 1, o) + noise(x + 1, y + 1, o)) / 16 + + + (noise(x - 1, y, o) + noise(x + 1, y, o) + noise(x, y - 1, o) + + noise(x, y + 1, o)) / 8 + + noise(x, y, o) / 4; + } - private static float noise(int x, int y, int octave) - { - return noiseBuffer[octave & 3, (x + y * 57) & 8191]; - } + private static float noise(int x, int y, int octave) + { + return noiseBuffer[octave & 3, (x + y * 57) & 8191]; + } - private static float noise(int seed, int octave) - { - int id = octave & 3; - int n = (seed << 13) ^ seed; + private static float noise(int seed, int octave) + { + int id = octave & 3; + int n = (seed << 13) ^ seed; - if (id == 0) - { - return (float) (1f - - ( (n * (n * n * 15731 + 789221) + 1376312589) & - 0x7FFFFFFF) * - 0.000000000931322574615478515625f); - } - if (id == 1) - { - return (float) (1f - - ( (n * (n * n * 12497 + 604727) + 1345679039) & - 0x7FFFFFFF) * - 0.000000000931322574615478515625f); - } - if (id == 2) - { - return (float) (1f - - ( (n * (n * n * 19087 + 659047) + 1345679627) & - 0x7FFFFFFF) * - 0.000000000931322574615478515625f); - } - return (float) (1f - - ( (n * (n * n * 16267 + 694541) + 1345679501) & - 0x7FFFFFFF) * - 0.000000000931322574615478515625f); - } + if (id == 0) + { + return (float)(1f - + ((n * (n * n * 15731 + 789221) + 1376312589) & + 0x7FFFFFFF) * + 0.000000000931322574615478515625f); + } + if (id == 1) + { + return (float)(1f - + ((n * (n * n * 12497 + 604727) + 1345679039) & + 0x7FFFFFFF) * + 0.000000000931322574615478515625f); + } + if (id == 2) + { + return (float)(1f - + ((n * (n * n * 19087 + 659047) + 1345679627) & + 0x7FFFFFFF) * + 0.000000000931322574615478515625f); + } + return (float)(1f - + ((n * (n * n * 16267 + 694541) + 1345679501) & + 0x7FFFFFFF) * + 0.000000000931322574615478515625f); + } - private static void initNoiseBuffer() - { - if (noiseBufferInitialized) - { - return; - } + private static void initNoiseBuffer() + { + if (noiseBufferInitialized) + { + return; + } - noiseBuffer = new float[4,8192]; - for (int octave = 0; octave < 4; octave++) - { - for (int i = 0; i < 8192; i++) - { - noiseBuffer[octave,i] = noise(i, octave); - } - } - noiseBufferInitialized = true; - } - - public static warp_Texture CHECKERBOARD(int w, int h, int cellbits, int oddColor, int evenColor) - { - warp_Texture t = new warp_Texture(w, h); + noiseBuffer = new float[4, 8192]; + for (int octave = 0; octave < 4; octave++) + { + for (int i = 0; i < 8192; i++) + { + noiseBuffer[octave, i] = noise(i, octave); + } + } + noiseBufferInitialized = true; + } - int pos = 0; - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - int c = ( ( (x >> cellbits) + (y >> cellbits)) & 1) == 0 ? evenColor : oddColor; - t.pixel[pos++] = c; - } - } + public static warp_Texture CHECKERBOARD(int w, int h, int cellbits, int oddColor, int evenColor) + { + warp_Texture t = new(w, h); - return t; - } - } + int pos = 0; + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int c = (((x >> cellbits) + (y >> cellbits)) & 1) == 0 ? evenColor : oddColor; + t.pixel[pos++] = c; + } + } + + return t; + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_TextureProjector.cs b/trunk/managed/Warp3Dmod/warp_TextureProjector.cs index 7deb3904..a13a03ed 100644 --- a/trunk/managed/Warp3Dmod/warp_TextureProjector.cs +++ b/trunk/managed/Warp3Dmod/warp_TextureProjector.cs @@ -2,40 +2,40 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_TextureProjector. - /// - public class warp_TextureProjector - { - public static void projectFrontal(warp_Object obj) - { - obj.rebuild(); - warp_Vector min = obj.minimum(); - warp_Vector max = obj.maximum(); - float du = 1 / (max.x - min.x); - float dv = 1 / (max.y - min.y); - - for (int i = 0; i < obj.vertices; i++) - { - obj.fastvertex[i].u = (obj.fastvertex[i].pos.x - min.x) * du; - obj.fastvertex[i].v = 1 - (obj.fastvertex[i].pos.y - min.y) * dv; - } - } + /// + /// Summary description for warp_TextureProjector. + /// + public class warp_TextureProjector + { + public static void projectFrontal(warp_Object obj) + { + obj.rebuild(); + warp_Vector min = obj.fastMin; + warp_Vector max = obj.fastMax; + float du = 1 / (max.x - min.x); + float dv = 1 / (max.y - min.y); - public static void projectTop(warp_Object obj) - { - obj.rebuild(); - warp_Vector min = obj.minimum(); - warp_Vector max = obj.maximum(); - float du = 1 / (max.x - min.x); - float dv = 1 / (max.z - min.z); - for (int i = 0; i < obj.vertices; i++) - { - obj.fastvertex[i].u = (obj.fastvertex[i].pos.x - min.x) * du; - obj.fastvertex[i].v = (obj.fastvertex[i].pos.z - min.z) * dv; - } - } + for (int i = 0; i < obj.vertexData.Count; i++) + { + obj.vertexData[i].u = (obj.vertexData[i].pos.x - min.x) * du; + obj.vertexData[i].v = 1 - (obj.vertexData[i].pos.y - min.y) * dv; + } + } + public static void projectTop(warp_Object obj) + { + obj.rebuild(); + warp_Vector min = obj.fastMin; + warp_Vector max = obj.fastMax; + float du = 1 / (max.x - min.x); + float dv = 1 / (max.z - min.z); + for (int i = 0; i < obj.vertexData.Count; i++) + { + obj.vertexData[i].u = (obj.vertexData[i].pos.x - min.x) * du; + obj.vertexData[i].v = (obj.vertexData[i].pos.z - min.z) * dv; + } + } + /* public static void projectCylindric(warp_Object obj) { obj.rebuild(); @@ -48,6 +48,22 @@ namespace Warp3D obj.fastvertex[i].u = obj.fastvertex[i].pos.theta / (2 * 3.14159265f); obj.fastvertex[i].v = (obj.fastvertex[i].pos.z - min.z) * dz; } - } - } + } + */ + + public static void projectCylindric(warp_Object obj) + { + obj.rebuild(); + const float inv2p = 0.5f / MathF.PI; + warp_Vector min = obj.fastMin; + warp_Vector max = obj.fastMax; + float dz = 1 / (max.z - min.z); + for (int i = 0; i < obj.vertexData.Count; i++) + { + float theta = MathF.Atan2(obj.vertexData[i].pos.x, obj.vertexData[i].pos.y); + obj.vertexData[i].u = theta * inv2p; + obj.vertexData[i].v = (obj.vertexData[i].pos.z - min.z) * dz; + } + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_TextureSettings.cs b/trunk/managed/Warp3Dmod/warp_TextureSettings.cs index 5df54c30..e95a6881 100644 --- a/trunk/managed/Warp3Dmod/warp_TextureSettings.cs +++ b/trunk/managed/Warp3Dmod/warp_TextureSettings.cs @@ -2,32 +2,32 @@ using System; namespace Warp3D { - /// - /// Summary description for warp_TextureSettings. - /// - /// - public class warp_TextureSettings - { - public warp_Texture texture; - public int width; - public int height; - public int type; - public float persistency; - public float density; - public int samples; - public int numColors; - public int[] colors; + /// + /// Summary description for warp_TextureSettings. + /// + /// + public class warp_TextureSettings + { + public warp_Texture texture; + public int width; + public int height; + public int type; + public float persistency; + public float density; + public int samples; + public int numColors; + public int[] colors; - public warp_TextureSettings(warp_Texture tex, int w, int h, int t, float p, float d, int s, int[] c) - { - texture=tex; - width=w; - height=h; - type=t; - persistency=p; - density=d; - samples=s; - colors=c; - } - } + public warp_TextureSettings(warp_Texture tex, int w, int h, int t, float p, float d, int s, int[] c) + { + texture = tex; + width = w; + height = h; + type = t; + persistency = p; + density = d; + samples = s; + colors = c; + } + } } diff --git a/trunk/managed/Warp3Dmod/warp_Triangle.cs b/trunk/managed/Warp3Dmod/warp_Triangle.cs index e6286610..b252c47b 100644 --- a/trunk/managed/Warp3Dmod/warp_Triangle.cs +++ b/trunk/managed/Warp3Dmod/warp_Triangle.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Warp3D { @@ -7,17 +8,16 @@ namespace Warp3D /// public class warp_Triangle { + public const float oneThird = 1.0f / 3f; public warp_Object parent; // the object which obtains this triangle public warp_Vertex p1; // first vertex public warp_Vertex p2; // second vertex public warp_Vertex p3; // third vertex public warp_Vector n; // Normal vector of flat triangle - public warp_Vector rawNorm; // Normal h vector of flat triangle public warp_Vector n2; // Projected Normal vector - private warp_Vector triangleCenter = new warp_Vector(); - public float distZ = 0; + public int minDistZ = 0; public int id = 0; @@ -39,36 +39,41 @@ namespace Warp3D public void project(warp_Matrix normalProjection) { n2 = n.transform(normalProjection); - distZ = getDistZ(); + minDistZ = getMinDistZ(); } public void regenerateNormal() { - rawNorm = warp_Vector.vectorProduct(p1.pos, p2.pos, p3.pos); - n = rawNorm.normalize(); + warp_Vector tmpa = warp_Vector.sub(ref p2.pos, ref p1.pos); + warp_Vector tmpb = warp_Vector.sub(ref p3.pos, ref p1.pos); + n = warp_Vector.vectorProduct(ref tmpa, ref tmpb); + n.normalize(); } public warp_Vertex getMedium() { - float cx = (p1.pos.x + p2.pos.x + p3.pos.x) / 3; - float cy = (p1.pos.y + p2.pos.y + p3.pos.y) / 3; - float cz = (p1.pos.z + p2.pos.z + p3.pos.z) / 3; - float cu = (p1.u + p2.u + p3.u) / 3; - float cv = (p1.v + p2.v + p3.v) / 3; + float cx = (p1.pos.x + p2.pos.x + p3.pos.x) * oneThird; + float cy = (p1.pos.y + p2.pos.y + p3.pos.y) * oneThird; + float cz = (p1.pos.z + p2.pos.z + p3.pos.z) * oneThird; + float cu = (p1.u + p2.u + p3.u) * oneThird; + float cv = (p1.v + p2.v + p3.v) * oneThird; return new warp_Vertex(cx, cy, cz, cu, cv); } public warp_Vector getCenter() { - float cx = (p1.pos.x + p2.pos.x + p3.pos.x) / 3; - float cy = (p1.pos.y + p2.pos.y + p3.pos.y) / 3; - float cz = (p1.pos.z + p2.pos.z + p3.pos.z) / 3; + float cx = (p1.pos.x + p2.pos.x + p3.pos.x) * oneThird; + float cy = (p1.pos.y + p2.pos.y + p3.pos.y) * oneThird; + float cz = (p1.pos.z + p2.pos.z + p3.pos.z) * oneThird; return new warp_Vector(cx, cy, cz); } - public float getDistZ() + public int getMinDistZ() { - return p1.z + p2.z + p3.z; + int m = p1.z; + if (m > p2.z) + m = p2.z; + return m > p3.z ? p3.z : m; } public bool degenerated() diff --git a/trunk/managed/Warp3Dmod/warp_Vector.cs b/trunk/managed/Warp3Dmod/warp_Vector.cs index 7898b3c5..d73155ec 100644 --- a/trunk/managed/Warp3Dmod/warp_Vector.cs +++ b/trunk/managed/Warp3Dmod/warp_Vector.cs @@ -1,14 +1,13 @@ using System; +using System.Runtime.CompilerServices; namespace Warp3D { - public class warp_Vector + public struct warp_Vector { - public float x = 0; //Cartesian (default) - public float y = 0; //Cartesian (default) - public float z = 0; //Cartesian (default),Cylindric - public float r = 0; //Cylindric - public float theta = 0; //Cylindric + public float x = 0; + public float y = 0; + public float z = 0; public warp_Vector() { @@ -20,20 +19,31 @@ namespace Warp3D y = ypos; z = zpos; } - public warp_Vector normalize() + + public static warp_Vector FromXYZ(float xpos, float zpos, float ypos) + { + return new warp_Vector + { + x = xpos, + y = ypos, + z = zpos + }; + } + public void normalize() // Normalizes the vector { - float dist = length(); - if(dist == 0) - return this; - float invdist = 1 / dist; - x *= invdist; - y *= invdist; - z *= invdist; - return this; + float dist = lengthSquare(); + if (dist > 1e-6f) + { + + float invdist = 1f / MathF.Sqrt(dist); + x *= invdist; + y *= invdist; + z *= invdist; + } } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vector reverse() // Reverses the vector { @@ -43,21 +53,33 @@ namespace Warp3D return this; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float length() - // Lenght of this vector { - return (float)Math.Sqrt(x * x + y * y + z * z); + return MathF.Sqrt(x * x + y * y + z * z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float lengthSquare() + { + return (x * x + y * y + z * z); } public warp_Vector transform(warp_Matrix m) // Modifies the vector by matrix m { - float newx = x * m.m00 + y * m.m01 + z * m.m02 + m.m03; - float newy = x * m.m10 + y * m.m11 + z * m.m12 + m.m13; - float newz = x * m.m20 + y * m.m21 + z * m.m22 + m.m23; + return new warp_Vector( + x * m.m00 + y * m.m01 + z * m.m02 + m.m03, + x * m.m10 + y * m.m11 + z * m.m12 + m.m13, + x * m.m20 + y * m.m21 + z * m.m22 + m.m23 + ); + } - return new warp_Vector(newx, newy, newz); + public void transformToXY(warp_Matrix m, out float nx, out float ny) + // Modifies the vector by matrix m + { + nx = x * m.m00 + y * m.m01 + z * m.m02 + m.m03; + ny = x * m.m10 + y * m.m11 + z * m.m12 + m.m13; } public warp_Vector pointtransform(warp_Matrix m) @@ -66,8 +88,8 @@ namespace Warp3D float newx = x * m.m00 + y * m.m01 + z * m.m02 + m.m03; float newy = x * m.m10 + y * m.m11 + z * m.m12 + m.m13; float newz = x * m.m20 + y * m.m21 + z * m.m22 + m.m23; - float w = x * m.m30 + y * m.m31 + z * m.m32 + m.m33; - if(w != 1.0f && w != 0f) + float w = x * m.m30 + y * m.m31 + z * m.m32 + m.m33; + if (w != 1.0f && w != 0f) { w = 1.0f / w; newx *= w; @@ -78,109 +100,58 @@ namespace Warp3D return new warp_Vector(newx, newy, newz); } - public void buildCylindric() - // Builds the cylindric coordinates out of the given cartesian coordinates - { - r = (float)Math.Sqrt(x * x + y * y); - theta = (float)Math.Atan2(x, y); - } - - public void buildCartesian() - // Builds the cartesian coordinates out of the given cylindric coordinates - { - x = r * warp_Math.cos(theta); - y = r * warp_Math.sin(theta); - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector getNormal(warp_Vector a, warp_Vector b) - // returns the normal vector of the plane defined by the two vectors - { - return vectorProduct(a, b).normalize(); - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector getNormal(warp_Vector a, warp_Vector b, warp_Vector c) - // returns the normal vector of the plane defined by the two vectors - { - return vectorProduct(a, b, c).normalize(); - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector vectorProduct(warp_Vector a, warp_Vector b) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static warp_Vector vectorProduct(ref warp_Vector a, ref warp_Vector b) // returns a x b { return new warp_Vector(a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, a.x * b.y - b.x * a.y); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector vectorProduct(warp_Vector a, warp_Vector b, warp_Vector c) - // returns (b-a) x (c-a) - { - return vectorProduct(sub(b, a), sub(c, a)); - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static float vectorsCos(warp_Vector a, warp_Vector b) - // returns the angle between 2 vectors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float vectorsCos(ref warp_Vector a, ref warp_Vector b) { a.normalize(); b.normalize(); return (a.x * b.x + a.y * b.y + a.z * b.z); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static float Dot(warp_Vector a, warp_Vector b) - // returns the angle between 2 vectors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(ref warp_Vector a, ref warp_Vector b) { return (a.x * b.x + a.y * b.y + a.z * b.z); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector add(warp_Vector a, warp_Vector b) - // adds 2 vectors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static warp_Vector add(ref warp_Vector a, ref warp_Vector b) { return new warp_Vector(a.x + b.x, a.y + b.y, a.z + b.z); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector sub(warp_Vector a, warp_Vector b) - // substracts 2 vectors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static warp_Vector sub(ref warp_Vector a, ref warp_Vector b) { return new warp_Vector(a.x - b.x, a.y - b.y, a.z - b.z); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static warp_Vector scale(float f, warp_Vector a) - // substracts 2 vectors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static warp_Vector scale(float f, ref warp_Vector a) { return new warp_Vector(f * a.x, f * a.y, f * a.z); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float len(warp_Vector a) // length of vector { - return (float)Math.Sqrt(a.x * a.x + a.y * a.y + a.z * a.z); + return MathF.Sqrt(a.x * a.x + a.y * a.y + a.z * a.z); } - /* - public static warp_Vector random(float fact) - // returns a random vector - { - return new warp_Vector(fact*warp_Math.random(),fact*warp_Math.random(),fact*warp_Math.random()); - } - - public String toString() - { - return new String ("\r\n"); - } -*/ - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vector getClone() { return new warp_Vector(x, y, z); } + + public static warp_Vector Zero = new(); } } diff --git a/trunk/managed/Warp3Dmod/warp_Vertex.cs b/trunk/managed/Warp3Dmod/warp_Vertex.cs index 91c55d88..193ef014 100644 --- a/trunk/managed/Warp3Dmod/warp_Vertex.cs +++ b/trunk/managed/Warp3Dmod/warp_Vertex.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Warp3D { @@ -9,70 +10,64 @@ namespace Warp3D /// public class warp_Vertex { - public warp_Object parent; + public warp_Object parent = null; - public warp_Vector pos = new warp_Vector(); //(x,y,z) Coordinate of vertex - public warp_Vector n = new warp_Vector(); //Normal Vector at vertex - public float u = 0; // Texture x-coordinate (relative) - public float v = 0; // Texture y-coordinate (relative) - - - public warp_Vector pos2; //Transformed vertex coordinate - public warp_Vector n2; //Transformed normal vector (camera space) - public int x; //Projected x coordinate - public int y; //Projected y coordinate - public int z; //Projected z coordinate for z-Buffer - - public float tx = 0; // Texture x-coordinate (relative) - public float ty = 0; // Texture y-coordinate (relative) - - public int nx = 0; // Normal x-coordinate for envmapping - public int ny = 0; // Normal y-coordinate for envmapping - - public float invZ = 1.0f; - - public bool visible = true; //visibility tag for clipping - public int clipcode = 0; + public warp_Vector pos; //(x,y,z) Coordinate of vertex + public warp_Vector n; //Normal at vertex + public float u; // Texture x-coordinate (relative) + public float v; // Texture y-coordinate (relative) public int id; // Vertex index - //private Vector neighbor=new Vector(); //Neighbor triangles of vertex - private List neighbor = new List(); + // projected data cache + public int x; //x coordinate + public int y; //y coordinate + public int z; //z coordinate for z-Buffer + + public int nx; // Normal x-coordinate for envmapping + public int ny; // Normal y-coordinate for envmapping + + public float invZ; + + public int clipcode; + + private readonly List neighbor = new(); public warp_Vertex() { - pos = new warp_Vector(0f, 0f, 0f); + pos = new(0f, 0f, 0f); } public warp_Vertex(float xpos, float ypos, float zpos) { - pos = new warp_Vector(xpos, ypos, zpos); + pos = new(xpos, ypos, zpos); } public warp_Vertex(float xpos, float ypos, float zpos, float u, float v) { - pos = new warp_Vector(xpos, ypos, zpos); + pos = new(xpos, ypos, zpos); this.u = u; this.v = v; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vertex(warp_Vector ppos) { - pos = ppos.getClone(); + pos = ppos; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vertex(warp_Vector ppos, float u, float v) { - pos = ppos.getClone(); + pos = ppos; this.u = u; this.v = v; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vertex(warp_Vector ppos, warp_Vector norm, float u, float v) { - pos = ppos.getClone(); - n = norm.getClone(); + pos = ppos; + n = norm; this.u = u; this.v = v; } @@ -80,66 +75,62 @@ namespace Warp3D public void project(warp_Matrix vertexProjection, warp_Matrix normalProjection, warp_Camera camera) // Projects this vertex into camera space { - pos2 = pos.transform(vertexProjection); - n2 = n.transform(normalProjection); - - if(pos2.z < 0.001f && pos2.z > -0.0001f) + warp_Vector pos2 = pos.transform(vertexProjection); + if (pos2.z < 0.001f && pos2.z > -0.0001f) pos2.z = 0.001f; - if(camera.isOrthographic) + if (camera.isOrthographic) { x = (int)pos2.x; y = (int)pos2.y; invZ = -1.0f; - tx = -u; - ty = -v; } else { invZ = 1.0f / pos2.z; x = (int)(pos2.x * invZ + camera.halfscreenwidth); y = (int)(pos2.y * invZ + camera.halfscreenheight); - invZ = - invZ; - tx = u * invZ; - ty = v * invZ; + invZ = -invZ; } - z = (int)(65536f * pos2.z); - nx = ((int)(n2.x * 127 + 127)) << 16; - ny = ((int)(n2.y * 127 + 127)) << 16; + + n.transformToXY(normalProjection, out float nxf, out float nyf); + nx = ((int)(nxf * 127 + 127)) << 16; + ny = ((int)(nyf * 127 + 127)) << 16; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void setUV(float u, float v) { this.u = u; this.v = v; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void clipFrustrum(int w, int h) { // View plane clipping clipcode = 0; - if(x < 0) + if (x < 0) clipcode |= 1; - else if(x >= w) + else if (x >= w) clipcode |= 2; - if(y < 0) + if (y < 0) clipcode |= 4; - else if(y >= h) + else if (y >= h) clipcode |= 8; - if(pos2.z < 0) + if (z < 0) clipcode |= 16; - visible = (clipcode == 0); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void registerNeighbor(warp_Triangle triangle) { - if(!neighbor.Contains(triangle)) + if (!neighbor.Contains(triangle)) neighbor.Add(triangle); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void resetNeighbors() { neighbor.Clear(); @@ -150,16 +141,15 @@ namespace Warp3D float nx = 0; float ny = 0; float nz = 0; - for(int i = 0; i < neighbor.Count; ++i) + for (int i = 0; i < neighbor.Count; ++i) { - warp_Triangle tri = neighbor[i]; - warp_Vector wn = tri.rawNorm; - nx += wn.x; - ny += wn.y; - nz += wn.z; + nx += neighbor[i].n.x; + ny += neighbor[i].n.y; + nz += neighbor[i].n.z; } - n = new warp_Vector(nx, ny, nz).normalize(); + n = new warp_Vector(nx, ny, nz); + n.normalize(); } /* @@ -183,42 +173,44 @@ namespace Warp3D n=new warp_Vector(nx,ny,nz).normalize(); } */ - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void scaleTextureCoordinates(float fx, float fy) { u *= fx; v *= fy; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void moveTextureCoordinates(float fx, float fy) { u += fx; v += fy; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public warp_Vertex getClone() { - warp_Vertex newVertex = new warp_Vertex(); - newVertex.pos = pos.getClone(); - newVertex.n = n.getClone(); - newVertex.u = u; - newVertex.v = v; + warp_Vertex newVertex = new() + { + pos = pos, + n = n, + u = u, + v = v + }; return newVertex; } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool equals(warp_Vertex v) { return ((pos.x == v.pos.x) && (pos.y == v.pos.y) && (pos.z == v.pos.z)); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool equals(warp_Vertex v, float tolerance) { - return Math.Abs(warp_Vector.sub(pos, v.pos).length()) < tolerance; + return warp_Vector.sub(ref pos, ref v.pos).lengthSquare() < tolerance * tolerance; } } } diff --git a/trunk/managed/XmlRpc/AssemblyInfo.cs b/trunk/managed/XmlRpc/AssemblyInfo.cs index 6cd8b21a..f05c9aba 100644 --- a/trunk/managed/XmlRpc/AssemblyInfo.cs +++ b/trunk/managed/XmlRpc/AssemblyInfo.cs @@ -1,7 +1,7 @@ using System.Runtime.InteropServices; using System.Reflection; -[assembly: AssemblyVersion("1.10.0.2")] +[assembly: AssemblyVersion("1.10.0.4")] [assembly: AssemblyTitle("XmlRpc Opensim")] [assembly: AssemblyCompany("Ronin Consulting Inc, OpenSimulator")] [assembly: AssemblyDescription("XML RPC client and server")] diff --git a/trunk/managed/XmlRpc/Logger.cs b/trunk/managed/XmlRpc/Logger.cs index beacd6bd..bdd2834e 100644 --- a/trunk/managed/XmlRpc/Logger.cs +++ b/trunk/managed/XmlRpc/Logger.cs @@ -40,8 +40,7 @@ namespace Nwc.XmlRpc ///The LogLevel of your message. static public void WriteEntry(String message,LogLevel level) { - if(Delegate != null) - Delegate(message,level); + Delegate?.Invoke(message, level); } } } diff --git a/trunk/managed/XmlRpc/SimpleHttpRequest.cs b/trunk/managed/XmlRpc/SimpleHttpRequest.cs index b4d39da7..e40d7ef1 100644 --- a/trunk/managed/XmlRpc/SimpleHttpRequest.cs +++ b/trunk/managed/XmlRpc/SimpleHttpRequest.cs @@ -16,9 +16,9 @@ namespace Nwc.XmlRpc private String _filePathFile = null; private String _filePathDir = null; private String __filePath; - private TcpClient _client; - private StreamReader _input; - private StreamWriter _output; + private readonly TcpClient _client; + private readonly StreamReader _input; + private readonly StreamWriter _output; private Hashtable _headers; /// A constructor which accepts the TcpClient. @@ -107,16 +107,16 @@ namespace Nwc.XmlRpc { get { - if(_filePathFile != null) + if(_filePathFile is not null) return _filePathFile; - int i = FilePath.LastIndexOf("/"); + int i = FilePath.LastIndexOf('/'); if(i == -1) return ""; i++; - _filePathFile = FilePath.Substring(i,FilePath.Length - i); + _filePathFile = FilePath[i..]; return _filePathFile; } } @@ -126,16 +126,15 @@ namespace Nwc.XmlRpc { get { - if(_filePathDir != null) + if(_filePathDir is not null) return _filePathDir; - int i = FilePath.LastIndexOf("/"); - + int i = FilePath.LastIndexOf('/'); if(i == -1) return ""; i++; - _filePathDir = FilePath.Substring(0,i); + _filePathDir = FilePath[..i]; return _filePathDir; } } @@ -143,28 +142,38 @@ namespace Nwc.XmlRpc private void GetRequestMethod() { string req = _input.ReadLine(); - if(req == null) + if(req is null) throw new ApplicationException("Void request."); + var sreq = req.AsSpan(); - if(0 == String.Compare("GET ",req.Substring(0,4),true)) + if(sreq.StartsWith("GET ")) + { _httpMethod = "GET"; - else if(0 == String.Compare("POST ",req.Substring(0,5),true)) + sreq = sreq[5..]; + } + else if(sreq.StartsWith("POST ")) + { _httpMethod = "POST"; + sreq = sreq[6..]; + } else throw new InvalidOperationException("Unrecognized method in query: " + req); - req = req.TrimEnd(); - int idx = req.IndexOf(' ') + 1; - if(idx >= req.Length) + sreq = sreq.TrimEnd(); + if(sreq.Length == 0) throw new ApplicationException("What do you want?"); - string page_protocol = req.Substring(idx); - int idx2 = page_protocol.IndexOf(' '); + int idx2 = sreq.IndexOf(' '); if(idx2 == -1) - idx2 = page_protocol.Length; - - _filePath = page_protocol.Substring(0,idx2).Trim(); - _protocol = page_protocol.Substring(idx2).Trim(); + { + _filePath = sreq.ToString(); + _protocol = string.Empty; + } + else + { + _filePath = sreq[..idx2].Trim().ToString(); + _protocol = sreq[idx2..].Trim().ToString(); + } } private void GetRequestHeaders() @@ -188,8 +197,8 @@ namespace Nwc.XmlRpc continue; } - String key = line.Substring(0,idx); - String value = line.Substring(idx + 1); + String key = line[..idx]; + String value = line[(idx + 1)..]; try { diff --git a/trunk/managed/XmlRpc/XmlRpcBoxcarRequest.cs b/trunk/managed/XmlRpc/XmlRpcBoxcarRequest.cs index 5083359c..0d3b9303 100644 --- a/trunk/managed/XmlRpc/XmlRpcBoxcarRequest.cs +++ b/trunk/managed/XmlRpc/XmlRpcBoxcarRequest.cs @@ -43,9 +43,11 @@ namespace Nwc.XmlRpc ArrayList reqArray = new ArrayList(); foreach(XmlRpcRequest request in Requests) { - Hashtable requestEntry = new Hashtable(); - requestEntry.Add(XmlRpcXmlTokens.METHOD_NAME,request.MethodName); - requestEntry.Add(XmlRpcXmlTokens.PARAMS,request.Params); + Hashtable requestEntry = new Hashtable + { + { XmlRpcXmlTokens.METHOD_NAME, request.MethodName }, + { XmlRpcXmlTokens.PARAMS, request.Params } + }; reqArray.Add(requestEntry); } _params.Add(reqArray); diff --git a/trunk/managed/XmlRpc/XmlRpcDeserializer.cs b/trunk/managed/XmlRpc/XmlRpcDeserializer.cs index 44d571e1..414dcc71 100644 --- a/trunk/managed/XmlRpc/XmlRpcDeserializer.cs +++ b/trunk/managed/XmlRpc/XmlRpcDeserializer.cs @@ -21,7 +21,7 @@ namespace Nwc.XmlRpc /// but is designed to be subclassed. public class XmlRpcDeserializer:XmlRpcXmlTokens { - private static DateTimeFormatInfo _dateFormat = new DateTimeFormatInfo(); + private readonly static DateTimeFormatInfo _dateFormat = new(); private object _container; private Stack _containerStack; @@ -58,7 +58,7 @@ namespace Nwc.XmlRpc switch(reader.NodeType) { case XmlNodeType.Element: - if(Logger.Delegate != null) + if(Logger.Delegate is not null) Logger.WriteEntry("START " + reader.Name,LogLevel.Information); switch(reader.Name) { @@ -77,13 +77,13 @@ namespace Nwc.XmlRpc } break; case XmlNodeType.EndElement: - if(Logger.Delegate != null) + if(Logger.Delegate is not null) Logger.WriteEntry("END " + reader.Name,LogLevel.Information); switch(reader.Name) { case BASE64: if(string.IsNullOrEmpty(_text)) - _value = new byte[0]; + _value = Array.Empty(); else _value = Convert.FromBase64String(_text); break; @@ -105,25 +105,19 @@ namespace Nwc.XmlRpc _value = Int32.Parse(_text); break; case DATETIME: -#if __MONO__ - _value = DateParse(_text); -#else _value = DateTime.ParseExact(_text,"F",_dateFormat); -#endif break; case NAME: _name = _text; break; case VALUE: - if(_value == null) - _value = _text; // some kits don't use tag, they just do - - if((_container != null) && (_container is IList)) // in an array? If so add value to it. - ((IList)_container).Add(_value); + _value ??= _text; // some kits don't use tag, they just do + if(_container is IList cnt) // in an array? If so add value to it. + cnt.Add(_value); break; case MEMBER: - if((_container != null) && (_container is IDictionary)) // in an struct? If so add value to it. - ((IDictionary)_container).Add(_name,_value); + if(_container is IDictionary icnt) // in an struct? If so add value to it. + icnt.Add(_name,_value); break; case ARRAY: case STRUCT: @@ -133,7 +127,7 @@ namespace Nwc.XmlRpc } break; case XmlNodeType.Text: - if(Logger.Delegate != null) + if(Logger.Delegate is not null) Logger.WriteEntry("Text " + reader.Value,LogLevel.Information); _text = reader.Value; break; @@ -179,20 +173,6 @@ namespace Nwc.XmlRpc _container = null; _containerStack = new Stack(); } - -#if __MONO__ - private DateTime DateParse(String str) - { - int year = Int32.Parse(str.Substring(0,4)); - int month = Int32.Parse(str.Substring(4,2)); - int day = Int32.Parse(str.Substring(6,2)); - int hour = Int32.Parse(str.Substring(9,2)); - int min = Int32.Parse(str.Substring(12,2)); - int sec = Int32.Parse(str.Substring(15,2)); - return new DateTime(year,month,day,hour,min,sec); - } -#endif - } } diff --git a/trunk/managed/XmlRpc/XmlRpcException.cs b/trunk/managed/XmlRpc/XmlRpcException.cs index 4185f11e..38e11632 100644 --- a/trunk/managed/XmlRpc/XmlRpcException.cs +++ b/trunk/managed/XmlRpc/XmlRpcException.cs @@ -7,7 +7,7 @@ namespace Nwc.XmlRpc /// include a message so this adds the code needed by XML-RPC. public class XmlRpcException:Exception { - private int _code; + private readonly int _code; /// Instantiate an XmlRpcException with a code and message. /// Int faultCode associated with this exception. @@ -38,7 +38,7 @@ namespace Nwc.XmlRpc /// Format the message to include the code. override public String ToString() { - return "Code: " + FaultCode + " Message: " + base.ToString(); + return $"Code: {FaultCode} Message: {base.ToString()}"; } } } diff --git a/trunk/managed/XmlRpc/XmlRpcExposedAttribute.cs b/trunk/managed/XmlRpc/XmlRpcExposedAttribute.cs index 2a33092d..6238a050 100644 --- a/trunk/managed/XmlRpc/XmlRpcExposedAttribute.cs +++ b/trunk/managed/XmlRpc/XmlRpcExposedAttribute.cs @@ -35,7 +35,7 @@ namespace Nwc.XmlRpc Type type = obj.GetType(); MethodInfo method = type.GetMethod(methodName); - if(method == null) + if(method is null) throw new MissingMethodException("Method " + methodName + " not found."); if(!IsExposed(type)) diff --git a/trunk/managed/XmlRpc/XmlRpcRequest.cs b/trunk/managed/XmlRpc/XmlRpcRequest.cs index 40ce8b4c..1b877f42 100644 --- a/trunk/managed/XmlRpc/XmlRpcRequest.cs +++ b/trunk/managed/XmlRpc/XmlRpcRequest.cs @@ -5,6 +5,10 @@ using System.Xml; using System.Net; using System.Net.Security; using System.Text; +using System.Net.Http; +using System.Threading; +using System.Linq; +using System.Threading.Tasks; namespace Nwc.XmlRpc { @@ -12,9 +16,9 @@ namespace Nwc.XmlRpc public class XmlRpcRequest { private string m_methodName = null; - private Encoding m_encoding = new UTF8Encoding(); - private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer(); - private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer(); + private readonly Encoding m_encoding = new UTF8Encoding(); + private readonly XmlRpcRequestSerializer _serializer = new(); + private readonly XmlRpcResponseDeserializer _deserializer = new(); /// ArrayList containing the parameters. protected IList _params = null; @@ -67,12 +71,12 @@ namespace Nwc.XmlRpc { get { - int index = MethodName.IndexOf("."); + int index = MethodName.IndexOf('.'); if (index == -1) return MethodName; - return MethodName.Substring(0, index); + return MethodName[..index]; } } @@ -81,12 +85,12 @@ namespace Nwc.XmlRpc { get { - int index = MethodName.IndexOf("."); + int index = MethodName.IndexOf('.'); if (index == -1) return MethodName; - return MethodName.Substring(index + 1, MethodName.Length - index - 1); + return MethodName.Substring(index + 1); } } @@ -94,6 +98,16 @@ namespace Nwc.XmlRpc /// String The url of the XML-RPC server. /// Object The value returned from the method invocation on the server. /// If an exception generated on the server side. + public object Invoke(string url, HttpClient client) + { + XmlRpcResponse res = Send(url, client); + + if (res.IsFault) + throw new XmlRpcException(res.FaultCode, res.FaultString); + + return res.Value; + } + public object Invoke(string url, RemoteCertificateValidationCallback certCallBack = null) { XmlRpcResponse res = Send(url, 100000, certCallBack); @@ -121,7 +135,7 @@ namespace Nwc.XmlRpc request.ServerCertificateValidationCallback = certCallBack; using (Stream stream = request.GetRequestStream()) - using (XmlTextWriter xml = new XmlTextWriter(stream, m_encoding)) + using (XmlTextWriter xml = new(stream, m_encoding)) { _serializer.Serialize(xml, this); xml.Flush(); @@ -129,11 +143,67 @@ namespace Nwc.XmlRpc XmlRpcResponse resp; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) - using (StreamReader input = new StreamReader(response.GetResponseStream())) + using (StreamReader input = new(response.GetResponseStream())) resp = (XmlRpcResponse)_deserializer.Deserialize(input); return resp; } + public XmlRpcResponse Send(string url, HttpClient client) + { + HttpResponseMessage responseMessage = null; + HttpRequestMessage request = null; + try + { + request = new(HttpMethod.Post, url); + request.Headers.ExpectContinue = false; + request.Headers.TransferEncodingChunked = false; + + //if (keepalive) + { + request.Headers.TryAddWithoutValidation("Keep-Alive", "timeout=30, max=10"); + request.Headers.TryAddWithoutValidation("Connection", "Keep-Alive"); + } + //else + // request.Headers.TryAddWithoutValidation("Connection", "close"); + + byte[] outbuf; + using (MemoryStream outbufms = new()) + using (XmlTextWriter xml = new(outbufms, m_encoding)) + { + _serializer.Serialize(xml, this); + xml.Flush(); + outbuf = outbufms.ToArray(); + } + + request.Content = new ByteArrayContent(outbuf); + request.Content.Headers.TryAddWithoutValidation("Content-Type", "text/xml"); + request.Content.Headers.TryAddWithoutValidation("Content-Length", outbuf.Length.ToString()); + + responseMessage = client.Send(request, HttpCompletionOption.ResponseHeadersRead); + responseMessage.EnsureSuccessStatusCode(); + + XmlRpcResponse resp; + + using (StreamReader input = new(responseMessage.Content.ReadAsStream())) + resp = (XmlRpcResponse)_deserializer.Deserialize(input); + return resp; + } + catch (WebException ex) when (ex.Status is WebExceptionStatus.Timeout) + { + throw new HttpRequestException("request timeout"); + } + //catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) + catch (TaskCanceledException) + { + throw new HttpRequestException("request timeout"); + } + finally + { + request?.Dispose(); + responseMessage?.Dispose(); + } + } + /// Produce String representation of the object. /// String representation of the object. override public string ToString() diff --git a/trunk/managed/XmlRpc/XmlRpcRequestDeserializer.cs b/trunk/managed/XmlRpc/XmlRpcRequestDeserializer.cs index 5e96cc41..81ce496f 100644 --- a/trunk/managed/XmlRpc/XmlRpcRequestDeserializer.cs +++ b/trunk/managed/XmlRpc/XmlRpcRequestDeserializer.cs @@ -14,9 +14,7 @@ namespace Nwc.XmlRpc { get { - if(_singleton == null) - _singleton = new XmlRpcRequestDeserializer(); - + _singleton ??= new XmlRpcRequestDeserializer(); return _singleton; } } @@ -26,10 +24,10 @@ namespace Nwc.XmlRpc /// XmlRpcRequest object resulting from the parse. override public Object Deserialize(TextReader xmlData) { - using(XmlTextReader reader = new XmlTextReader(xmlData)) + using(XmlTextReader reader = new(xmlData)) { reader.DtdProcessing = DtdProcessing.Ignore; - XmlRpcRequest request = new XmlRpcRequest(reader.Encoding); + XmlRpcRequest request = new(reader.Encoding); bool done = false; lock(this) diff --git a/trunk/managed/XmlRpc/XmlRpcRequestSerializer.cs b/trunk/managed/XmlRpc/XmlRpcRequestSerializer.cs index b83a14c0..e86b1d59 100644 --- a/trunk/managed/XmlRpc/XmlRpcRequestSerializer.cs +++ b/trunk/managed/XmlRpc/XmlRpcRequestSerializer.cs @@ -16,9 +16,7 @@ namespace Nwc.XmlRpc { get { - if(_singleton == null) - _singleton = new XmlRpcRequestSerializer(); - + _singleton ??= new XmlRpcRequestSerializer(); return _singleton; } } diff --git a/trunk/managed/XmlRpc/XmlRpcResponder.cs b/trunk/managed/XmlRpc/XmlRpcResponder.cs index 9f0e0ea7..942175cd 100644 --- a/trunk/managed/XmlRpc/XmlRpcResponder.cs +++ b/trunk/managed/XmlRpc/XmlRpcResponder.cs @@ -12,9 +12,9 @@ namespace Nwc.XmlRpc /// side dialog. Namely they manage an inbound deserializer and an outbound serializer. public class XmlRpcResponder { - private XmlRpcRequestDeserializer _deserializer = new XmlRpcRequestDeserializer(); - private XmlRpcResponseSerializer _serializer = new XmlRpcResponseSerializer(); - private XmlRpcServer _server; + private readonly XmlRpcRequestDeserializer _deserializer = new(); + private readonly XmlRpcResponseSerializer _serializer = new(); + private readonly XmlRpcServer _server; private TcpClient _client; private SimpleHttpRequest _httpReq; @@ -58,7 +58,7 @@ namespace Nwc.XmlRpc public void Respond(SimpleHttpRequest httpReq) { XmlRpcRequest xmlRpcReq = (XmlRpcRequest)_deserializer.Deserialize(httpReq.Input); - XmlRpcResponse xmlRpcResp = new XmlRpcResponse(); + XmlRpcResponse xmlRpcResp = new(); try { @@ -74,12 +74,12 @@ namespace Nwc.XmlRpc XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); } - if(Logger.Delegate != null) + if(Logger.Delegate is not null) Logger.WriteEntry(xmlRpcResp.ToString(),LogLevel.Information); XmlRpcServer.HttpHeader(httpReq.Protocol,"text/xml",0," 200 OK",httpReq.Output); httpReq.Output.Flush(); - using(XmlTextWriter xml = new XmlTextWriter(httpReq.Output)) + using(XmlTextWriter xml = new(httpReq.Output)) { _serializer.Serialize(xml, xmlRpcResp); xml.Flush(); @@ -90,13 +90,13 @@ namespace Nwc.XmlRpc ///Close all contained resources, both the HttpReq and client. public void Close() { - if(_httpReq != null) + if(_httpReq is not null) { _httpReq.Close(); _httpReq = null; } - if(_client != null) + if(_client is not null) { _client.Close(); _client = null; diff --git a/trunk/managed/XmlRpc/XmlRpcResponse.cs b/trunk/managed/XmlRpc/XmlRpcResponse.cs index 838e8e02..539b3918 100644 --- a/trunk/managed/XmlRpc/XmlRpcResponse.cs +++ b/trunk/managed/XmlRpc/XmlRpcResponse.cs @@ -69,9 +69,11 @@ namespace Nwc.XmlRpc /// String the faultString value. public void SetFault(int code,String message) { - Hashtable fault = new Hashtable(); - fault.Add("faultCode",code); - fault.Add("faultString",message); + Hashtable fault = new() + { + { "faultCode", code }, + { "faultString", message } + }; Value = fault; IsFault = true; } diff --git a/trunk/managed/XmlRpc/XmlRpcResponseDeserializer.cs b/trunk/managed/XmlRpc/XmlRpcResponseDeserializer.cs index 853d4bfd..38a70ee6 100644 --- a/trunk/managed/XmlRpc/XmlRpcResponseDeserializer.cs +++ b/trunk/managed/XmlRpc/XmlRpcResponseDeserializer.cs @@ -18,9 +18,7 @@ namespace Nwc.XmlRpc { get { - if(_singleton == null) - _singleton = new XmlRpcResponseDeserializer(); - + _singleton ??= new XmlRpcResponseDeserializer(); return _singleton; } } @@ -30,10 +28,10 @@ namespace Nwc.XmlRpc /// XmlRpcResponse object resulting from the parse. override public Object Deserialize(TextReader xmlData) { - using(XmlTextReader reader = new XmlTextReader(xmlData)) + using(XmlTextReader reader = new(xmlData)) { reader.DtdProcessing = DtdProcessing.Ignore; - XmlRpcResponse response = new XmlRpcResponse(); + XmlRpcResponse response = new(); bool done = false; lock(this) diff --git a/trunk/managed/XmlRpc/XmlRpcResponseSerializer.cs b/trunk/managed/XmlRpc/XmlRpcResponseSerializer.cs index 2bc78915..f4a3c206 100644 --- a/trunk/managed/XmlRpc/XmlRpcResponseSerializer.cs +++ b/trunk/managed/XmlRpc/XmlRpcResponseSerializer.cs @@ -16,9 +16,7 @@ namespace Nwc.XmlRpc { get { - if(_singleton == null) - _singleton = new XmlRpcResponseSerializer(); - + _singleton ??= new XmlRpcResponseSerializer(); return _singleton; } } diff --git a/trunk/managed/XmlRpc/XmlRpcSerializer.cs b/trunk/managed/XmlRpc/XmlRpcSerializer.cs index 8ce31104..b2850c75 100644 --- a/trunk/managed/XmlRpc/XmlRpcSerializer.cs +++ b/trunk/managed/XmlRpc/XmlRpcSerializer.cs @@ -13,7 +13,7 @@ namespace Nwc.XmlRpc /// public class XmlRpcSerializer : XmlRpcXmlTokens { - private Encoding m_encoding = new UTF8Encoding(); + private readonly Encoding m_encoding = new UTF8Encoding(); /// Serialize the XmlRpcRequest to the output stream. /// An XmlTextWriter stream to write data to. /// An Object to serialize. @@ -30,17 +30,13 @@ namespace Nwc.XmlRpc /// public String Serialize(Object obj) { - using (MemoryStream ms = new MemoryStream(1024)) - { - using (XmlTextWriter xml = new XmlTextWriter(ms, m_encoding)) - { - xml.Formatting = Formatting.Indented; - xml.Indentation = 4; - Serialize(xml, obj); - xml.Flush(); - return m_encoding.GetString(ms.ToArray()); - } - } + using MemoryStream ms = new(1024); + using XmlTextWriter xml = new(ms, m_encoding); + xml.Formatting = Formatting.Indented; + xml.Indentation = 4; + Serialize(xml, obj); + xml.Flush(); + return m_encoding.GetString(ms.ToArray()); } /// Serialize the object to the output stream. @@ -48,12 +44,11 @@ namespace Nwc.XmlRpc /// An Object to serialize. public void SerializeObject(XmlTextWriter output, Object obj) { - if (obj == null) + if (obj is null) return; - if (obj is byte[]) + if (obj is byte[] ba) { - byte[] ba = (byte[])obj; output.WriteStartElement(BASE64); output.WriteBase64(ba, 0, ba.Length); output.WriteEndElement(); @@ -66,25 +61,25 @@ namespace Nwc.XmlRpc { output.WriteElementString(INT, obj.ToString()); } - else if (obj is DateTime) + else if (obj is DateTime dt) { - output.WriteElementString(DATETIME, ((DateTime)obj).ToString(ISO_DATETIME)); + output.WriteElementString(DATETIME, dt.ToString(ISO_DATETIME)); } - else if (obj is Double) + else if (obj is Double dbl) { - output.WriteElementString(DOUBLE, ((Double)obj).ToString(CultureInfo.InvariantCulture)); + output.WriteElementString(DOUBLE, dbl.ToString(CultureInfo.InvariantCulture)); } - else if (obj is Boolean) + else if (obj is Boolean bl) { - output.WriteElementString(BOOLEAN, ((((Boolean)obj) == true) ? "1" : "0")); + output.WriteElementString(BOOLEAN, bl == true ? "1" : "0"); } - else if (obj is IList) + else if (obj is IList lst) { output.WriteStartElement(ARRAY); output.WriteStartElement(DATA); if (((ArrayList)obj).Count > 0) { - foreach (Object member in ((IList)obj)) + foreach (Object member in lst) { output.WriteStartElement(VALUE); SerializeObject(output, member); @@ -94,9 +89,8 @@ namespace Nwc.XmlRpc output.WriteEndElement(); output.WriteEndElement(); } - else if (obj is IDictionary) + else if (obj is IDictionary h) { - IDictionary h = (IDictionary)obj; output.WriteStartElement(STRUCT); foreach (String key in h.Keys) { diff --git a/trunk/managed/XmlRpc/XmlRpcSystemObject.cs b/trunk/managed/XmlRpc/XmlRpcSystemObject.cs index 47725d64..fc61044f 100644 --- a/trunk/managed/XmlRpc/XmlRpcSystemObject.cs +++ b/trunk/managed/XmlRpc/XmlRpcSystemObject.cs @@ -1,251 +1,254 @@ namespace Nwc.XmlRpc { - using System; - using System.Collections; - using System.Reflection; + using System; + using System.Collections; + using System.Reflection; - /// XML-RPC System object implementation of extended specifications. - [XmlRpcExposed] - public class XmlRpcSystemObject - { - private XmlRpcServer _server; - static private IDictionary _methodHelp = new Hashtable(); + /// XML-RPC System object implementation of extended specifications. + [XmlRpcExposed] + public class XmlRpcSystemObject + { + private readonly XmlRpcServer _server; + static private readonly IDictionary _methodHelp = new Hashtable(); + + /// Static IDictionary to hold mappings of method name to associated documentation String + static public IDictionary MethodHelp + { + get { return _methodHelp; } + } + + /// Constructor. + /// XmlRpcServer server to be the system object for. + public XmlRpcSystemObject(XmlRpcServer server) + { + _server = server; + server.Add("system", this); + _methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description."); + } + + /// Invoke a method on a given object. + /// Using reflection, and respecting the XmlRpcExposed attribute, + /// invoke the methodName method on the target + /// instance with the parameters provided. All this packages other Invoke methods + /// end up calling this. + /// Object the value the invoked method returns. + /// If method does not exist, is not exposed, parameters invalid, or invocation + /// results in an exception. Note, the XmlRpcException.Code will indicate cause. + static public Object Invoke(Object target, String methodName, IList parameters) + { + if (target is null) + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Invalid target object."); + + Type type = target.GetType(); + MethodInfo method = type.GetMethod(methodName); + + try + { + if (!XmlRpcExposedAttribute.ExposedMethod(target, methodName)) + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Method " + methodName + " is not exposed."); + } + catch (MissingMethodException me) + { + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": " + me.Message); + } + + Object[] args = new Object[parameters.Count]; + + int index = 0; + foreach (Object arg in parameters) + { + args[index] = arg; + index++; + } + + try + { + Object retValue = method.Invoke(target, args); + if (retValue is null) + throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": Method returned NULL."); + return retValue; + } + catch (XmlRpcException e) + { + throw e; + } + catch (ArgumentException ae) + { + Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + ae.Message, + LogLevel.Information); + String call = methodName + "( "; + foreach (Object o in args) + { + call += o.GetType().Name; + call += " "; + } + call += ")"; + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, + XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement type mismatch invoking " + call); + } + catch (TargetParameterCountException tpce) + { + Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + tpce.Message, + LogLevel.Information); + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, + XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement count mismatch invoking " + methodName); + } + catch (TargetInvocationException tie) + { + throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + " Invoked method " + methodName + ": " + tie.Message); + } + } + + /// List methods available on all handlers of this server. + /// IList An array of Strings, each String will have form "object.method". + [XmlRpcExposed] + public IList listMethods() + { + IList methods = new ArrayList(); + Boolean considerExposure; + + foreach (DictionaryEntry handlerEntry in _server) + { + considerExposure = XmlRpcExposedAttribute.IsExposed(handlerEntry.Value.GetType()); + + foreach (MemberInfo mi in handlerEntry.Value.GetType().GetMembers()) + { + if (mi.MemberType != MemberTypes.Method) + continue; + + if (!((MethodInfo)mi).IsPublic) + continue; + + if (considerExposure && !XmlRpcExposedAttribute.IsExposed(mi)) + continue; + + methods.Add(handlerEntry.Key + "." + mi.Name); + } + } + + return methods; + } + + /// Given a method name return the possible signatures for it. + /// String The object.method name to look up. + /// IList Of arrays of signatures. + [XmlRpcExposed] + public IList methodSignature(String name) + { + IList signatures = new ArrayList(); + int index = name.IndexOf('.'); + + if (index < 0) + return signatures; + + String oName = name[..index]; + Object obj = _server[oName]; + + if (obj is null) + return signatures; + + MemberInfo[] mi = obj.GetType().GetMember(name[(index + 1)..]); + + if (mi is null || mi.Length != 1) // for now we want a single signature + return signatures; + + MethodInfo method; + + try + { + method = (MethodInfo)mi[0]; + } + catch (Exception e) + { + Logger.WriteEntry("Attempted methodSignature call on " + mi[0] + " caused: " + e, + LogLevel.Information); + return signatures; + } + + if (!method.IsPublic) + return signatures; + + IList signature = new ArrayList + { + method.ReturnType.Name + }; + + foreach (ParameterInfo param in method.GetParameters()) + { + signature.Add(param.ParameterType.Name); + } + + + signatures.Add(signature); + + return signatures; + } + + /// Help for given method signature. Not implemented yet. + /// String The object.method name to look up. + /// String help text. Rich HTML text. + [XmlRpcExposed] + public String methodHelp(String name) + { + String help = null; + + try + { + help = (String)_methodHelp[_server.MethodName(name)]; + } + catch (XmlRpcException e) + { + throw e; + } + catch (Exception) { /* ignored */ }; + + help ??= "No help available for: " + name; + + return help; + } + + /// Boxcarring support method. + /// IList of calls + /// ArrayList of results/faults. + [XmlRpcExposed] + public IList multiCall(IList calls) + { + IList responses = new ArrayList(); + XmlRpcResponse fault = new(); + + foreach (IDictionary call in calls) + { + try + { + XmlRpcRequest req = new((String)call[XmlRpcXmlTokens.METHOD_NAME], (ArrayList)call[XmlRpcXmlTokens.PARAMS]); + Object results = _server.Invoke(req); + IList response = new ArrayList + { + results + }; + responses.Add(response); + } + catch (XmlRpcException e) + { + fault.SetFault(e.FaultCode, e.FaultString); + responses.Add(fault.Value); + } + catch (Exception e2) + { + fault.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); + responses.Add(fault.Value); + } + } + + return responses; + } - /// Static IDictionary to hold mappings of method name to associated documentation String - static public IDictionary MethodHelp { - get { return _methodHelp; } } - - /// Constructor. - /// XmlRpcServer server to be the system object for. - public XmlRpcSystemObject(XmlRpcServer server) - { - _server = server; - server.Add("system",this); - _methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description."); - } - - /// Invoke a method on a given object. - /// Using reflection, and respecting the XmlRpcExposed attribute, - /// invoke the methodName method on the target - /// instance with the parameters provided. All this packages other Invoke methods - /// end up calling this. - /// Object the value the invoked method returns. - /// If method does not exist, is not exposed, parameters invalid, or invocation - /// results in an exception. Note, the XmlRpcException.Code will indicate cause. - static public Object Invoke(Object target, String methodName, IList parameters) - { - if (target == null) - throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, - XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Invalid target object."); - - Type type = target.GetType(); - MethodInfo method = type.GetMethod(methodName); - - try - { - if (!XmlRpcExposedAttribute.ExposedMethod(target,methodName)) - throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, - XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Method " + methodName + " is not exposed."); - } - catch (MissingMethodException me) - { - throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, - XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": " + me.Message); - } - - Object[] args = new Object[parameters.Count]; - - int index = 0; - foreach (Object arg in parameters) - { - args[index] = arg; - index++; - } - - try - { - Object retValue = method.Invoke(target, args); - if (retValue == null) - throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, - XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": Method returned NULL."); - return retValue; - } - catch (XmlRpcException e) - { - throw e; - } - catch (ArgumentException ae) - { - Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + ae.Message, - LogLevel.Information); - String call = methodName + "( "; - foreach (Object o in args) - { - call += o.GetType().Name; - call += " "; - } - call += ")"; - throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, - XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement type mismatch invoking " + call); - } - catch (TargetParameterCountException tpce) - { - Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + tpce.Message, - LogLevel.Information); - throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, - XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement count mismatch invoking " + methodName); - } - catch (TargetInvocationException tie) - { - throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, - XmlRpcErrorCodes.APPLICATION_ERROR_MSG + " Invoked method " + methodName + ": " + tie.Message); - } - } - - /// List methods available on all handlers of this server. - /// IList An array of Strings, each String will have form "object.method". - [XmlRpcExposed] - public IList listMethods() - { - IList methods = new ArrayList(); - Boolean considerExposure; - - foreach (DictionaryEntry handlerEntry in _server) - { - considerExposure = XmlRpcExposedAttribute.IsExposed(handlerEntry.Value.GetType()); - - foreach (MemberInfo mi in handlerEntry.Value.GetType().GetMembers()) - { - if (mi.MemberType != MemberTypes.Method) - continue; - - if(!((MethodInfo)mi).IsPublic) - continue; - - if (considerExposure && !XmlRpcExposedAttribute.IsExposed(mi)) - continue; - - methods.Add(handlerEntry.Key + "." + mi.Name); - } - } - - return methods; - } - - /// Given a method name return the possible signatures for it. - /// String The object.method name to look up. - /// IList Of arrays of signatures. - [XmlRpcExposed] - public IList methodSignature(String name) - { - IList signatures = new ArrayList(); - int index = name.IndexOf('.'); - - if (index < 0) - return signatures; - - String oName = name.Substring(0,index); - Object obj = _server[oName]; - - if (obj == null) - return signatures; - - MemberInfo[] mi = obj.GetType().GetMember(name.Substring(index + 1)); - - if (mi == null || mi.Length != 1) // for now we want a single signature - return signatures; - - MethodInfo method; - - try - { - method = (MethodInfo)mi[0]; - } - catch (Exception e) - { - Logger.WriteEntry("Attempted methodSignature call on " + mi[0] + " caused: " + e, - LogLevel.Information); - return signatures; - } - - if (!method.IsPublic) - return signatures; - - IList signature = new ArrayList(); - signature.Add(method.ReturnType.Name); - - foreach (ParameterInfo param in method.GetParameters()) - { - signature.Add(param.ParameterType.Name); - } - - - signatures.Add(signature); - - return signatures; - } - - /// Help for given method signature. Not implemented yet. - /// String The object.method name to look up. - /// String help text. Rich HTML text. - [XmlRpcExposed] - public String methodHelp(String name) - { - String help = null; - - try - { - help = (String)_methodHelp[_server.MethodName(name)]; - } - catch (XmlRpcException e) - { - throw e; - } - catch (Exception) { /* ignored */ }; - - if (help == null) - help = "No help available for: " + name; - - return help; - } - - /// Boxcarring support method. - /// IList of calls - /// ArrayList of results/faults. - [XmlRpcExposed] - public IList multiCall(IList calls) - { - IList responses = new ArrayList(); - XmlRpcResponse fault = new XmlRpcResponse(); - - foreach (IDictionary call in calls) - { - try - { - XmlRpcRequest req = new XmlRpcRequest((String)call[XmlRpcXmlTokens.METHOD_NAME], - (ArrayList)call[XmlRpcXmlTokens.PARAMS]); - Object results = _server.Invoke(req); - IList response = new ArrayList(); - response.Add(results); - responses.Add(response); - } - catch (XmlRpcException e) - { - fault.SetFault(e.FaultCode, e.FaultString); - responses.Add(fault.Value); - } - catch (Exception e2) - { - fault.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, - XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); - responses.Add(fault.Value); - } - } - - return responses; - } - - } } diff --git a/trunk/managed/XmlRpc/xmlrpc.csproj b/trunk/managed/XmlRpc/xmlrpc.csproj index b6442074..49337608 100644 --- a/trunk/managed/XmlRpc/xmlrpc.csproj +++ b/trunk/managed/XmlRpc/xmlrpc.csproj @@ -1,71 +1,17 @@ - + - Debug - AnyCPU - {678DEE13-A366-4130-9BCA-46F695640AD5} + net8.0 Library false - XMLRPC - v4.6 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false + false + True + true - pdbonly - true - bin\Release\ - prompt - 4 - false + false + True - - xmlrpc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file