From 3ea9686964c118bf249fd17c678b0f9afcd8ba82 Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Sun, 7 Dec 2025 23:50:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9shader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BladeSmith.sln.DotSettings.user | 1 + Content/Maps/Demo.umap | Bin 1537420 -> 1537794 bytes .../Private}/FTemperatureComputeShader.cpp | 0 .../Public}/FTemperatureComputeShader.h | 8 +- Source/BladeSmith/DynamicIronBlock.cpp | 157 ++++++++++++++---- Source/BladeSmith/DynamicIronBlock.h | 4 + 6 files changed, 136 insertions(+), 34 deletions(-) rename {Source/BladeSmith/Shaders => Plugins/MiceShaders/Source/MiceShaders/Private}/FTemperatureComputeShader.cpp (100%) rename {Source/BladeSmith/Shaders => Plugins/MiceShaders/Source/MiceShaders/Public}/FTemperatureComputeShader.h (92%) diff --git a/BladeSmith.sln.DotSettings.user b/BladeSmith.sln.DotSettings.user index d87c8ec..7b4f914 100644 --- a/BladeSmith.sln.DotSettings.user +++ b/BladeSmith.sln.DotSettings.user @@ -2,6 +2,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/Content/Maps/Demo.umap b/Content/Maps/Demo.umap index d24cbc54507bd665364229ef5af8661e19276013..c9eb402fbb601524f70d05b580c23560e04381b0 100644 GIT binary patch delta 91728 zcmeHw2Y6IP_x{}t3E9v#$?o1wvI*%BN+2PjlST*Wp@kZXiUNTUP!tIuqF87vj95WL zKO4dq5xdyMLa|^$G5A?g>8N1If6m;Q&E0!vHi;?R=l|Pz2)i@yJLjA^_uOf>?Cn2K zT5)31I;Z2^kKUPkdd;HOUJ584__uXf;A8Tn8ElladfNs>L+y~EUY z^preFy1^t#Wd)LS2QGfR5B?9Q=_XuAKY;`J0GvPD*d#RxHc74U-wWTQ_U~}!i@|p+ zT7|B8_012V2FM!hPBo8;pMx3u7s(*RlPYj3Ic<_&#HM%;CjF z^JW&8EFQLKW^wVX88a712S3<-Pg!tKX=qE}^c_-IGP8JA;p|aY7S5P8_lhA!GiJ_~ z5|ST$E8gAGX)n7^e%_LrjERglaPESV!nxCD@`abDG&)~4Hg0f0SP0Y^HDt=@xwB`L zNbXM`KU4N#;vME_J1!qKuW`t!4#8G{Y1x za~@ecX*bl{Alu4zwiz1Wvf)hrl{1Uy6wdBlGZ~74mIrWMNG;uQx$*9$Il-o(X!+De;VH|NV?#jp z^;Y%;m$I*0P+@scxak3RL~d7ii(Cu&mTS7zJs~#%|6iJ$Y&MzPM|0bUG@`XgHZyWF z5iLo9__L%KH#vyno89g6oTfGIetF$YkGYrS*-g3bVZBpKkGuEgb;sp@=H-PwrmCDj zYx>m^qb%;-Bj^fZ8xFlz?qj`emXE2a`R;#uxAe+IHCv8@HSKbmKPtosbafy&fI0H%=^L@uO|iX#Ka@Mh!k^U-j|H$p4&gF*;BaWQ(OXt`A|~2LtR3-VE(K0 zJ2M;_@jqIoKD@8dGG;h5;#&^yby~*khBka>k`$M!1-?Ov%y4ML_Z)#N3@%rkE!~89 zKltEXNqNUH?<)_F9#X)Vz@rodfj@ePw6=KV< z$N1rDDvl5Y7gSfrmP^ko0WBR89QN2T{#?k}C1laova&XI9;i zQfUPy9;=38;0@vkWWy}${a_y2*$0pHesI~g44eAG;B?XlkIiU*#=IV+#kl=y3zDf%Erd!tiB&B5+{1y;vL z907r6+*2kDa(A1MXzm1G3mj~#Q>~^DpfQn?u>c;t7T~cF0`w(w%yM_o=lOG<9`QBX`nFeyjVLG(>25 zxIpS1mrgy9CB*` z`CXYqP7#m~IyL0&MSM>jj?fUn6Kw@jzl`LN+X=|uL~+RN1>_%H9CE6Fd?K1d?jRta zi_wt#7V|xEv89Fxo=6i&mEt($bOAXio%hHE6eA*`Syhny=QPfgR1ldj`?;_`G25j@dbAa!;p4!Mtj zT%5rn_Z5(@&E$~#3CK%3bIAP#$+dE;`vC*JR^A%Z7h@y$EuKJ3FGj~0+W>&qdJ5s;7e+O?ub=Z2i)3#|B*%W17szPzYo=r zGYeeq<*S?yxPb?DNMMCcud_&xXvv?f%5cCPJdAh~h{x%r+UbMsmoxw>TYeiv}ZKPRTi=+(ow($Y0ScY;LS0 zQY7i&e6xG%gj|&UMWbMM%4(|vZpi>1_~1iI>O5MbY~kt*ncc?&(gssv@EEhZd~Gh8 zJw^_8TW+<=Z-DtYpW9HE|*34N`cOYiIlI8IE(9#fT8o zkY3Re!Md%tJ7sp=6RazRBY;!H6vevGJDl=)Dgf}}*MkUtS0Uh|qXIFe#!LEu`z0wP z$pUyJ?7OJ(7isgB&T@afb`063;lbUnAuw#t@ZjH85*W5*c<_HW5EwRMc<|gU7%p(| zpf)>5&8C8y-@ia$2xVUF2lo*eHduJ@BFD zKp<9m_|WDG4x1%BJjA8ous_1X-%3$%y8vI&MZsYggjYYSuY$`0{NhLjhrLW*{b!~s zIBaq7@Zz}&9x1?6mnb-FWbo=AU#;M}b_I_S;4x(i4jU7^`d{o- z@K^!9X}^NQ_5-i}$S)Nfb{u$k^WzE*n+!bs-QN{FQGl-s3@7mg+X=k-dEp8UI|w}d zpI8NlO#>eOe5!)OJ^>G3)KkG>Yk-HR4^r?p0{p~S1&2)lUj4^rDmbkBdHCfE6dacN zJUo86g2U>bhaX*|;IN?Q;dkGw;2j0{n8yfCmhQazVb8ixZYl8Ut65*W|4l_Stj&2< zZ~aKYVL8sj`yEs8OaX2_so5guDLAZNdH8GZDmW}ydHB-L6dcy5JiPn&3O+!9 z|8`cvVKvID|6h}x#1AY$dHCEG3Jz;d9^NKe!C{%n!@qB%;6nuXgIyII7L>gDllv(+ ztRZ=LWWIvKvXO@$E>v(>De~|QMG6j!Lmoc#1_d7_z(a0Ra99TN>c4fDf{zy9D;`#G zSnToYXFaXpu)gEr7hffKBrNH8_%jFG6}$LdTk%(lXqe;js;2&|;ILHV;m7|_a9Ex3 z@P~sOIJ5@3El*qJ-!KRNBkPO(WRioQ;+z<-d2N2^)qUs8MzPmrl)wTw}-*b#z<;?%5@ zToyclr)IsEOvxiYGgHUrDm}s6sut!<+9>5tR1a+vf?$Yr71jCf`&D;4Ud(m zVa2rNv63{b6I@oZhV^ngo~%|H)|&P_R%;Dwaw?AnTN>&kl-fbfVn--NBkON37CS<1 zG^`Igs%0@&TMg^JG#;y+hBYUh$7-)(_3Ff9!JR(pv$k0Vj|F#+t65)hSsgX3Co_4n zV1q|3>xRxeR=S2YqzjJ)8!_r56x~(LVn?WxM%FK0Eav+R4eP~jYFUhxNmUK8ksb(^HjVX)u(_eY9SPMXox}8t-Qp;j2xCu$kx;~4?f~Szx ztU=jomi@g9`AN)x1e;y&vCXg?m<8mzx4!4}!eOll>DxR74}ISof1BdgJ_^2EfPdOo z!A}eD`}-?+;Rk&AGY2a8ApzcQu!86A=hr`bnS$>U;Li_L@VEo~`pbta_<8|8V5EYZ z4)W`_$XD?B0{rl31wSUh?;ES&BM$N9&ls=ZuL|(C6BIn{L#IQ1{H^r(pPA@x`SF;@ zqxewsVDl6Le=UH+3JF{;fa9kV_?Q4rpNZiDu333kkedG!)EqgBz~2hsX;%Xr^_>Q0 zF};{tf`H3a@y0Iyz5;9msr z=IaUks{nrV1_GZDz|WNu_@n^dw;bT8QyQ3g_46AQub%#b@6|0g5$R_%)tOg!-b~=L z0(kFg0-qDW2X7-VT(#kaarqqt{!IX%T1(*H1+cV%z<&r}>zxF?Ab?{x5%{72?syl# zQGaS+=GC0L>G}ON->Z%9CDQ-aRA*k5?{qs2(nMf0N;U^jllN zrtR9&i0^Cx=l`QF9sS*afcz)aB~beU=9N9AEuHnfEg-B+TlzUJo&Susbl?xRfP>Fz zOP|A~9bQtGp8sJ$z?!}4641Z-M_a(JuWL&!KiL88>WH>9;ul-McSp6Qt8r2tWW>zjMfB5_aeme~^E|so=S%`S^lJ1>YsW zySo%T?u=88Qng|?K3b4WF{Bkq?uBPEWOz1`?J^uuaOoL^pxO&Eops9WiAb1F!hQ*) z^({fwK=A_ z3hAbZBhSN(|I;eRU|FCRtb2!&=5*7DLq<@Z&R>4$ls;#_Xi@%wj)H+m>`4$sHW?L_=-mDzJe(3lZm0ZzkvDqddxx6=zR1OMQ( zHCw??Yw$x9&(Blv!hf7HJe$NC22&?UNqw~qJA|2gROZ6Q1SD5KMN)1>o4WC_+!$wa%J8f`J50?KqV(z_ zH4~qvyEmIo21U2PH)DX>u|>tRXOL(3`pleDBt_xk;iva(Z)|FFAf!wCv`(q%>8a^m z_I!TW^hJcJ0Bt>OvL(Zn1+N5q!jSH!0DRnmjtw`u7*@JIQ?{8DCN+WP^!27-B~{DR*VE|>(=}2N+e+7r>p-QdZKRF)=;bq} zw#i@722WSvYHl&$`KZNNQ*3Hi${j;L*@yLR`c3WKk!bk^lgk1ZLLeiX^!H#}b5&w2|hR0k_fo68Z`J=g`*Pj>KV%0!a3qLH}t~bkop#_CEG-`4#uB zBW#heQniikS4IR$lF;71l)#J;+Uv4xU{n-!-iIqMxpi^B8QHp4zZ==Q_J0`8y0M

Fko3sBkvUPp`GO~4p|2DF9BmOb6 zbz>_GY~995X7Oy{(hPCjj&Q{aB#-;O|7CXt9y#%?eLd{A<9B7yky<;;xiogGx|&&L z!+BL@T~UCMty|d0$ktsSXk_b_2N~JAn;RS1x;ugmXC1dE>|f;ka9q)VK@WW2df(=~ z&t7Hho^W%B;k>Hq2_I-;Wb3v!HL`V&H#4$zyP6x>x;-t7Y~4#%!&%4e30+G*S+@Sk z&-&eS+BE*WiW;m~^dcp(YMz-z~n~|+MVmGpN#~ens?g!b(*8S=_?a z`(Ity_WO?9#NhWG4j}H`AN6!Yg+K26aR(zZ+~MGlJMaYDvE@H2{AUG3g8!^g-+bpk zEBt4L|Ez#pX#Hn}?)GE<%pIQ^{uvbX5B?bx_`p9_fEWBTDF0aDA1f~PV>68h8hK~7 z*&xGN$Gwta8g0I!N5ap&H-t^RbAM8Z^1!HJ?|6>5%y3>+UCC%U#2jmf*YiSBKl)aN zPLDRb(7YjLr~L*Fz7&}TIa;C@hnVdS_UIT9!XA4rKtBvICt6}@-2!AAinHt?^#YVT z)NHdX@t}~kld)Q3$Y1b;qYG^tYId47qZfyo6ZB6;P9&PO4ZsG*qgKPrVfOKy29!Yq z;?X>eTi~VFkP6UpJ;62`+7VtnH?Fku9+B2e$Y(rg! zo2}&c1VFMUZKB@_P$70e58V@ypl}1#ssjTt=rN5vPm10jZVqmyKE%Yc8Q7_g5$5E4 zuY(Ax?-a>ih=F^6fs;=0cKl5Hy$Xiixo(8nmJDu!bVm6g{}cZeh27Y_(#Us5m|c4N zT&TrJ;;Ggn&2gP{Mk+US;I{y%WW!f0dqTbd?HgrwmFn!AjY(I~N;+^J26|FZD_OFO z08dkBdx1yVIUAm4wxZ=@&Ee_lLAWU}fb51ZIQfW|>qnWLrJGff;gMzuo|?}qUbx`O zH2uS|9uKH05p&>o0%k#NoOG=#p=@=al+iL2JV1^@?eooHrMd%ggCb8K5OXoGk_yl{ z8r~5gbQ^$Nk4O^w-*9sz`YfMB#_4=>Y)GX+Xy-iq?u_)&xMSk#%SXF5207d4u;+2) zj-;(nx1KB}~PvZQp;Yd^s-~dn4S7Zp`RfU<8?}qURg$a z#+dh+=IG%fv~{dG*0h@-CJQ<|)|}XXyP{Tt7yq+F;sEkZXrVeRiO*SsW>bO%epuax zmW-2wE9DoDt6%xe$Jej?$Ld*rzk>R;|C@S|-vxeb0g^5#SU<&rgRl_!9|q4Y6Yv;* z7tN}}oQ}0WPrzd_{|q5xamGY*Sh$yLkd<%3Gk~CQuk(OT3N}t89l(x>=1Bec8Sb&G zutc^Eewbxx;~+~;19(9K`~U-_0v@=A7M2qVeoF*8I8ly@UqZ9+K~9zme$fa8-BC_lHw5b>R>s%RNL=ZcPJeF*ed1R+wXB9IQ4ix7ZtA? zl@E{UnSBkdsnI7(R`{t2NZyC{&}4JQmGCPSkaQBALs0)m6ig2`Z=aqGhX9gBog~>g z+ElN+h&XbwqJke#>-);-^5PsHoo?f)cxtz>_%CEn&%`EOuDjWEq!IoL#D78fukjw!k>G@Z_yrqkDyBEV ze@*dUGyK;a|Fyt>RxBcPVjolf9!!3rng7%82ZO}Hq5bx%<59eRg5Npvbe8l)gA<5! z%K&m`f699Bhj0g84{owKoIQj+==D^lZb_pxD_FAg5I=s~3ELYuV2T?cIQA>5@-s*6ct1d|<>ayfos@o}hwGL;w z>dq_bnmcQ$Zkf|d9nNyqz3C*aJ6cP?$YE5=^dpy)DN#WtkmBjhOfUQMx{TkF7?~I^j8S< z_i3rppI(>xVbWUJ{sS%f?YG6M^mnRD{jiv+)c-&%U;npi{TX$sAJzet`itWD`k#(d zwLh~i^}`aSQh#sPuQKOZ|~v`o9+FPfS$l?^>7o zqrCJ#mB?@ZIko<7b*bOwrN1Qt+a z7nxdlX@;yX&C^?{G}o%jMTfjKL)Mq(POVj%Yt`AJt*yP<3|U{A15#9)Yj>RIr+8_G ztS`+UsWsQCgGIS*yfj1Bm*#D4RBf(R*NQ^hdTEBNFU`W&9&=1_jdGZGh}^fj!#o* zu3hK3F3n3bWPNEqqt;xz&U0$ImuATN()?Pws?D{Fu#TO)G(*;x<_(=xnrqd0Zpu(A zhMX_G(=t?gYu9xi^iqr<>q~Qbrb=_|y3Q?`>Lw$|`qFIftkPV&u5(^zFU^qkrTIg( z=Gt|gIbFOoL)Mq(2fL6qL)TeQtMJ0JT$ad&X(z8~Z@aGiY47~59+ywZtW^i9)t}*| z|1W|5+-@HIS(&x#Mh`vd<#@=V@@@{!O)f4J+TEizDYF`VXk+CPdI7qwyW?K{wZ+N} zYOjDW1Kqraht$BwMbTJ@-#da4ye&X?Y4J&9ftb+zhTv_~DTm{u-!f9c7Oy0l)E z>TA`*Xw}23*(=q*(2J{nQ&y$+TJv=W{I5fi*OFk zI!Fa*e72)CesO={NQW)r5uf+6H2)kbY2IqT^PI`rOnsSiH@VE2wTFoJ@dEiq)Ph?n z@ot)jkJa!3eL1KZd|#};U*$TKcEBNbdIxI4Z!g>gUvSc03%{Dmyq110LAwSyWYlzz zBeeU=N*&J)P(mS3i-lxrh2&E%`z)b(b>qs=e7l_1FK5bL#-f%;lJJnhg?0A38fs@N zzX;F+Zajmey%26~rbTANpS{Ab`&pC;#2-{fnt>U*h{a%u(~tk==50S-Bp zwE_@wz3vUl5jNA+e*>jd-72aV_$1_8;4kodppeF(*ujoOCFGrU{;gZ{C*X&~+E6KH zQ0^FqEt)l*?;w09N(!oQ$(FvHOZ&p(x)|GI7$;e=F%96PQxhB(*DN2AgkJ53M93>^uZtkt963ppP+(cfzqT_3oXN zq0;Ic;*=VN5!{7q7)QbH&eyM_mdkQ_- zIJ-*g+$psf;2rfIG|WYozT2%^d^x+acV zVI)UIG=!tlUB3K-_To9YaVCk84dIa#1as#aEihIZDQuz|#z`p!H5$iY;cuA9kE>x^ zgyX0tL;WfvxhT3JToi|%n&b!X)vc^a*Olk-hrd00J z*W^0S+B$bsO*Rm%MGdimNalN1dA#7^Su$y^;aI!wlDaK+enYs8tTaOF)NSi8soOd( zXh65ks>5i5E%~|~Q)_aP<}7Rox6zZdF5Tw7q;4C2O~beiFT|{JUApbgOX{|%iyOji zWbqYRr*7MLN!>Q@`i5~EUU1c|+csTNw=KJ&!Q2*%<}P(K@wMwz!#N1Lq@G(>+AyBO zn9RWY9K6!!(v7EEmN$gw;t?Jg8lyEU>eO?aFRABt-q;YHORv#&EO-TL-NsTa zZZ?2>G3xz(4Dyc2#_0Wf9hN#BK;adPhVf*2LwJ&`u!7N-_t&u}?=_MqJ2kW?>vElc zpOHM7(GZ?gpWSuo$@`7u$;^iFBpZP(b-Ic2fRQ}exgk7BR%>JySht?sVkA#?X&6u9 z1sqw4)U7AC8p)Gg8`_hS#D6X0zu8%S&`6%_)-axAYdj0S547$)xy?wP?A{QbtX%We zttTHck|%pKgeO(^!q((!+IAy3s%Jwus&cwmms?x#ZeznN1$#AwA1jxFb?HZVNwH!4 znAL!O{AG)H+iQRvk5acf!fJ4jPfEvabGS^Rhooc4oA)(OvWBuJ=-^2!NCgO9 zg5c{F>lNF4-#0SoA&1otSL-2_lmC@Rv!5qVa!+K<282BA`vT=KQ)t%5BhZgxieXN& z9Bhm~ILI5%eBYP#=p*$W9FG|(ICA~LfjvNjq#s|M z9{J_=p0E=|QvEJycVZoe=|uWtWBi>+85;3E{&CmeU-sFFBnACr`Lf%*tZKwbr+=~W zfp)!Oq&FK3@J9yr0PR6WzvjCK361`2|KRAH`icze>qd%^cfkO-m3R` zM;SK8FyqM}e{8VWh)1@4KF1R@WnaAq$J0g%j=}!mAi;qr8=~KLCik?w{C>U1#qK(f zi<&(1cI*Suo#y}dF0#d;LkD~gAt?G_y@vx_i>%u*q$c5zbI2bKq+`IZiHmP_Pp_sp zgI|E1ga6BNUiIExjjq$Hn$*LqCtvl>{esSb*y$8p$ zMhcE${@}nKpi9;hpZgwFx_?pc!SS4tf@8QpI4Xl<$XC9DWAj(_9vsgbDL6*>gQGGy zo+$Sn9DkJ8dvL&QVuqP>!0mnhIR_qA;6hu|Z+*`>#(rDx!SSMzf@73FIIsukoa3q= zeD@sB{7~<~@sg2(Bi|n!mBF#^xG*?QAIBSF^?TLzWh2GKXn$O=xQIt7Cwz}6B`4~= z(|E;5!2ypK_@^6mM7jC2u+xC8@}yt?hD-88t8wAAE}li$Yos_C>yH!c0U9UYpYt6j z7oxs;@ygM|su3su>eh{|=Z$oMew;rtSY*Vbet-BJUXsk@){Wuy6d9W?7%4Eu`vZdo zMjU$RPoG@|I`wD0&pTen));1+qre{=mBA5E;X62nRn&WMykVr^nBWf%5*)c^+2^er zJIr!@KM?z7oySE@uGJq3kOeOCCZx}@RIv9UiKNXI~K z-B9L>V7+L5HaD}24Be72?DW8-aXgkk0vld&=WeGPgUWm!|%=TBt4*R-vCm@rY@1=!=j%^ph%z3axIv;z(~683zgn<-uG=68vkr{qy+GW|S5MZpI-OqV_USQgX$ zDg|{dn~7^jQVIG7gih>I{@KL^(U;vFFd$`DWb?L4Cl|)VdQ_6~HmG@ThXqX@Yqp@| z_VQKuJOsqZ0tbl4mqUksV~0{(N+yDILhLc{<;0L$`O3nlowRy64KPUkGyg>rmL||ojkc1kJb{7%gE+OJc`c{ zu5a-oEG2E(m>g!KM;t5gA@+;@Lb3S1=8Q<4tlOxG1!#RH9s-AE%E9`>2Qey~=vXjV zd1$Z{?x8u1XKi>!b23wo?|+syMBST^c}D~_MrY-0T0RH)$M$xx#?Viw zMb0l62!suQ*;i!)e(bDkKxh{^BX_e(W-|~B?H+aY;>%`Tab<~AjV5gxEz71csKXrD z*-YI>yh!?p_?JK?PP(>lWQ{_mu6UwdX(?pw#BaD7$1~Hr5|=OODo2*)fM}d_J)&5h zs=dUA*Wdz=avsPvMNx}>7;bi;w%ufBsoIZC0Bwx_M$Nl&X7NlPZP)6%)|W8=ii|KScV8Z827W(u$?1)O&jm(n&F`2ww^$=$8tt3y0Je?zl;lTp5^cHHl7Rx}? zSlv_|$}3HX&y_2*CfqAD0Xr(KQO?@EiUG6tP#wC^xef!u&JRvO;aLqexhE1?HKVS5 z$XPU^kDS<}8XX`#Q0ya~*^74ek!>B=S;AoHQHIk`07yr411@-t%tyWE05&ogi&+T^ zZ!o$PD&XWDU3k*zLNofxPWv)~dgDtW`Z7i>rI2o(Cgr&LEvKOFEMzTcXvJ28DkeL| zWJ1DsHB3nA#no^O(bOS9!CvuEn`2vriyK^_p`*C8cBc=EW;o5Jsm^_=gXqK?LRqxu zYe9IVtAHh(S-&g|AYDaiR%_f-K;0*(0Nwc>_~@1_Saw0ufzTP0D42eXix2_%9>7Vz zv5y%HK@*!ab~Y=gS=hLFlWg3ivS}kAq+f=+VjvY!8`XVX57Nly+hIYxisBYDHQixL ze@@sWDJQ2DM^>`3&fOh|Wj#c3W(;J1AamI0?G)O^5fO|+N8nxAtrXIGQ`(Z#nStol zR~_LY*hB5FV1#Q(ePmDOG2~(Fb#7i|Gsqvsu=8K_S_& ztD#6DFzm>xvdWGu)nl_KP*mOKeM*~?Cv zAOzAF3GEtXcCsNyDa8Lr({EZ;($ylMjR=4TFd*qfjZ?}2Kp#$#9r_*jhbTiGmP#Dh z$5OX(p!>v7FEO-C43Xs;Hie^Wy{J~S3s+cn?bd)##ce1QRZg@|6+$Rxf$TjnDIW3s zY)MC2RS9uN&MuCqJ75AvHqR_qwqty1D14C4Pm;Fel6`Mnz&qXYqG(OstQ0OqjHaL` zo>A^3a}?^B;|N4^CYgin&9nrp=4}%2VefzqI3+3{5T(b4n&nbm2*J(RkaR$?QmzMZ zmratc#xwz7UEl%W8mBtVp&6|#E9;rHfW#zd0c#H?3eB4*JI85pDT?80{qVhu2pCM= zs}9<3%DD9rY^SvsRV15{7}VL2A*#_w09dc$0$I59IxZqf*6Rg;G?%u=f^x^1!$X#h z@#H&>#oHS9cxOjZF&0G72PnD|D%&hgX;#@2w(Rg^p)0rlM(dW-PZ&Ut2(Em5NjWF! zXE}s2D!atE%NuDwz1hhK5`r z2cxco9ZkLFt(kDpW9@xuRdqb{CGik+D~P#DKh5b+YMPYGqFyrl5rq7DQWBeQvClM) zqAn=3Kz60y&ssx2L9f!);DQ;nupCNp0=NGtn78~y7{2PHvwjDdFxN4G_;HrXK9Hv3mYl9-b3cf0ma>gS$6&AtV-^Q0$-O z5ts0$S4N-ZxzMsgIoRIdbG*M5kJ0O`_83mr`H5h}I!vpsrFE3Cqeo|2tR3~OtptEH zi2Y^5_H{_mg?vak z%0?O6RUO6o`qqKZE3eUN6^JQoJ2$7W4tQy*RrJRt>26>>9 z?KZfv3etm=r}NZ7JjHs_%jS@r-<3Le%L+`hT%_4@wEikNL4PmSev1F;sRoOh>`pYR z^FOTBA#waaFYmve2Hic4c=cs`pIvg0$b7KvP9lv3MRK}W{NC&o2BN~*vMs(Ft*W(A z8ssqpR#*Bqe?cn_@W>ihz96Km~e`GB_4r?6XY_4#>-5JrKFi(!yLYnq1AsH?D6ek^6XU)Xh#NMov5pVeloVJJVu^D-<_z2P zrTV&U(~j6)0h^)|ms&Ty*JNVArmoGe3fS~s>d;_&T9+#)UO#n4zbTXF6pX!otTOh2 zxoMkPXCJm$9(6_MW>|Wtgn?++6MdcCN@go8?wl zVqQG{zeiq@wN!CU&ui`8rPyj>Eui56*MU4|!~;Ypq+UPchVe=*{GkT^Tj~nVZ?8T} z#I^B0Z~WC0rxZ>eJ4fm48k3)FdBSy9eh14^*W3Ad4Jij|22LC|$%Et_SKjlFI=fd1)2HYMZTaP*T3}1Nzwfa+VodkTHD5AB58h=C5ScO|i8m%SNc)x(# zO44v8-TsFf9}s~5A!)RhP~(FFj(TuBt|4rwGSVN?;&M?>DjLHQzudps5rze>2hK5{ftJOvYdTK#F8fQ2+v zco58BxqUtsEQ%SX!fyXV!3&`Tyj`r|uYD}|SD(0&($Xj01Q`oRAX{cU_y*w#m`vyWWKvWru^xl|uKb2H|?@+#P^+)9r>8iWAm3l!2}miB%y zJ#A^g(cTYY>w7`LR9^}0{b1f4DNqPgcG#$UVRA)3v!3BOm|OY4l_)Rx1}bYW!qIkx zru#uqV`?C6*55F*qX`9di1lsN=3X(m` zX#)sOfKw2spy{Z-V3HT~{mkjp;*WxfM9D)K%rj6(xqC1dLCi+Sg3c>;d{VV_#I^~9fj~En8r`)hU zMn8-6xo6zi|Df=wfkHavhK00t@|q}1iH?<+%kqJH0COEgD;Ct%RN);MY)=)sa)!DW zZH^h3Oegi=eEM1B11g(^xolbj(F?&sEuoWtaMdpsM%&?Y7KeOB+oKB)p znTS7pNcbSUG@MEVu;8t2Xzw>QRP@WJpdi<(kqIem-s`!%<4E2wgn6S}i-$WK(RE<9 ztgMs5vRnZp5^NKNvh)~S6kT^^RUsjs7CQ=;AmZN^@|lf9>}d&0*NIs>Vo!;xlomTh zVn;s-KaJxLF&@FmLUOQ3qE0g3>qt_xe0bb!%X6Vq&_K*)`^R5nxNv1t(? z5P_M$Guj)E^)`WO>+pvclok;r(Uxz^E0TLShz%Yk(yVE=O*SXTlL`HKO! zjT6^+)OiL1|J9dhyYd1DHjseV{LO*E5^&l-92g=2XO?ncLkT!vL5FJAAQ1$W36+31 zTNrTnRbtoNVPzoD7ADblcK`=EB;b8DI8c><57p#ArvzLb$bsP!@Tpo1ICY}9#%qHZ z2x=TD(e|Q^1EVD1tF<|>kp%p29S)3^fN$01z{V2rgL)hoBLVl+XTXxlV%L1$fPug@ zaT0Cc1an}#1pF?90}~|R;f5TTC;@*9<-jBf_`970n@GUF!WeMbRIzImm4U!D$r5dW zP7Z7)0qcfyU~>uBFoFYHNI+*K2c}5C=qLtkIYV6IghmVmHEt=<)-0L>(Kw>oPg8s|0+hH3xQ+ zfUmbJ>d=4Ba0fP!SaF7J7--Q9! z-y(L6y(+=t1uYGjnF*CMCmV@$yU z2%Jp9sR3;Jd2kB`;a(pK(#y)n`+93k9YZvnV$8V$r{NobJemJ+7orR<^v>*Bd56;h zHvrL6>JeqVi#V{(olX_*>!9FO1RUDmigw4T;b=0xfzLJi&df&imLFIrSWe2_HURSB z&1;z@%605cr(+-11j@i09|ME8egnDUhu`H?>D?WmxR;w#Vh35#)b{RLXPf)p?6n_73w(!!$nU3#B z!ReT&N2uZvpf1TZbxEcJ!h+WI1|lD)$;n9|)ZqLQr{h;lQ4qo#wl8>a(o&~NFQbB8 zbbU2r6l)i#{2G(=0x6I)-VD@KZXCmsCCkzxVcmiyuP=ahQIz-u8{=poVGV*Mw~`8@ z2_YO=OF|Bq=}l&O*+}&@xYe6Vi-ffcQOP^+;YnD$5Rr>k@g%HXh{*gGcoLQ`L}cmf zJP8Mrh}`lXOE$h&T<=+*vlMt7Rxm{RTK&M2u!JEZkDug8Si=yJ&zJHf97rPa>Us^i z_J&mq5g8xJlXeMtuqjKnTp{+*$~G(o9)g7oQOSb~coJ4JL}X}xo`j_g5xILbPr_P; zh+KRvPew?{Zu58&Ry0I91MXzW><7di+IBxnfrnr{L!@us6FdnE8X~goi#!P{8Y1%a zHlB=;kn2C>$yf1&oe_>j2XO^&b>)EibcL^ghZh9_ZJLqx8s5z56mtZRtK;SG5b z7B)nr8pD&YvLPZrN#V(+5^`A}#G^3SC#xoow#-kVmk6x17*I7Bv1eU>L-jYC8>-^`O467uJ_coJ4Q zL^{`e%#*OpAtFbA%ag4oWF%g2VTgOsRZ&>yamE3qMPr|x~i2V9}o`i)D z5sCKkB&>Xh$N@j{WOoVK;51Ld`iDs8P9=tD)Lk7E-5)6V2S(#{$cWnQQQ?Fg`hyRVYN zxoFnQPK~TX$UTJYVQ0y0o89{Aa#VPjh=Nm3I8UXzjtHsx9QB$*q^ki(-KmPGV2&E< z6j5*=18Y}uxQJ@VQRjpdJRZdAdOt#>%g#{`Mv5p{@&H}C@gHmxqgd+T&V(jgxz!|S z+b~SB7PM=`=Atd;;HaQz5e3hlvbw$yQcjL~rm;v@I7iKn5m6Bw)jd{3MRJrgPDDj< z)L|jjh@)PO7wL-Ts5=rw6x?^mw%CwFmXfttV@`LIBsLdqu^5i}Q%J>f)VobYy5cwr zH5F0u9CdZFh)Uq7w#`ITB1hF~E~1h+YM+p5!ckAR5b0{lQL|D+6s#t)E!HiSrDQFZ z%;|QtWOLCLYsOJO3905B^-7vZR|}51JzYela8z-Ih=O~s*rPhBm56G|QD=ly8b`g8 zDbfWit!#<+w-!+u9CcM25d~|aY>TyN%TlryYsKlV*^bRcTP%~KJ`+-{3AGT0QM^6b zp4Fu-Z@=#zJ=8bJ;=*V`Rg-LhP8{~Vj^zD~a6f@kvC zT;n=>=gL&+BQLOSy^~x<$jD~r9^jWryPRGmtUSQ>cP>wU&5`dCGC7|mkAIXF39|{d zbV{Bm;K{uba&uRngex2(oi}yo$xkF?m!96Fw}+^Wb$feLX_22v^nKEYCqI{v5B1~8 zeG+nP5l?<0A)5~1$uA}3@qsM4Y_GW9uN1QswBpwieK!u_$!{cN!7!fO&ym3-F6)e7 z$yJ}GMIMllk)wF>poDBbh9|$3kXcuFlinVpHuk#Gn@WrPPNHx4RXq8iCx4KT z^RD5^A32gn@Z!lV2`%{(N75SgpTd&a`^5FWVj4?9+Z>kYn=ykYk4VUyujk3567t@e zJb6q)K0cc#f02+c&gIEpCFC3PS+d_(Vh??M6H9@Ij!X1?w}2;4a3pQTQ#Z3DwBkvQ zq^mLovi%C4{7XXiKrC5$(7WEW6^A~^QqYPQB>G}jvLtxuZwZ<92v7baA#+xV z$P;AThS|etat*)mF?ROQ;QLJVq<~ug1W%n3Q1zeTsnY`L+UI%dcLB9)9Z&rspqjtP zQ)dL!T`zN#>+~Uez>!z^)N$Y01G;bJQ$N9}PjB;1?H&;LrguuFYt#4kfbs9}sqsJ9 z1KxU{Pj%tc_z(HifFJDvH}Bz7ufwT_K4DYW|JXesXCIq_+s!rLCwsuEulUqAaq2(& z`P5cF+XF@(;#1e))U7}8siBAM0g;FK)LU@s++%F&w!_^6zWj|%0jtIxu?Ms}#ixFP zQ_&xMYW`7sz?rjr>L#2z=mMV_f6N~6QYo7{`dIgX&;XTLJA?8Aez6D42;@_*!>N01 zeCqc&HLV_>I^b7(z_MWP)U?P_$&vS4sLHJI!O;v0e4_d5KVd9ca#Gxnysfen^dnY@ zz9r#286Y7CM0%46O+IDTuEyb9b}~DIp5kTv2U|oV%?F9D?9-VlJOodd#9+Dz+io|& zEL`q9?NsTb?{G2#i+iv=7tI=g9|UUh17pBQYyiAc2=BBLonq2eZNa(l!f2vv+3%Su z0AM6ocoliXG9-=wr5MNnDc%5hnrN7n#5Uv9KQh%vHOBo8Ir`*ybTbuh&`NY2#6SyT z2yn{@+^q$x2A@l{yp>2kWU6r`wm*d!0b*MDeO|et^1GN^a39)uqD?cMd_H{9TyU>3 zI0AC*B)OKQu$DquJDIZJ7Cg9mga2t;9@!5|Wplx~V)L_36&@`G67<6zJ+M8jBUfS) zZoH!;t@Sf4S)IqvW@;2Y+fFAf_?j05_P)p1dkrp;KCBAiv_`W*QI57^!wKv?cFw7m z5F1%?X(mq&|I4Yuv#T_i;hr7X(pt0jg3g_oyp7R$g&S1z+VDEF&O23jeii@Iyah8o z*q*>-*~V?xEPc0ZW&se4TRj* zjwjC(lCI#uxfETMVzebweF+x;w!*Qq02VCqwzF3m zs@;gW;+Vy$!rNuQ8X(E-fl99%SP~x1mtS?6Z6oo&^`>=%yWY)A6@LCQ%c@1s2%uBLdhh zK}G$nxNAIV39;ZCK2KWgmO`}Pl*Ng^El*RKZyVZpzB$C@(R|yZJ)};HUS~s{oo)k~ z=n9#O4Hm$6oVGMiqT>||baZ3c8IV|rzC3M-No`AjK@=qW;0MVE)13exW5m63f0<^et9}!S{?jqmkp+mV26>XjB2pLR=A<6)fXv{{EH-))c67dP{Rbq+wP$Z`tc6~|C+kZvZ{nY4O>g4g=gev1V9(*gbQHhE<&WD{v2MFyPX999 zQG>rtZ(`U#rZ+LN)a)j%h_%Xxb#n|mO)RE2F~w?56NMe-!tJ~6p8Vaaf?K}*XGrP# z`+EEjbeQdHnA5*ZhnZc|^d@!*G`)$vYMI`|q9D_oIM`--6GzrIr-{NIbJ*iGO7{GI zUC$+bhEID}Jqk+;iem05=ES<@^e@w6POWEp6K|+*dK2e0FujQjgH3PZq7c)YxU``; zO%(Q+VfA+XGWUb9f(H}O!6=}r9E{7bFB#+lQL!X7hZU)!+sarbn2PVM){*0Yhz{s(%@a|!13FVka& zBv+e=BT08FLiw)dS9?)A8mqmixxwqcp^M)Z*CKGv8p zct<{L>;r&nQS4xA6zb5|>I|DLkk3J}JsgeE?R~9b4my5lO5g?guoa_?eXR*LcniM! z>jC^ViN5+k_lRZ<%+2z!P5W7+>aphh1y7r1T3qP%e%AOTuL42)aIFko4SG1@n%HyP zvd(0GHXB}93mdB+R~N(Rz%)m#zT0QyXmTYrCx}`^_K>bUCc6ed{VS~t{02x_UDg(g z>Jo)c7FwOSLbZ#m3EEF1xyvN~uORzk)V;_WrGHEp>{%i+4sOsbD}NPMp!a1I`k_cG z|L-DeQi;x?cgu9&fCUdx;|K&_HLpC0y|Pj!dt z?{WnhUl`+f&~B-ZECa2f8SIsyx{wbFyB9v-O?%cC@7aLV0zL{|F;J`Uyn)t8YoP^g z9HTn5N~BKEatg|yN~*ysTZ{JofTPx7OsD;L0M`;{vO3gAtFNdC?q54$0Mu_!z2e8jI@y>tqnuH zN&;Q@n4kW1V5rxjM0&#cBeh;;dV7bx7QEOZ4ZoCO15S(9Sq6vAlf*Ljn<;E|V!iuo z*d65S0YX8J@3liRj=rV=B-)|$17XVgL3r0im_eWY+$hb`H%D0$OHPv9x+7F2Cg@xM zFA9c@@413saHsQQCr-_Ixa)IbA#{^tj&_Y8h3Pus&x<=|&m>uO1v*GBu61Er_~~e? zb3&L*tFsU4_LSHlMX8^Xk?o!t>+7h%FJHmN7FqV95t}5S7JleRKqvAYHPQ0kfi`tc z&NP7d+kul9pE;sAno$mZZZVdE|ul=9O(`0>NC9`#PULwJhvk=@}eagl=& zjx?>;h2bY}>TE^Yx6ULvbbYJzj16X29&?!Ta8Z%&xtBqI|8STCPUP^&q?V+o`O_>o z{gjx6eSw9%RdD91kO6bDgE1@JpJu_dQ)(6njm+w(GG=A?(=7P0N~u{OG%~BMlQFB6 zKh1()#*~@`LL;-jVa>|)r&;jQKB-wCl$+HEy%6qDJt5!PpJu^iRB9FoDXWYyCwcCPsOiCjjguGQ27ywBpzc{+A4Kw^Y_P|tJY8D82v)Hj`cw@#a zzm7d{CMPuughpmHk73O6Ya_zBkkl*?8ku#THOsGK51hhD%>tp3SvzAHZ~1lXQH{(3 zp^;fD;~2C2I`+UX6-&JZLL;*##WQC4b?kwkMV6WcLL;-X5*V}mI`+V#g48S!8ktom zkul4!V-H-+l9~lVBeT9{&GPHm6J=x;2#w5oK1uVIHum_ne}Id1QfGmXw~8Hm<~Av7 z!&ZJBd*Gs>)GQG4X0c;W&!&u7ejR(@ceTz z_yuyQw?JrQ*6Ym}Z~1lXi8C?_ghpoF)toWQuVYWVky#)#GHX~1#w@>%J@8}h(nbWK zky*`B7_(~Q7Q!n)v2#w78AeHf!U&o#%MrMJ~$gGE2YG!F; zk6#bOrbbqQkhh8*dnTrpIV;nzV^6Y?Ss>)iV#l8L>5N%^9ebJ?nFT^4vuqiRS$-XR znj4t~LL;-jWXtv@|jcghpnCw_(ik>)4ZKWEKdG%sRrF<=3$%-N-Bu8kzN4TgF>{9eXm2%mSg2 zS$DS6%+kglzeap3Bdb8jTg8q&L)uekMaZx3m;V}6rjbn`)Y$}g2$6NbrX4h!ayvL8 zvi`+&F;w)9!*26TVYIJCw>q447aNl0h={vru{GT{Iu)a_S&oOt6yh3rZPJT#LyQ~) zLV;tbU*>eAZRITS>j*Q{$S4r%jAB-8dvq#`Yrl4x!;Gu~p^;V2&eW=Kf5!E2Bbz{| zvx$l8pFK9Io&HsKiKb_JMK=B_yyW~!Hg$qJ>{2?ywzxvD6_Y)DhL%_0HXG=MVZ--b z<~W+-+bEUzVn5!P}0YHqsww zfuD_o?N0a3>xP>Moy^j{~id$V>Y z4O%eP8q}Hg0#ZdU_@*d@D&qH3N&frD9(HRHy<@0ByX`assR@3uF(Gv=I13|-3C8>a zPyTX}HkIskH&Qj13CiMOW`0>bz)!^)n;o3*yVbLC;rEj&vG=>Zf%bNDsQUZe-Xz8f zYTE4{BD;8nNP)m>(U0%&R?vncfioZTRqvrtY(<3!PBQvs zrNh>xJl?s{f8J^4dq<2m_QCU>O7O?3zK%NOIGS$qpEv%+uoPA~v69lioUu}33hy{7 z%{%3ZoCT)vjaoUr!AF&?%g}NR-V06P9cSfu2m0VBBk!PZ@x;*t)4lLY@kiOHZR8Vv zx_65yA||3zJcN1-E!QXor=?XtqrtbJSNx17qg>t0ZKiMxKGmyu$0V1XMV05Ug5?-L zg-&^+b#Oue+86&O!goz7!*|+DKh%%DgQaUfjfnpu?wVGP@BY5t;ZNVe3!|#ucgdCEyEs&T zmcyUNtjiR>YgReF!vm*3eRrQJdFL-@w)q5ztGCbsdqb};b$Wh0Srw}Vm;lPZ_aG-Yj6@*6I=~o9nYzhaqsze7) zUhG!~K4J<7W>$s+$ss&{sb3xVs3{!Sx^f(dN7!K1pUar5OyRvYmEpZugy(i4e)ZmJ zQ+Tg!<#-RDRruF?kD0=I?JCiGMt3=s_xkDMrtn_-%JH6dSkm3krGI6gP%;tPF3IorT#9?>o{uGb5+PK57c5b*c=f zm7RJ0OQ)r;HigqVSDw>iHn?(dX^Mh>Nk@Exp!RblVUIf+8bw>~+Md;*C{7j?wQ@WjBCZzO1-2wuLAJlzpizCV+dqy`Vgx+%L{UD_$ z*-ms9yPxxyK35rsetN=ijWF@-j&Tv+Jb3|nGi`&L3t(d(IDJifthg0CMn^a8$?w<6 zH)<>umOY?|RzGQqNXWsaUEYxpiwd5($V&S&j^HqP^N+ZM=I8HRf#15Q`t{S?>R>=? zo;MDL5 z^ttSVAr3h|H5?%l7uK8p*&AKE!CeB{R1|O~#>F`;6maS@VKd zPB!oELtI?aMe6h~OmUIA-(?>OvFNj}42O%vhkK|fCZtD2MZ)s0O%Vx&)scWQ4;V%Q zT5zDkCk}9bOVtk$a7)r=KoLCH2?M^)yc=dSV%)K9kj-Q|C!?j3%JUjcYDdJ+( zWybXqvskCk`LrW~llz%dwJRu!lcVT3RZ8y-N9iGG6=Xj}!Th zdhm0l)5&Wl*k7_+iym{R<@t^9ou=^K6%y|`!~Eqr+;1f#?YC*m^Wx)Ortr?V%JGi& zo6qI(&WHZsl9e-dVw)yVJeiSM?1^1086@c}v}ax_1}t zCrAR>>+~kd1igu6E|ip@P6+EmzOvOPjnz9=4Ym}bk+Eu)WhB~@sD|4fCRz*8*t|e{ zO>n`r@Ua!z8K_&6q{-q@coVg2Rwp9T^#@mDL5NH^4hvRmorV55RnguiYLDn(S>Ee#E8Hv1JGH4g zG^_#1D~R25P1WG&mt?xS5DG zkQFCBLwm=CMGkw-ky!mLsHmqSG@9)s>OyuX`7nIITU~us|EonEYS2Qpg~6>Fu(4HL zgklFNcT&SkbMs z?*8Pu{ijBsx1b#*Az>)z+K`&)>vXlAL+LIk{Pr@}YcF>7DlSx3l>jVQNQG#5nrhEK zL%)(j;H%ab)xsYfG9e5xWrHDB6s14DqR?k)+Bo=Ini>=DWgw{4C!q9nbsqN33+cG0 zDMUxo)$7W>J($6M>(Pq+cFybDvzZ=Qw5Je*7d$@D+W5>9pC#2yUr?L_M#JXO>HexH z2D%G-NOfx~VGm-t{ym_#k+vvC8``K*S-NnA_L0t0Y*<*Oq&qkWc~4idbib@ zXnb2WuG>jcA-0>)vZ}-$UCk$v90J8h+iIi6`L=3IlAYFvk1;s$^i%}pw!_o$GSf%3 z6GzpWcACjs+o_Gll;7kr#ALox3GPUYqca`&x&H%qL$C`;3QO_rK`36bXW*}LEkuN(VY?d~qunZ1-WhOWit;2~g|+s%4E zFDJQNo0oS~>sTy_?h*D`ghyCMKxye%cEk;T4672`TXf!{Mmzwe3?>@cXxpSkTgXOY z#UirLLHC@oIBni9vTYMfq|%rW_eyl#rzTq7JJ6<{$QG~tXg7#<&c>aW{IIZj4hxw2 zIg_NWos0cZ=E3)})qgA&Z;w5J39-FeFWD?lZAcn0Us}p+tm7WyRo%1*(#uSEAWtpj zOhDTU)EZ%88@zlLR3m5Ec5N(o=5HXy>4Ir47I^wK0zrFehkqUXq3lqc*hPG}tm~p3 zF7I|x6H3b8%Qco+_Bi%ZA!89(r93X9VM4biR!w5BuYcwA0{OEW&*00fa_$I0^v4p zZHggwvS*4>A^!DlLZ8~%4^FO{Qvc_|xx}|{CP$WMN4o~P2f-C=cacg^-LcriyANrg zaoQm~0VMj9OMIRKOI#cJsJ8qkNoL*QU-Q}3oyW+8&^N$II&6c`g+8ith|a75Zn1ty zhox}XbUAJ&#j(pTZ)qpOsdTW0eA)yK?mL3*8j^>d2e(5T`>Ij>77~$eNNRxvFA|}y z-uUDA4Wsb1yOrl_ z(G!l)+S#qi_vg{{rvt<4-tlJ61+AuvqMgBq*3N0q=CIpf$b;QrSGv<#0s6d1t>0TmUZW@7DakTHF%_WF>8dkZr!k7k$=2Ch80mOX*5t55K$VKy{BD8D(w@glKSS-uZ2x_!o_G1B`$ty5ImN_A8xKxDJ z4iuW=IcZ2CS=~YI$azknY6)CPaIbnz#`cva(>%mj*tzE}%Ie&|NuuBgU!50Da_ag9 zSePp!LQi0$jg!&7v#E=#_Ax>BQKm}+7&Zr*-BB`A0 zJ-aqCK{w)E_5f+4^}Z#az%p73S;mnvK`WUcUm^&eC=?9GsdI!9MG2lO5hxQ$ee|)0 z54HYOh+?kBgTV^#ukgq`Y_;6d2Z{7qG-jC9ZmUhcy1Q)K)1I$*9I)GdcV|@&pwUCs zc*{JLJ_XO8f2FBpZ^iq4+87&0!yP2r82bfbj*&en=NIpOH1!ZMwWwq^5AD8t2tq<8l`R@?;l$u#C?$cZ<3o(#TOETH~U@E~!S*-V|kBs|{nLuT`D4J4q@TP+AiWgV4el)mfsOy^Nr8viIzI%LKh- zf}RpVaGXa#mxM@yGTS4eDeisnEQvt5LLvz6Arr6@yOsoU?>fn{cu)EC_wrQF!#oqO z>UkIrqAHk&!CVVAcFvcEEZ3+}wo#sbq1I$IUO#!=Jda_#sl5;9PQhc1KyRHTqnU?rGwOyLxwuCfPjGMD$Og8mX_y2ZY;1wQ0*dy|XiA z#hxMpov|P8+SO?Y(c&o;!w0p=X*YhNseDTm6_UNqEw>1ieW)qR)+zglD0!IV(KWyx zJP&)ytBy@_{verzRXIz92T;f)Q3UKF@^e%PIL@A(LB8s0^AF|79(H6kVOrkvw5llS z8c|^%lT0;e_0WQA@%fyKB)d%sH+%Jz zT64!`x0sp`_8Ex-cKjksuzp=d^-QI2rh!NHup=GJiQHaZ$9c&Ogy9cxC0!y2rV~p( zNAM^~4yBn)P-wZzEqaU)x-MQ({wjD}F6^F{CF^*g3#8{31GV)ee7}0oOtrvk35eY3 We8WubYX99c)p*gJgKy4Mv;Pk~Ejexg diff --git a/Source/BladeSmith/Shaders/FTemperatureComputeShader.cpp b/Plugins/MiceShaders/Source/MiceShaders/Private/FTemperatureComputeShader.cpp similarity index 100% rename from Source/BladeSmith/Shaders/FTemperatureComputeShader.cpp rename to Plugins/MiceShaders/Source/MiceShaders/Private/FTemperatureComputeShader.cpp diff --git a/Source/BladeSmith/Shaders/FTemperatureComputeShader.h b/Plugins/MiceShaders/Source/MiceShaders/Public/FTemperatureComputeShader.h similarity index 92% rename from Source/BladeSmith/Shaders/FTemperatureComputeShader.h rename to Plugins/MiceShaders/Source/MiceShaders/Public/FTemperatureComputeShader.h index fecad12..f7bf1f0 100644 --- a/Source/BladeSmith/Shaders/FTemperatureComputeShader.h +++ b/Plugins/MiceShaders/Source/MiceShaders/Public/FTemperatureComputeShader.h @@ -8,7 +8,7 @@ /** * 温度传导计算着色器的调度参数 */ -struct BLADESMITH_API FTemperatureCSDispatchParams +struct MICESHADERS_API FTemperatureCSDispatchParams { // 体素网格分辨率 FIntVector VoxelResolution; @@ -42,7 +42,7 @@ struct BLADESMITH_API FTemperatureCSDispatchParams /** * 线程安全的温度计算参数队列 */ -struct FThreadSafeTemperatureParams +struct MICESHADERS_API FThreadSafeTemperatureParams { FCriticalSection ParamsCriticalSection; TArray PendingParams; @@ -52,12 +52,12 @@ struct FThreadSafeTemperatureParams }; // 全局参数存储 -extern BLADESMITH_API FThreadSafeTemperatureParams GTemperatureParams; +extern MICESHADERS_API FThreadSafeTemperatureParams GTemperatureParams; /** * 温度传导计算着色器类 */ -class BLADESMITH_API FTemperatureComputeShader : public FGlobalShader +class MICESHADERS_API FTemperatureComputeShader : public FGlobalShader { DECLARE_GLOBAL_SHADER(FTemperatureComputeShader); SHADER_USE_PARAMETER_STRUCT(FTemperatureComputeShader, FGlobalShader); diff --git a/Source/BladeSmith/DynamicIronBlock.cpp b/Source/BladeSmith/DynamicIronBlock.cpp index b3e367f..dae9ba0 100644 --- a/Source/BladeSmith/DynamicIronBlock.cpp +++ b/Source/BladeSmith/DynamicIronBlock.cpp @@ -13,8 +13,8 @@ #include "GeometryScript/MeshNormalsFunctions.h" #include "Materials/MaterialInstanceDynamic.h" -// GPU计算相关 -#include "Shaders/FTemperatureComputeShader.h" +// GPU计算相关(从MiceShaders插件引用) +#include "FTemperatureComputeShader.h" #include "RHI.h" #include "RHIResources.h" #include "RHICommandList.h" @@ -23,6 +23,8 @@ #include "GlobalShader.h" #include "ShaderParameterStruct.h" #include "RHIStaticStates.h" +#include "ShaderCompilerCore.h" +#include "ShaderCore.h" ADynamicIronBlock::ADynamicIronBlock() { @@ -39,7 +41,8 @@ ADynamicIronBlock::ADynamicIronBlock() void ADynamicIronBlock::BeginPlay() { Super::BeginPlay(); - + // 初始化 Box 网格 + InitializeBoxMesh(); // 创建GPU缓冲区 if (bEnableTemperatureSystem && TemperatureGrid.Num() > 0) { @@ -57,6 +60,12 @@ void ADynamicIronBlock::Tick(float DeltaTime) return; } + // 检查GPU缓冲区是否准备好 + if (!bGPUBuffersReady.load(std::memory_order_acquire)) + { + return; + } + // 累积时间 TimeSinceLastUpdate += DeltaTime; @@ -88,7 +97,7 @@ void ADynamicIronBlock::OnConstruction(const FTransform& Transform) Super::OnConstruction(Transform); // 初始化 Box 网格 - InitializeBoxMesh(); + //InitializeBoxMesh(); } void ADynamicIronBlock::InitializeBoxMesh() @@ -684,11 +693,11 @@ void ADynamicIronBlock::CreateGPUBuffers() ENQUEUE_RENDER_COMMAND(CreateTemperatureBuffers)( [this, TotalVoxels, BufferSize, FlagsBufferSize](FRHICommandListImmediate& RHICmdList) { - // 创建温度缓冲A + // 创建温度缓冲A(使用ByteAddressBuffer而非StructuredBuffer) FRHIBufferCreateDesc TempBufferADesc; TempBufferADesc.DebugName = TEXT("TemperatureBufferA"); TempBufferADesc.Size = BufferSize; - TempBufferADesc.Usage = BUF_StructuredBuffer | BUF_ShaderResource | BUF_UnorderedAccess; + TempBufferADesc.Usage = BUF_ShaderResource | BUF_UnorderedAccess; TempBufferADesc.InitialState = ERHIAccess::UAVCompute; TemperatureBufferA = RHICmdList.CreateBuffer(TempBufferADesc); TemperatureUAV_A = RHICmdList.CreateUnorderedAccessView(TemperatureBufferA, FRHIViewDesc::CreateBufferUAV().SetType(FRHIViewDesc::EBufferType::Typed).SetFormat(PF_R32_FLOAT)); @@ -698,7 +707,7 @@ void ADynamicIronBlock::CreateGPUBuffers() FRHIBufferCreateDesc TempBufferBDesc; TempBufferBDesc.DebugName = TEXT("TemperatureBufferB"); TempBufferBDesc.Size = BufferSize; - TempBufferBDesc.Usage = BUF_StructuredBuffer | BUF_ShaderResource | BUF_UnorderedAccess; + TempBufferBDesc.Usage = BUF_ShaderResource | BUF_UnorderedAccess; TempBufferBDesc.InitialState = ERHIAccess::UAVCompute; TemperatureBufferB = RHICmdList.CreateBuffer(TempBufferBDesc); TemperatureUAV_B = RHICmdList.CreateUnorderedAccessView(TemperatureBufferB, FRHIViewDesc::CreateBufferUAV().SetType(FRHIViewDesc::EBufferType::Typed).SetFormat(PF_R32_FLOAT)); @@ -708,7 +717,7 @@ void ADynamicIronBlock::CreateGPUBuffers() FRHIBufferCreateDesc PlasticityBufferDesc; PlasticityBufferDesc.DebugName = TEXT("PlasticityBuffer"); PlasticityBufferDesc.Size = BufferSize; - PlasticityBufferDesc.Usage = BUF_StructuredBuffer | BUF_ShaderResource | BUF_UnorderedAccess; + PlasticityBufferDesc.Usage = BUF_ShaderResource | BUF_UnorderedAccess; PlasticityBufferDesc.InitialState = ERHIAccess::UAVCompute; PlasticityBuffer = RHICmdList.CreateBuffer(PlasticityBufferDesc); PlasticityUAV = RHICmdList.CreateUnorderedAccessView(PlasticityBuffer, FRHIViewDesc::CreateBufferUAV().SetType(FRHIViewDesc::EBufferType::Typed).SetFormat(PF_R32_FLOAT)); @@ -717,18 +726,28 @@ void ADynamicIronBlock::CreateGPUBuffers() FRHIBufferCreateDesc FlagsBufferDesc; FlagsBufferDesc.DebugName = TEXT("SurfaceVoxelFlagsBuffer"); FlagsBufferDesc.Size = FlagsBufferSize; - FlagsBufferDesc.Usage = BUF_StructuredBuffer | BUF_ShaderResource; + FlagsBufferDesc.Usage = BUF_ShaderResource; FlagsBufferDesc.InitialState = ERHIAccess::SRVCompute; SurfaceVoxelFlagsBuffer = RHICmdList.CreateBuffer(FlagsBufferDesc); SurfaceVoxelSRV = RHICmdList.CreateShaderResourceView(SurfaceVoxelFlagsBuffer, FRHIViewDesc::CreateBufferSRV().SetType(FRHIViewDesc::EBufferType::Typed).SetFormat(PF_R32_UINT)); UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: GPU buffers created (%d voxels, %.2f MB)"), TotalVoxels, (BufferSize * 3 + FlagsBufferSize) / (1024.0f * 1024.0f)); + + // 通知游戏线程缓冲区已准备好 + AsyncTask(ENamedThreads::GameThread, [this]() + { + bGPUBuffersReady.store(true, std::memory_order_release); + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: GPU buffers ready for use")); + }); }); } void ADynamicIronBlock::ReleaseGPUBuffers() { + // 先标记缓冲区为未准备状态,防止Tick中继续使用 + bGPUBuffersReady.store(false, std::memory_order_release); + ENQUEUE_RENDER_COMMAND(ReleaseTemperatureBuffers)( [this](FRHICommandListImmediate& RHICmdList) { @@ -840,33 +859,111 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime) return; } - // 准备调度参数 - FTemperatureCSDispatchParams Params; - Params.VoxelResolution = TemperatureResolution; - Params.ThermalConductivity = ThermalConductivity; - Params.CoolingRate = CoolingRate; - Params.AmbientTemperature = AmbientTemperature; - Params.ForgeTemperatureMin = ForgeTemperatureMin; - Params.ForgeTemperatureMax = ForgeTemperatureMax; - Params.DeltaTime = DeltaTime; + // 准备调度参数(捕获到lambda中) + FIntVector Resolution = TemperatureResolution; + float Conductivity = ThermalConductivity; + float Cooling = CoolingRate; + float Ambient = AmbientTemperature; + float ForgeMin = ForgeTemperatureMin; + float ForgeMax = ForgeTemperatureMax; - // Ping-Pong缓冲区 - Params.InputTemperatureBuffer = bUseBufferA ? TemperatureBufferA : TemperatureBufferB; - Params.OutputTemperatureUAV = bUseBufferA ? TemperatureUAV_B : TemperatureUAV_A; - Params.SurfaceVoxelFlagsBuffer = SurfaceVoxelFlagsBuffer; - Params.OutputPlasticityUAV = PlasticityUAV; + // Ping-Pong缓冲区引用 + FBufferRHIRef InputBuffer = bUseBufferA ? TemperatureBufferA : TemperatureBufferB; + FBufferRHIRef OutputBuffer = bUseBufferA ? TemperatureBufferB : TemperatureBufferA; + FBufferRHIRef FlagsBuffer = SurfaceVoxelFlagsBuffer; + FBufferRHIRef PlasticityBuf = PlasticityBuffer; - // 保存当前bUseBufferA状态用于Lambda - bool bCurrentUseBufferA = bUseBufferA; + // 创建SRV/UAV(在游戏线程创建,捕获到lambda) + FShaderResourceViewRHIRef InputSRV = TemperatureSRV_A; + FShaderResourceViewRHIRef InputSRV_B = TemperatureSRV_B; + FShaderResourceViewRHIRef FlagsSRV = SurfaceVoxelSRV; + FUnorderedAccessViewRHIRef OutputUAV_A = TemperatureUAV_A; + FUnorderedAccessViewRHIRef OutputUAV_B = TemperatureUAV_B; + FUnorderedAccessViewRHIRef PlasticityUAV_Ref = PlasticityUAV; - // 切换缓冲区 + bool bReadFromA = bUseBufferA; + + // 切换缓冲区(下次Tick使用新的缓冲区) bUseBufferA = !bUseBufferA; - // 调度GPU计算(使用RDG) + // 调度GPU计算(直接使用RHI,不用RDG) ENQUEUE_RENDER_COMMAND(DispatchTemperatureCS)( - [Params, bCurrentUseBufferA](FRHICommandListImmediate& RHICmdList) + [Resolution, Conductivity, Cooling, Ambient, ForgeMin, ForgeMax, DeltaTime, + InputBuffer, OutputBuffer, FlagsBuffer, PlasticityBuf, + InputSRV, InputSRV_B, FlagsSRV, OutputUAV_A, OutputUAV_B, PlasticityUAV_Ref, bReadFromA] + (FRHICommandListImmediate& RHICmdList) { - // 暂时简化实现,跳过GPU计算 - UE_LOG(LogTemp, Warning, TEXT("DynamicIronBlock: GPU temperature compute temporarily disabled (API compatibility)")); + // 检查ShaderMap是否有效 + FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); + if (!ShaderMap) + { + UE_LOG(LogTemp, Error, TEXT("DynamicIronBlock: Global ShaderMap is null!")); + return; + } + + // 获取Compute Shader并检查是否有效 + TShaderMapRef ComputeShader(ShaderMap); + if (!ComputeShader.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("DynamicIronBlock: FTemperatureComputeShader not found in ShaderMap! Make sure the shader is compiled.")); + return; + } + + // 使用RDG(简化版本 - 创建临时缓冲区) + FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("TemperatureConduction")); + + // 创建RDG缓冲区描述 + FRDGBufferDesc TempBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(float), InputBuffer->GetSize() / sizeof(float)); + FRDGBufferDesc FlagsBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), FlagsBuffer->GetSize() / sizeof(uint32)); + + // 创建RDG缓冲区 + FRDGBufferRef InputTempRDG = GraphBuilder.CreateBuffer(TempBufferDesc, TEXT("InputTemperatureBuffer")); + FRDGBufferRef OutputTempRDG = GraphBuilder.CreateBuffer(TempBufferDesc, TEXT("OutputTemperatureBuffer")); + FRDGBufferRef FlagsRDG = GraphBuilder.CreateBuffer(FlagsBufferDesc, TEXT("SurfaceVoxelFlagsBuffer")); + FRDGBufferRef PlasticityRDG = GraphBuilder.CreateBuffer(TempBufferDesc, TEXT("PlasticityBuffer")); + + // TODO: 添加数据复制Pass(从RHI到RDG) + // 暂时跳过,shader会处理未初始化数据 + + // 设置Shader参数 + FTemperatureComputeShader::FParameters* PassParameters = GraphBuilder.AllocParameters(); + PassParameters->VoxelResolution = Resolution; + PassParameters->ThermalConductivity = Conductivity; + PassParameters->CoolingRate = Cooling; + PassParameters->AmbientTemperature = Ambient; + PassParameters->ForgeTemperatureMin = ForgeMin; + PassParameters->ForgeTemperatureMax = ForgeMax; + PassParameters->DeltaTime = DeltaTime; + + PassParameters->InputTemperatureBuffer = GraphBuilder.CreateSRV(InputTempRDG, PF_R32_FLOAT); + PassParameters->SurfaceVoxelFlagsBuffer = GraphBuilder.CreateSRV(FlagsRDG, PF_R32_UINT); + PassParameters->OutputTemperatureBuffer = GraphBuilder.CreateUAV(OutputTempRDG, PF_R32_FLOAT); + PassParameters->OutputPlasticityBuffer = GraphBuilder.CreateUAV(PlasticityRDG, PF_R32_FLOAT); + + // 计算线程组数量 + FIntVector GroupSize = FTemperatureComputeShader::GetGroupSize(); + FIntVector GroupCount( + FMath::DivideAndRoundUp(Resolution.X, GroupSize.X), + FMath::DivideAndRoundUp(Resolution.Y, GroupSize.Y), + FMath::DivideAndRoundUp(Resolution.Z, GroupSize.Z) + ); + + // 添加计算Pass + FComputeShaderUtils::AddPass( + GraphBuilder, + RDG_EVENT_NAME("TemperatureConduction"), + ComputeShader, + PassParameters, + GroupCount + ); + + // TODO: 添加数据复制Pass(从RDG回RHI) + // 暂时跳过,需要实现正确的缓冲区提取 + + // 执行RDG + GraphBuilder.Execute(); + + UE_LOG(LogTemp, Warning, TEXT("DynamicIronBlock: Temperature compute dispatched (%d x %d x %d groups) - Data copy not yet implemented"), + GroupCount.X, GroupCount.Y, GroupCount.Z); }); } diff --git a/Source/BladeSmith/DynamicIronBlock.h b/Source/BladeSmith/DynamicIronBlock.h index ffe82e4..8df2a11 100644 --- a/Source/BladeSmith/DynamicIronBlock.h +++ b/Source/BladeSmith/DynamicIronBlock.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "DynamicMeshActor.h" +#include #include "DynamicIronBlock.generated.h" /** @@ -258,6 +259,9 @@ private: float TimeSinceLastUpdate = 0.0f; // 距离上次温度更新的时间 float TemperatureUpdateInterval = 0.1f; // 温度更新间隔(秒) + // GPU缓冲区是否已准备好(线程安全标志) + std::atomic bGPUBuffersReady{false}; + /** * 创建GPU缓冲区 */