From c8bf64701441daf8e7465de6bd84b0b77d0fa00d Mon Sep 17 00:00:00 2001 From: Andrew Cohn Date: Sat, 29 Nov 2025 16:30:47 -0800 Subject: [PATCH] edge relax go brr --- fallingCand | Bin 90624 -> 91000 bytes main.c | 54 +++++++++-- sand_hello | Bin 81768 -> 0 bytes sand_sim.c | 188 ++++++++++++++++++++++++++++++++------ sand_sim.h | 18 +++- shaders/sand_display.frag | 21 ++++- shaders/sand_paint.comp | 42 +++++++++ shaders/sand_relax.comp | 135 +++++++++++++++++++++++++++ shaders/sand_step.comp | 125 ++++++++++++++++++++----- 9 files changed, 519 insertions(+), 64 deletions(-) delete mode 100755 sand_hello create mode 100644 shaders/sand_paint.comp create mode 100644 shaders/sand_relax.comp diff --git a/fallingCand b/fallingCand index a89e0c5ea9bfedca7a321bb2969dd5e42653e1b4..61939f4962cafcea89d5720b6f0e5e8c06a1895a 100755 GIT binary patch literal 91000 zcmeFadw5jU)jvK2q7fm9)@Z5-QyVl|xdZ~ndqP5(v;k6sKot!knIt1aCQc?06a^(9 z(|}Uzg{oDm6lt}JwrZqSf+#^>FL){9rIK2!lX|1tV(M*vYwfkxnSItw`o91Cp6B}q z<0I#MK6~x8*Is+=b@th3CTIE7%JQ^9gFNyX>^aYaXk$Jx#az&JaYClpQ{>6?6njQ_ zMtTl|@*(iQ$mwJDU?_x-95V=dkj8p{;BtK&!E$nx*71n-2KrPTMJ00dWD~}E>3HhJ zf8gUa|2oHi&LAvDyPj#Uh2_<;tb}+u+V#*kTmMAmTVHS=ySd*j#}hC0(g0$;-CS=s z*OTJ`>qn@&78;tB=J{hM!{dug9b7t>=3Bn8)&R#5VAQNBlqUAwj#kE zH-P-31Mp7|V9&J!@LvqTUpRn%j59C%uRntakiT;P{^bGq(?PLUD+l}?@Xtvp_(=ok z$sfR9+<6#5nd13%PH3R6mX8`%?0ramMKz{fD@(lyX7Yra@GyoqNKu_5K z{K*6GIH6PdvwQ$Pa{#`70R8(1;LjgG&+i5pm*)r2^VR_J*#pQwFaZBg;E(bg?^*FO zD1?t(7d$mJjf+CfHLa1_aHOWjQ&TZ(dQH7Q>~9RTM*QJf(@TS)X8)|(1wlWRr7UR- zHY}<3M@rkmt)XyLsI{~<7+g?Wx6p$H)BKUDaHy`NzCP@4Z8cQ&lG>IjZ4C|nFyX3T zZKNR-UR2%I(h>?s{PiT|k4z1RLt&S;>9q^}rJ?4Cza5(k`&T0?acCf2mJ*3~vQcp}&vP#c7@ zMKA)PIuLD0EH}XA_GYa$ORyaO8_e|w!&P6jA~y(DB1>T|%ev;KpWOn>g2Oy3nL8#B7>=uz>lt^M z@*$oLjQ?QpV9z~_+l~*w(k0FF0AT#cx>n}n`csMT;&rR=#msjr{y+!G&sXzWaMw#z zj%yjSekbVZws`3q7VWlpTt~@gkHzN*3CDdFZ!e5`E&d0VyyPFWWrD?f_7FH64v?yx zPlm;_%S>6e#q-kJ@VOR`YeD&VE#9o_xFFx+v2FQ`w|E%?EGx2jS%V^9Z1F>p;E&JZ zj}#J)RTe+Y;^$gC1eWueZ}FK9>+v*Me3r$xSUlg-nbLNPx37yUEdD!|o;4PKti`Xl z_~R^ogT?1s{6>rauElS$c)69s;;j~cf+gQ&@h4h*x5baN_}vyi%HsD}yw~FQS^W1b zzSrVUviO+Af8XK{SbUzv_gnmEi}&#S#TY!<;xjD%6pPQc_f2-RHpIBq)z@wC!-#QWZEW`)C1!D9M5oaUrHFyf*qY&>g z_$2|0LHDTzG8zvLyTKced7)O2V&gn?DHD@E@Iq@>dQ9xO~kkb z)#owz6~wsp)OX+;?EiC!y@-1aehM*eHTCT=cpGBeV(RNQ_+iAjwbZxO;NK#~Ev3GV z2H%Ysx03qS8+;pL+(PPGVepNJaqFnB#o(VJ#x0}1`37H$7`KZ0stmpYF>Vp{6$37r z<6C#`b=P@3C9_K|ESYs-H3WNm=cLO)sNUCk%xF~Pi_Xex>dg5GaIv?G%_PX`I0r(q zBHB~Au0F5Ww{BiunJ?PoTQ_Om6c9Qb6#LfYN0-M}b&>eg5>v(*7nknm`vNcZG&#`Q6C)R7CvMZdj zB~X@?pMC(kRT+IBrtU#F+m3Uvr!21+4xqm*Z+_OPWqDP=lpzz(c^tk{;)}jxxk0;tw5`89~lfTvDX~_EYmCv)XPn`QB$UphdW#vB=&+&jAv5DXV@H%wQ zY}>=pY}fjFhM4Wj%4mJwcwcm0UeRY+N0sGe$6kfeS=F7j>TqbXpF%zx%VSd6jwDMmXcMtnBprR(m|3WnHr4v#hy8@*1+nrQdCcqlV;#&$sG>*}s6E z#)g!eF08}zrfFC;J1-A(j$R1Wp!(=Ks-BnE`h2Vh+AKJTF*63Nitb$5KPYnClQ@3B z6}%NY4p`u_Zrb_9TR0SzzIBm2AIhHyH9KeJ`OeOH@IDx|%Dl>~j>pr$V>x#L#<+_P z{~4Ucb6&;r^eFths{87O&YUap?3JtMfkU5#|GPiWIt}`KQTkx8*tahIHSp=@S(om> zVkpY^JnJWN{@my2!Cb6_6`pfT`i7vW5g7pkmGk=wRFqwrHzN8DIA@uW&W8H2lJg;w zJjH0-;_YZ$?6>gm@b}^0vgoG}u=$-3z0u0N)8RZjo^vY990-m#uQKKFoS!{r@au#@ zCNG|I6>%>TR}$S7&$*1KokBew&zVEiADx=scupyCk1z+}Rsan6HZQt!)hq2Iu)FAt z=cEVwHdSPdpym0{;|re|VCw zbNIQxfmuEhS~Z5jc%O4EOn@&ypbNv<2mWlT_x*tKY}@x?UD1B=B_;DpE-AURq-JN< zD9@S0-`xT;d)3)sPhTbc6FUtmC*~)vPYOP+h`xNOFZ!-;<$DLJW=$>VE_fE2`W|#8 zo^!&z9?!oL@;DzsAq>uqcsR2^k^}j1?`AlMrOgrX-mFn8z%UQAZH^!xF!GZY;NjG7 zebEEH9iN=<+tEMRH)xmdX$fP!DLf{b`?L)NIWM18bx#kuD~T~FJ@3wd4Frcc|(_{W5g>tgKV^4~KqBfR`!#>Is9L!veM&Li9-xQK8K z1e+;mJPmTZ!DmwWIE9ZBd={0*?`Qcl2%j(Ig@jKLTtGM^_Mb|48bqII?_|Pp>F;R5 z_X<9V@auxTgq`+A5S}UJxrCQXdq=ap;3Ek)LS``Sn=Btq&l3FgROA0I9_8|W#-jf- z!g2BE0m9qGA0H9EL2!)gi~jcshhT|k+JBGm9KnAje1`b%9m1mozfJi3N4WkQg!lh} z@oR*81n(hyuizI6&lLO|;nyX8b`!o!@biQl1+%`Hg1LRC{9V>Bdp%S$?cGTAzmWOP_yg(hS}NZz^Epb`v8RKuGv3#5`9rLq%g-0QhRR0?UPZV^ z<`3iRr2T8DJS6sAMcBzNR}j8V{K?qyH_JQmw3Os;mGN6bIR0z4_iDo9Onjj~PlPk& z&l|y56F=#WKQD%A#-Eo_{qx11Cc-^pe~>X`bt4}p{JP+F!jB1N`(77)%c%S=g_n!J zxco7he^-+Ha)pl*%>LRh_OXAK2$t~`jDZ7QSt9dqJ>igy=g$c{0``eMfiRwG@JYJ5x%x`I*=l2rPx0>poB6ua?*F}B>;p+tRd~@VEz8rau zXGcCl_0N#{G9T~e{&W59f?5A^!ON-sD8X#c_Fr**&L8&*X8Ugt%=u=CVD4{?V9r<5 z1hap}3GSr!j}y%PjBn)jdA@ug_y&@HN$}4IKPH&-?_Gj<{H_yxGs%Yp-%5CnU>^VT z1>Z*HrwHcxlqHzs>5IGA-g`)XzhIs}J%WEr{r`dFBOafS1%FETR>A)yTqF2jgzpvn1>q%vza%_O@Hd3xx3hitGZYxrkdzPR^}680 z2xkdCg7E87e>mY$QhpTSPo+GE@OHu9CA>s1-#^?ccoda~1b?6K9Koj$9;fhKo4G$@ zxxQQ<`F`j)SugVa&|MPWEI(8552$_|qTIx9KH&!i^Zn8sk(^Zn_k zu%%@DaW<9jm-$gb_yfs5GYHSz%<_yM6a7_GzFqY5{q}>Re-4%3D*ERWeo*FDBVlKL zHWR)<s+X2#)I2;^mBj4ZRT?J&nePB z?!Pl17E=44%Jn%w_zTI$tbe=2Z>uUlIO5%I_n*U+`}U-yr%PB<#fVBZOZU`9BiA^;Y)JlZ2Pt!uTn|HG-cZJWcSk zgvSYfp73#k_YjWX%=%s>{DI)t3BM%xHNy7_eueN2f?p!MMDPoQYXm<>c$(ndgvSZ) zVR^}yjHe0S#qtUtCz$&mzm5CD_yfUDQ~j3|eoQdiyI}H z)A|GM2lf-Lk@@&H!qWuD2#*u|cfw9SW_c$bS>JM*A8(UGX@C0_^Oj{{rP;lRw&Xao@lH zhC7yJtju~oVm)>%h6ipV_<O(NAOFc{d)fh(28jyUx*1 zDx+V-!hgk`^R~Uy2fdHaWHNl~D)WXxK|wb>q<9ik#fqT{YX<$PHodQ(c_KWqOCJp+*am0EIcG;WXJ5hCQR&Cd8LrQe zTlr;LR>x)Fl%Dk6PT@AEaBx<~g{FmjoWh%(v+JC*rA~z*S*uP5!@$*R<{7OQaZy&s zY=g^16dq1>OgGdxhXPyfG|lBY6j<;hLk)7MAz2-{hWf{5?nHljR>!}g*`D-Qnd(U& z1M)*~l%o4%|B1oVRyZE@5x6(~bC7{3EQZ-N6XMNWRxW}Qh?@hyVmya%x1>47MTDd2 zw?cj`>zuR+RKcU1td8|29?mTjeLtf5MNn?^R}$VJX}Fs8OPc2Py9C!#`BuRT3AYPw zBitf*IpHS4(R5sIm35Bs82dhU>}vrrW6zZ&zh)ETs|e4PGg^2nPvA(=S{t7AN*1MvD1P;2^s9qG@M z{w}ID`ge)F?9~?q|D5CxO8;+Wd=u;Y8R3y4&;FS$5yI_VD(!K^92Ea^1dd$C`WPPu z-7xm@47gPM#S#38_?z2n7Wo^Vx6OiOTm^IeV+FJRz1MSl z(tjzxjoLdX<=mfLf;Un5X2HK9yjbu9gl7x>%cUl6SBw9DN#*BB+;RD2!5@?SEfPmO z@2?lkk@ho`ow* zx{3GqK+N>#bCQ2i;^i~KV+DUh_!GIZenohs%+vo6o+0u4HQ_tOpN!9wdFg?56I{7Z z7Ce~oTDIp1!jDSc$tJv5+B=r;VNyPd@VDZR(S$#d@jI39F7a=W?0ZN0rUPfI*S$| zMR>jRri^gA;Aw>WrS~%lS4s3;Ot?$3Zynbc%-e}EiS8CE?-k6^+%K572M-92lKdXQ zHxb?{fe2Zk;CkXol^Y)cjFmK;w2p%=ZL_@Y<-tOy{Y{=U`^JR2+ zyDCF)FSmCckH1}s zhm+1pXF~U_U4u*zGSQqv(oacrGENhmOXdHN-W^Z)T8Z8h312371mQ2GKKDLN%DFd3 z6ON`o1cIHzZv}`eyN!pN%;8w~9nv;WS|3Sxgk*-VW|^^BCYg>~uNTa|$dlL|$MprD zP52mvhl&1TDle3=m_qn;!P5v26I{jmr2ZVj&OSmd;n$>m0pVp*AHReRnX+E+Lc(uL ztSu#cx{UoQ!sp1`{W;-6!FLcoM(`%WUrT@Asy6K%Be~@lEHAORS>gX7`H!yS=-Ei+ zuL=Gg;eQGKBjL9N-$(VI5WJJ{Inv)PB%devVZv+0zCRI;2<}n%Wx_!z-$(dgg0JNE z1^=Az+j8aGMe?r+ex9-TTjW=B|G7U;3x15`JEVRW;fT~{JWFy3_Xod!Y-VFO$^T2n zdnaLM{Mo)Ir2I81|AXLn3Ew36eZtEG|ATP7;7B^J$$b0+;bk%(pCr6V z?0=T<+N-$!tArzh-yu9ta3A3g!JiWj3jUVxEWt-qnDISFa1P--!MTLjN_?M0xI^$* z!pj7YBYcPSr-<+-h1Uu$rE({KR1$Xb!(6T}{#!tJt>DWD=SjXVtup zCc?7>UrxAQR@yCu|0Vhuzpe1o3g0i7_1z>mMEcH=_*_WX(bq`W(N{;<(N|A+t>|kY z91+a@nJ0J=m6r)_R`nMX4odkl!ow7H>My18W2iiueh&mjXU^XteBsd=KFtV#fXQ5a zGmPi&zMAoW2wx_dt(r8QML3#%1Wf6b=Y9*C(z0%VaPVv&JPNAj6u?mUI>)>T1^86! zyw3F3;k4i__&v}lc<2R&z{l6Q>@ObQ%AJE^NB6|zXP)>65Lx~Yd;pdP7@o}C3Iu-O zw?uGuWcqdRZ`RG-S&w$(k2{K-A9rM}dT}tQTlsXc{J_IJSL{i@loh~JvN>?N{Y11o z=W8^sE`2I6tGbte5A2)(r(kE>%7*pU^F#QZ$*uSk3E#SNoF&t> zg121q@cTCE{STmj@Msbq;l+Hrpvt1xoA&pG4?gO1FX0o z9O#Qyl26V&@z(&C-`Dp9{1TF$6XLV6kHC(e^x04}2+p!P-Zyv(=63rO~=RN7)LS`bV1J2pI&KX`esdSff*5#c2me2a$evhBKqFu#cPEYz?C?mW2 z4StF-{-CU*z)<%&R9e<57!Ig)6Km{AU+tW=J7I4&{R-;m(JZ;LAD$Yvf%cyCuUK$pKRh)I8tOeq%U(FckNLHX z6VK3p=HZ#BbGFSnyWcsxlh69T1B$Ksw>T1OoUv$cAU=>wD5)Vce7cw3Hl#2h03> zpq}(co$@Wt*=-{Eypb%!(Rfa{bKU*B!l@N>&MtM%s-)Jnpec6ePHfozeG>e9HuuRi z=m%u1{m@SAY!HF@xEAWcq-q-{g)d;?NGc3*;n5KN_}lW_osPdDE6}sCp4dYqd=?2u z@cY!w`@x9E;UD-d)oH*-pYpAWNB#)E{TmiL`f2mDeHi4ZBcTXCs5-jG`gK*q`skNX z_0}*>>VKLi_4w?zZ!t7Q^SNX;Sa1PwB^Opizrh~}$9}T|+QZG4_dq55vMvoZ!;gQX zJ7Y7T2s#GU)I)i!g=uCmeCUd&3Ppr2ZLrWA@g&+WM!4C^lVIQ9T3h)sqg ze4Qi4KdrMo0MfqzX{!DrREM7(x0_m@Lj94@hYxqf?uA3a$DKv7^WX$(G&(iT#s;H1 zpi(JR`tWwra~ZIG5e%#KC9|u2*L+ko1jQnG8TFN&h2|?BmC--s4hUEN881qWi0*b}85oF#AxgfCN!fMo6Ub#@qq zaKRa3T40-w)aCj(_?-OGWf+_$B!@ zA9=DQl z=-cQg+s6ZjAF{^o1`2|s1TJ7pz$mPFxGu(X#0K*okZ46pz2S z0;wVNr z5ZS(N(!J%zi}~O#^4@t*g7*SwEq>9)8jZeq&W+F%)IJhw!-dz(#fwUy>;C|8H2VTD zlC<>y2^4gm``2b{2Paq~wbPA?VSY71D8KB(iH=Fb@8tq}dyIlK~Dk>C2x#iovgreeo(#jRKo0tUX8^%$aS7Oav`+*t2fZv$fAOHY%;X( zqbS8oOPMkCS3&q~GTfy(_VxlFyBIXY8vbZHx(fed2y|)*O6H1W0sQ9v+^NhA z34U12t^fgaK?O>mB+?*@4>4ndjVvrjV~>L|u@3m>!<`N5LH@fa?-hBYeKT6}GRn3kofoHamdbrW62!MP7NhlSO2+W z79O64xe)ydE?scZhF|Dc=4C*R9tQasW>)8wxezTsg>J@k&S)3B4lrC@hs1Nf?{FQ! znP1jtJ5;BkAcubgIUN290sIXB_{D!l^qE!9wtv^OG^De7Naw`pOYxkCVZxi@A^5ZT zOt7o@X_KCQ0|ir8#ff^RED95DFHrE)_hInoD2Cxr(jUcS0e_vQvo*s6M$-gvNx>`d z*Crm{hBux$C$50@ONW?+)w-6EE4#C@Dh_lG-tp~_b?t=ucf_GL({Z+}d^&$Sj)Nq# zlA8GFbJ}3#2J=G3*ZIC8`gu9TE;#x6C3t)#>-rEJE21y>aDTN9&hc*qR7Agx&KZ(* z)U=G~8NPK(GJ2+AAWxjyNxpf=AuVg}KvMhW zI)}8Z2?JShkQ@IsT%0QE%7;|cok!U%>%o4!X1~y7z6EM>&_^)ER-F4TmJ`)vM=kwKd%{b?3r&Y-;b^O zRz3slQ@%j=>)1TfWr_ohzxvb$$+YK71KeZo^_*Wi6_VVDZCHd^;AOVbvar z#TR1n&kNu$G8_$F9TdA6FkD`qgl?8}UOJ@jd*7N1!=+gH2Ug`@V$-=GOzV#skLdLW z{(Yfr*B6~LAO&|WKL*An05^Z>cY;R;LFQd~CgxnAx53Q`+&N@+PU?UnTo`Y?%oKsp zheep=Hju<{N~s`F0|L9s@(zdZGsC|H2RpAE_Bh7DHjIOK&U%TeRUU%$;M-A};hlSt57wj^iFN2<5RBOu?~G*`cf0;xhWMNA z3GmMzWx&!BllR#WbjI}~5RCx@*B1!CmFdS_2}96%%n)Cz4Dlz(ri!%KME+#z$4u6Lr$Ay%5n+h8vw(KB_{yrDILL8Aj;wTZY`R2 z0>;0og5HKG;=gK#Qine10en{oT-G41wM=OYmbL|h<+XMAikI>*yrhQD`K2+Hfo6C^ zOnsyYimO8K`W1dn4ZKQbX=A8)dZ->NR@Vhu;Vn9FHp|~$Rf{jRfw#ysXuMbv0gYLp zVo`0Qzo3jt4VZwSEL^*U-oOK(H0ZCz2F(Bo$G5TLS98@%0!fqCH& z@#Vo#ZG>rjlTRbO%ZEy;Bk=N^MmmQzd|tMo7S2U7{V3rvlQR`MXe z7s+_wg4*Ur43^me{}Oy-67n#jCE;-GQtTvxsX_lDem@aM*vtlaLl4T9hFX@2;U+e0 z!o&v?)olwB0i-x12U=V*7B=Nm;Jrq`McTq>Zkay_!OWNjquD|gtqIX-{^lRI`NILf z>5i$Fz!j}{z7#Z0ffrR7K?GBq>xGALHK*cm17&$92*_k(%gf+90S)8{PTM~h{Q8iUJ)xsO2uyf!Zpu&L#mGWjMjHE>^fuP?J zqW56I`@R|>aX}@*PBD2kQB2q>cC}bW31qe(UJ2GtO`8Y@y)++Em4F*daFG2}0c0KK z!sYL1i)Yh^GPRx~$+AYqX%(jgLrQV3Ls+QR1I09x4=%asYT zfwKh5h+ez5^( zTIYq(5Vk+;q#TIYquGi;hn9)k*# z;#@T+pub#4{}-C3as-@0$I%9-kg)f#p5+AQ*^P zTHKY{g38*ZW|8gO9!PdHH_E`a`1#&M3UF9>LgHN?XeGZ`9Q#Ev6|EM5=B1b?6_6_1 z{dk>&*Z|wjBr>F-nyVqt2I=j+yo65>u#P0ax)X@c65|aLNgU1)P2v(MiAyC&(@Bd~ zI8#cOn25vhCyO1+NKc9aGk;R>+=RK!EDg2ci+{mp5VUVRCl{j4Zn7;vERTt~zbXqX zhBy170-LQ$z7imXDqUj9SBI(;dATb2ih)#;w&bfwl`DIrZpQ+5L#`&=%27w67Au6Q zP#Ow_>$#&gM6(=BE!xmkOdWHIxl<0NmTic&r?&+o0f!_{CvnMwWQtpmo$V&cs3a~~ zBbnzGWdF>71y*g4y_x{1!>oC@#5qqCkWwmOW!3&VSQ^2?$`J=O$>d_sOnBAwv`X*P z>M^C$y*8?}kk>Q1e(Y$>7r66eQk1p*1b#KUNG5RtkR2m~)vl7^=ms7ndEbbpXK83L zBvx|;!EFb8o46l0mlC>@X_LZ}Y2Ng*lqj20lq9Q?Iz}5z>*RL8A1<2+RU|yW?t#5mZJKc$??uaL}yY9C6o;a1{ z#MF}1P{Iop&G7c~<~noLvG*@+9!8rMI@r%}U=b|s7yB=Ocd5hM>*ex<0YySEK8cMu z?j}eD>ddAh3>d{3m$b^oz{Sph{dZ^#gjs9~yw`l8w54^J!jyVU2`jTT;nkNmlkGm^ zoe(8;Xd4S$Q_5AE6gjR^*p^rvS_lm|+R9-^-(S7d?6||Fnojs4PM|axfPLdRwSfqi zTBp!qzCm;@W+)_GcFLUFNg4?!iur11Er$}bT&D7bGSjD4b4`Z+3qMiuV2dSauFRAu za32$A!t?+yRB#82ob1{;MHv?%Q`9NICk-xOOW2`X9|*(NG7px_VT?7VdC1Lv5T8%L zbjjytSB1|DExy3)EI1W#E;}_WKpPNT!V}r9NfhnWxTI25Y)>HvlRbsLcG_feX@tq1 zELoUJvXC1}Of6FpAk5ovs4W7I%aZ0KGX7eHSjH_?_bUhiP@9~Y`bfQMj30T2h#JU6I< z_z$$g{!2M7Ud#l+XNV@9;T0|MSP>CapFj_F`CM5S(BzJlci~@GPo2C*e_YArmjcp)l_8@_)3sDf{* z8Fm`nl;D1TRj9><>1#!mwF~^%9_VlrgsLLpBndaism){n`qGf|z{Jc~@V11!U~6ii zcgXLp{20|@WRQ{KLBoYCLf-inXy+Z_>_J5i_6Oi0tN53`;gfU^f`UmT8OZj5qml$9 zsFMri@pN*@?PR^Opt=2@d|A8tnJ zfg^d)eoW|e$R-nE%5#d+tC>l3>bQzMN?2loNme5Z1I*A&$>PAG%mTq(F-87&Rj~Hl zpyyK8{-UVjT+Ol^ElliPeA9-l#kHGmml}Noma=B^1y{QkZ}}(jtSyPRJ>x_-t{=H~ z2@W&1CNWkt=RE62vz+SuW@^XjfU4y1&NEcqwZ!9CT|zS!HmM@K`%TL&=%4$mp!mSs`Ff}8tDvqD zUTGl5t21W;Sw;5c4O$wY1ZSaDM6Kfml-5&8MS2O7Ff3`!lPF4B_+Sw?l33@!09nn+ zuvktjAcwT(EdoFqw;2rDprL2F-B6cT~Z-qXy0$6{dn+C(tC&>aUIj zTHw1Fb`E2*y!!*adk zf+?P9mF081{%|-H_KvPU%R9Pts3*L@Q(g<(%k|z!$Qy#E(B4x{V zhp1$zry=4G29wyCZ4qy%!Mn)ZLVF>(;FLAGhI*=-;Kvh&^Ez7Enin>QmNa{K8hBwl zSeiO(C>nNg1AL<_c!{?$*s!Dm(l56-)I%3@f0%5f2j^#by{0$QDyu8;oKUOT9B&=h zTH9P-(;D%&jID!fp~pqn!`*kVcIns#c;@YtG{7&6TI<4ofAiSIcq`LZ10Lk@1<)7{ z)SF)wwSiXrO+Z`Cf-u~_RE1jQ^aAKVd<_Z&nnFvaU?H3@!rvZ^9p`Glr52WkWS1%l z`-8RZRMJ)h=E)OMYfS|C5q>*>zdv!V2N3NZ82l6~9(zTBKdOQcWPDf}Chn=BH~47S zdbTNY5KE8&_<0e}Rp%xc^gua34A4?&(0?n(^&GV#_Q>78WsN+YX2Jhrw@dQB$WbE4 zo?105J4+&=MS(j0k`X0f<62_@=}o#x5>55){v^rK6Cz96w5xU=#JlD)P(CI|i1MTN)HK{nCoup=vaylp83xjb5YF?i??^ z-9etvYl|iuVB}m^E_+Rj{ctv*Bsgi4&cuhSJTaME5~iDw2=P}wUd%#}awUi6k8VAc zavrD))_a>n5ik5IuHK8e$KURyeBo^fhZb={RdSfSFbSlPq;{1&whw!c1B!dl6)4a` z55nx^>`hn;zhMo)?{hBkUf{pH4Za7YAy+lEU#9nnTf| ziN#!TPwCw?{BDuuaX&EMxS-9 zpheUiSTlx}IM)nvgL6$uT=C$Sk`cPVLNg_0tg#$6tFWDF?8+E#xpO)2{(M~;MjgW)PrNujq&((V zJWwT>UJwD%DVZ5lkHkJK105M#Ar|1=Q^zs1}Hxs%i&WA z|8E6u6L9Cjk=m`mcBi`AO^Xh7H&}jT9Wc;_W5SP)#J}zWf7{gKx#^*J{9QaZ{V2)I zT{(DK=AMkSr9(4wOO3rHM;1fz=^%@-d85;IqR(YcpH0=jL7z+ACX^gG9(1&Tjz2&= zk6;~Z)1bZ8gTXl~hfK@t&PdxhG}Bv}nOnl`o&$9@L7j~d`?olCeuQ;Ou}<2HU?d4o zJ2Gv$1AIq9Mfji}--G%dm@7vi0iP=PALq|G5W|mRd_cc%NCQ7!kK?y)$dt_dwK#sO z4=c$mT6uU`=GH-juSy@9Sp*ymB*-{kEaPU3&Ps@-E|{A?K|3!+ogl|Lr=iYrM`uN5 zRob62=T6U@zbvz;d?)DL1&W^s%`ZTVya=6tDKldpsM!cQp9b3}z`UjLN%%YwXM=A~ z12vh&m6^V&nN=5Mj{l(r=Vp#iTbvXRpzd_|-vfF#ZHvci(Pr@D$}|kb&6)W>f?7A2 z5u7zNGrJ@+qvS}G9}n{HL0*ZtcQAAY_|2hY|Q=;Q>(=5O%9x!l`#aXIb_;77{; z{cRlnd?V~Y;XVa?c-zL)%>suJ?dj)o+I@pRS;_Xv{VCpy;U|X<&c9p@{-!ihT+Nfg zH(|0rbU4>Ij*mX>sPM(?2(deZ9d!iDOZ#8NsXss)QTThHrhmO`DE#4QgT>DW7+=BV z|6%<9%01$49yji$bC~R<({et}=3@gN7xQs7A8+B~WoxA1W@A0Or8Ey z`Pjh6#e7`N$6NTgnU9b1aTg!=@{wADeTA3l4F2l52mhZ|T6&f@A9fK=^%hJTJ7H{r zcf$AylL`t7CPszR!K}@Q5ijwMd!)#F6pwpe zgCGk8*Z~oKKndmVe5zv9-I#7hkYLq zA^v%l`8|q9dr|&*=EXnXGT*J_zhr*5l23<$Oy#%i0r(Su$98jhya1X{9`naAFZN7i zK3nlvDOJ10Mq##R-+iQ4{;f88a-DoDe8BL>dUQSs1Ai3Q+5b0^6o0N|KK6#2$L9(t zpS$19FNX_{7vgX($D6eK7|S2{vs?aI=DYvu=HF)CtN1UO-}1U!{H z*WCP1m@nGr=JEQA{hF)V?G!z4y5;X-eyxg=$C%%!`2S&kyt4Bz%oi*F{G0iG7})TU zaTyM|2<^;PdPXtteb+629`h~lx%q0*qvY{V<)i+Vzq{pEGT+Pn@=~$qk}{9yXg;?O z*RlM16(^g4KN{w>r-r1>a|SqlgypN0{Hx4+RDa(W{uRA*USocX8kd!jSJ9r0`PA;obntx6@@rH+Jo0ez&k8l}Bbnc#?5|)x zU-_qod7tvn70l07^6Q!JR{DP>@+!_BVZLA4xr_OB<%hpBKmToVfW-Mh;8VqCT=b~C zoeLE)e)cK*f53dZ%KtxRK0}RH3-j6UyX{%U{061}*UWEK^YjtX|AAZ2^UUv2{(PJH zVio6~GGC?QAro$pu)k}R{CMVzRbDL>Ud_8I=6z~hLd@?|`lHNmQ2c$&dsIB^5dCVr z-e7)<8ux!NU!~$R&V0L)&xRXU^h1RG4C&N-@|Z9F3t@1h`JBo87UiD{m_MMd3-}!t z)L*6Ip+ofl-L3x?=DkXO3-i6o|IaYLMd{fmyz;{*%A4a9;v>pYl&F^Yhhpp-p&|AJ#G-Q}*1$`~jt>i}`Lf-(O(9 zOXbyfnBSn{|1;*R)HogqOEm0Pmm05;%*T{HMa&ned2u20y~@tZnD?o9x19NGrDr|! zUN!EUnD?mp(#`w|)$X5xPc^>pvwV9mg{|a)|1jUA>^VA<{L`-H-8kmE)i_RJevgv( zGk-w&VFmNWDy}v#->=5&LFU)1eDwUQnnct(vr9<>9J$EwS zt>W`B=3^>8UuM2n@qcIDr}F3L%+FWz_wetKABt5z%oF|R@L8)z=NqtpT|Ux>erjhXQ((l$b7frhaE%xZBq7}%={h7{o41B77-N*8~l|P?iK3Cuk4)7 z{Q6hj{awU-k;+RQ%x_TRvX%Kg%AV(#U!(l|j>s!J(~l+lH!3|NncuDKoXose>6y-a z#-H8&@-ttw*Uc{$dDX8Qfyevz@t2bPCOUY2$@0BQ&tpcOg{;F)k$;&;iK{;|?^ElI zPX^EvXZbZM9*#PW{I*{4Co#W5<-HQ-y~@uQ0gwIN>L)wRcU9pu!1D8z{to7g)V#Qj z`F*OtzhgdEwfhY4UTV_y?_XJdtFr%F=DU=h@8psnwyODlD)YT6|4b2irRQSi*Qo%o^j*{gi=YkdLD{*S`2%Xa?qq(Wio_4F7bD3YE)$cIMvd=_%=hm1n2+SQkA+wBEB$-a-}UOce=PG&YJA5szeUB#H0HZhyYrdfsQ9ay zuTuW}8S@7ef2Z)u{vFJ>EBoJIexHhyG?=`2J=&t=bD3Y$>-O6i=G#>~lrg_g^{avT z@k;+H;PJX}ppNvL--f~IO)T$I{rxrbxhk%nWqzL;_rEf~LdD6q%vY&-l=FR#bERh- z^ShNFrZAta^5i_`yZ+|(TMP63e|7UWFz-=z{+9V(CBK9DnDYPYqF=?s=gb!=K0S~6 z+wvE;JtLSeQvE7m-lP0e%KSbR{{i5SCe^Ng+gUzW<-;iQY2Wp<@O+m_H?zD~#lyYK zuTk;v6!Yy${$1cx+4BX&eI`KkQcXeI)bi6<@@BOzHn2^ZR<;`sXp<{hphT zFuy^~mvziUe*C)(x zRPvdpksmUY{RPbDs&>nn->1gwQsxh+__>_&_{>fJT8q0jQnqNO+KBmTZ9`jpO zf3IYIz8aTx!mDxoHS7aT!A4;(3Yr@oHYY$9%WS8{aVRRr6xR81h4tnn&Z9 z?^5G+0rMMr-ErH({BEWH8s@u|{#!*}`SUl-FH`;f6Z7L$oIK0?R>i;1yidjFm%yir zpJ8Lk4=dEX7{mM;HBYB9zd?=fT;{i^eg&BCR(39Dexr)RUohXL;^bkGS8?((^DSx} z_!0BjDjtT9zObu#nCDsP{|e3i;$Rm`_2zESij{mYo&pzOJg`T5HJ`9R{fgA{Cbr?L(I1*el_#E)x5h+ zcokQ_XWpyk-D}K`SNy+4Ud@;9OyGVgKm36CJxace`CK)RE@Hk&<-K<1Ta^AAna@!1 zxrzC?YP_Bl{VGmg2mWXY0_*r0%g^B%>&#{3!;pZ(0wSMxf%kor4U&5M(n?^gNeOy+kh{qvaL zqvm@X^IOz7-pu@b)!*NVo>$y{c!v4nSKWLs^ZAPJXTC-C>&Qve-+fAd9`i9Zzlxae zP}j{$<~OKz7ck$Y;$#`~`6{k9Fuy{@|3l1IseJwd^IO$8?q@z*<$+I`&sFpNu*u|y zDi#07F@Heulb9c`?5SYBeXo05E@D3RvYTJbyjR(I3-etn-X3Cpy|QOJ^ZhEW-e!J< z((_N|b9>$P3_XMV;8A(wB8HZ-33*uXmYWul)QK^SR1D!-~icyH&fRneS43Df1o`4;M4PXRq6yR_52J>tF}- z>(%_)0DLOHZD#pRN`4#jEz19IFu&%{ZhJmtexn-4p=V;d@H@(dByaxS7M$h+kNac2 zYP?QmKBoL%%6#tY?sn%gpRfEA06tZJm$Cc?rRNvS`&8UM#(bBu=SAk%zu~s$Q|4RL z_3JxlQGXApcJrAZ-|N;hgZY^9e-rchD&JlsyprF*{2CRvk1)S=pS#_cfyecpH%7et zyO-rXs$ZXr9+kI;olX5M>UHZshxsOze=3Dn{#?X-zp{TV@Ttc4t^xRmSWmm^Z;$9# z^X^0DcdLAO#5vTjD%G#knBVf2yT4`3uUGSBHuLQ&4=iGStD2A3FrTmD^LFO575}iv zzv;H;Ip#O0`1u?2UNw$iG2f!{%rWQk_^RvXXy&Vw{8ZpE5A0b$_L#qe2&WA!zemlB zE1CBw{#Mbi{P_^``6_OYKacznRO2<4`F($J z>zT%Ui?VYb^DBDY@|QCoQ{xq7exuU!OXl~eIDdlqE;Wzd1U{92`dB_g*?;u;)ZcEE zZ%<>sSLvV5eEvJ`e$|S+>eo`{x9)e#uVdb)#$^lh8`Zj~n|Zl!mG%Ft%=fCe{h0YZ zDsIz?slQ!H{ut)>sD6!QzDW6D3iF=*9`g}<<}rUj*}sJOb`}5Ym~ZO!n2+e$!u(t{ z-@BRbR_(sce3A0!f0*~FJT|O^{LrNOJBImwH7`n-&rtrXW`4YCcOmm#DxQ}!zfbA; zCG)w;&aKR^QT=_Ed7rwD?*~3r{2yTXElNIn3i-jO^2QIC&rtSE0X~)f3t2v<{NQJP zg~}&OnJ-d#?`ODE{BfH>o_3Rm$<8=0zU!*}ZQ4 zXE5KZ?5|+nqvCdf=vVSrFz;3Qr}d1~eqIam_`N>;eEjFECqvDz2Y|=x)co_ve)D&Z z;dDF8uUGPWnLnV${Zr;W$`2W3Z#Q`PkpxcK(!kubM}jnBS-T{}l7>Du2Eyyz)bz=vQ{eneSG4>9}d+hxr`; z;?D`p_o_VeW9IW!o@@d>RXkk5@*C8++&+N(!z{mE#lt?~mHqw9cYWaYbDEF(weNj5 ze**JcR6I;%e!a>YvzcF^^n`?0_T0eyMl~)EGvE6+w>>?~&sFiWkNNc~9=>3Hw`%u@ z3hM7}l?O&J?@{fJV?L(Fw}SZ%DsG#Y->Bm13g+j(=kC`H!YjZ1j`@~exBPR=cdLB( zF7qCxC;f-i-xX?Jj1pdr?_}Up#pg7`!}pX%kTHI{1z4eJDH!W{PPy`{c2o3Wj?0tN&6A`ZLacXF7qua4o_o# zqq=`7V?JBO&t<@;^7BCH7%jmnmXtGxHZJi z^)&L?PvER!!JI&Sq{*E5{DHvtJJ?OwU#neozteHLag6fKyGinOpAzd;DZw5<} zo0P;&NKtoUN^YX3(I2US_rrhz`1Ubljr)aT@ZMeMXL}7c?60p`RC`${>}hUWR0D4| zY^}jpH`c%l(}Jzg2lKiScy$V%`u!k1c7n&e@yfhk#~+3lo0+$bm{(to9q*~Bfxk^x zGu2m9enH9fsWrZ`3q0V0nNvzCYi5?0S5KW)Gpl4u<Z znjP>0rbtZ#ysgQS=CO80mfpeX>IJ=0&`@@Vs|wpN@Eev?!+S%`f{KEb?b(%3!l3sOxnzG%<9LRel9mdcdE#E`gq z17ng(-kMhUGHvL*yrvW0w`-EBNt-o|Ep1XAMwC4XsRQzg@Mxd=-DXl^4_*ai2A;1J z8u*?xd9A1EXR2Yc25F#ZV!zr$0S#EQIrZD%+zHX-lnMCuU}Fj;2zyF6X~OPPfl~on zgsWA=i3ZC2Zbz}D6P+tWsvcVd11}t<7y`5CO_pMxyJA6Nh|PP-ol2%JVA=rhSHw5r ziAIpJ0@!{*tx-ZMG_Qpu)d-oh9Q-A-zItu z*4Qs7gvp+eW}D=_!WHm_zRFOe2uun3o9pd$fq9kf^xD>ij`7q_^SVdYn(`%~qS0Jc z%ls{oCTAXTE;4x`VH*aHGEQ{Z)3M6F{upzOi6Y9^@0l(1YTbwv`|hzQfW%x=)3_+q zjIU@7M`~(Bfisin<=!t4sv`JI9)c*Uz(T~33Sq_&7w zyJLlkwGK9x^d?V_dEM^+cC|wHh;~X(Jg3Zp4i&>nQ3T5T_3* zx(u%J5kJp&AtC(eB5S8>$>8-#%S61Z;7D>0l6Ccj6*LF6<56nbvdJldLC{Mu*W4iE z>^QP!QR0e?YG4(ba93h*6Y8w?*+BEkdNYB6aF#|59)Eivl4|roYejR!-{=oJ`35Mn z;-n;Lr8Ay6_T)}9heM?E3aq>E<<9J>>QhF2&oWDXoRzMV(I1oN!Bu zSPBbs6@Vpjvm;p1I>0>Fd6tc)x6g8jP^z@1qBHE+e`->~3F=FS6M4q&t-Y``iHhnc zVjwAwM729oY1y74=NCB30Pr)ef>Vx$D5a22xgv45$9GNbg^3B82qSo}e2pDSa(!^c zk!6XvC=nApSEUte9-3UscPXa3)pdbZ*rRawpn7TI#?sYklkyYo*h5j6xI6@F7qz&O zdX+2SV02m|;pVzUEvcrH+fi0Fw8u99I}gENsLrLJ0bg(KVmRb%GZX7NaClP0C-Q*R7S2eRo2*D`xO-Tbr~-!C`zY4x0!u<|@Va{|K+7R9z}wH+IGnMx229+SLoGcG z6BE?7yE4_iBVEQTVchg8ssOeuTuFs4gxoJ@2C071ST!sGtE}8_UiZ$*%$gIH^)3E- zlh7!qxE6-VlOmCytT?seg3_MVVrz>2IQOq$oYgc0n!Q}pSvFNTfC5gwk{!TR4NI!{ zwawM?5@vCrJq#6viA>+TxHcH5hu08WmP?&Ps6!hm%H58)ZW2=jgnijU+w~Ti6>zqx zR{n%7iI&<(T@wxsye?Uc#IC*E z*sO$&THeWptx_&aECX=Dm}xO9khtR{Bke`0bBAFq&}k`+tDrE%i&zXDX`UWvhCN~z zQ`_Q7HTt&KPK)jc!kcch>0#bztu{TH$sEW{<$)mBAx+W}4ECyBx2lP54Ui@#Hh5|( zW=*ew*SY%}1FfKN*7VX~sM$ZO7Mc*{xT@8W>MUp2cv#g~+c5U6QzApU`)LI=7bFHr zS~f{WCxQlN-lFTP0`2}Fq_IS(z>qteXQBusQ{2iYc8f&Lj@AlmAIpTp)XUpyBY_Z) z0ZhJ{ikWa<2Dd6TZLoF~9XjL^mT?eIv^!uWz(oJ8J$A5sN+<-Il+DfXNH3-Wr7A;{6X06fg!02`{8nl zkIovywTo&R!Xfwv_Rf>9_l1dTNzx#*d;S;ake~TNBi1`{=Nc_p1|&ubtWO!1?(A&# zT;;+h?&PNIRp~%IkSeb2bEDMkMoTekOtr}YMw(feNS9W0N_~5Ruk7d` z=$Pr;g(Zg9DNAT`C!7@biFR|a9(1PQ1^(K4b765NflD&6MQYQP);<*5``@O2VqFQs z61TSZQ`48pJc9rHXBebt{bHn9x)o2>a`tL}yp2u=k@~1=h37(qO>f9GO!a zFvFIr<%w2Sfs0nyOc*P*Bz$Zp{eQEL!&ipQ3Z@zGRJk^2?~0JYc*pOIA>E4bKse)= zDkLVT4GNZ#^=?ITz28~e;iNS4log8)%+|5JBW=5|(z+{1xOkjGHSSc9W&5fT)R!+n2!Qtgas?ehi zA?<3gn|BPY<&5nXYr-WuFJ2d0r620X2f9F^4EN;`Sm!q^fq!b9HJI5?Fjr7^q7!ZI zr-SC38PbG1Hmscvs<%?E4p?b z?M)387_gAHDm!z+?!rWeO%*uOJ7*Q>T!o=m&QjcZBo78kYD-+$FuS_1Kq;L%~qbMLw5o_qdZ%9hjXW{@a>ZP!pbu+O@~ zc#^+*;wZrppit)+pUuvEHX%ll@c59fd}j!LXuv9s+X)YjX`UnNC)6-ztyv`OzGU$taDW!?jWsF*}aA5kjtI^4LISpf)xK+r; zf&AW6*rQ1_7IL(jlDEqREu8O>XRs+IBKt&V1+gp0}=; zQR)?%sYoE-H;HCcln~AKh1GdO$#EN`)_0+~utZsq4~q#U@ak)$Kmw~zsJrmfaEDDAlCF@&Ree-uoWlqS9Jb;^ zOnr19Rx5Br2&?Ted&TnEb><3xsd72O60jL=Z(v*89#4EfRoCx~2f3m(qcf~l8F>C5It7(RFBmL`7COh88eja)j@gw`l*%Nv-L8 zsn`XZw0sE>GR$a3u-;i@Z$P=TE&Hvu_F3MQJ|nocPlXwXlx{tqhw$UYP~L5*|s@n(Jstj-%4 zdQ{TDC)mbTtPqw|0sak*%U8^7w{0Kd{1;nx7PSibIIN0GzE4I==+^FP zJ*89!1Hm-ss|6~WSIaWOOx!1D6U>Kz+_+cQml9GGF5Ii27acy?i;`b4JlWU8g|;kR zK-l5Lw`|uH(doqewUR(#bn{)wtl{`}m?4VA%8VT5;gf514ar8sE#)KvX5MY4$Yi_1 za(=Mgwbh7Ft~W{KK$!y3T(u4!BR8k3&E>&(1Eav>{=i2m=XxNO@!CQHJzS9~j}Zr$ zx3tUTOS=*bOIWstb?%5v%H;|r@Dq-IyQz>$Q893l_HAR%+ODm3Nez;GK=~{g;Y8si zsym0X9_Z+7g9Apg2B!>A8jbF?Z5cF0;&(pziq|?R*9q$;g~F08=TB?8yM%*X0}E)| zXosWu`gH8W)9E)+@dS@LQ=_hAOk9n?f-M)bbqqkVON0g$&HIiiYO~{*_IF5OrQT*$DA9*O-QeF76bS^jL3_ zg0+88z!k!{xPvnAn5(n1J@jW;fz33d#8!+9L{D##d_{?-9uQS4z16f>S!Rt)zOFZ& zooN6vv$dRZ3AjAkVX9@!*I$^Q!!>dln{T0V+yycUU+*gq+R}@zIq2g&PnABkI^K&q z**rizy;B~W9OOJ$%w_A7gKEfB=OYjYZFJ{35y4z+K)k7JwfL2@#UeWZ81{U1AN7WU zt984LXLXufQI(^IT^}HD2&Pk{q8LqPAd>;pRlB0$-JO1jYFa;eNiB+lWrzb4r4(oO zXYh4`RH}QDQHlvKfnlufT|^g zEsPnI!@J$HL{+-8i?21CQ)H=-q_2BikJH8^OD9LCvssiMcUDcRc?s+qk zo_sZ)nig3J>BJ&7tL}Ibgr2IbssL5x0FVjUn_yjYeog0 zMJ&8$jpFd1B68icTC*f<);{X*WyM_JWSSStnoSm~wob^-E?$s=hi^iJuH3OI9SoZq zsAD`W_0%1$lQ8I`7+RW!b~KHUgiz0F(_K~i25LvGpkyQzaJhSoy&Rv!u6TJhz9Enc zd0bL?&-XlUE1su|hBgQTTb?v)}D z@e-BR#o_IjOIWaOTxjiby^$-4(-<@;4S|B|EyAa)Izx5~b$`9NEw%#HopF+IU6pv_ zRceB}Gs~xa0}-4flJTW+kh)eSL=U59GOm(ZYKWBs4#b&;( zwHEW)VXG9W<^Fy=hE+(o6uig?+<*}84&k0WXiYP8Rji$e0+~;$Y&S}REv$3&q zMbt_wYx>@ZCl+e@+8}w0BHFL(As>6T#v^Up$V#SAV}_;2{B6vVe7I4Z$|Q~Ah76S- z6ewJA0x4rlRx83w_^FYJ#31o=BJ(I6jU!fKk;3Pg3TlUoc?vmc zF~>64SXBBOnX&}38nS8EK`&8IpQV3UDAcpT1;5kg9{Z2Ze?hBq*hE=S&;vm>no>2H zxu!^|v`o%r&I%XjWUs2~!eC`#av%k)Q@}s2OQ66ROwp-A@Lz8mT@=zZuf(IY>LjtE z(+;*S%vZ1lJvWw~tK_3c+a!VF!D(|kSzo#&zV6biB&JzCoEtVF1^+$t3)89zP?Q;wp7Jjg&l3VZ>KzF=P*~;M=86Nw~G>HJ`dH0*Lzkb zB{6P3LOTXUiR@sD=Wd*rNvx9$w3&kIT{PLP!!{Y*c2V}IJl7$lbW4*ag`7!3qmy4$r-`NrA!qZN`a2WQ)D9KF0{IANUQG~ea! z$?9w|#q&IE17ofX5J1-qhOqCp<9leg8);AM({i{c|9cgv#{Vv^e*kZTe_P?L zeWVCKg+4U5!G9c|>Gf^A3B=$ZY6ok7Z#SqjUN!y`Tzj1G{~jTOm$ftbIxzfgIBVCq z|DOc^cZIk1I|^Upua^fuzz15Qjd$~da>v@Mim=UJum6V%e;{Ckmp&vPti7uABY2`& z4gV8dqbUeKeqAeD;?Mlj;r!B%@F~MzyC%1+eP|0)h!0ooPw_p&+wVJCd*zM-RB(3n zXBGTEmU72hC5LrqTKgA#_yyiHzoSVTN-u5S{Jea%zlOhk{|!E5_&0wgcdY%3b=0<| z-ybUYU;bJ;TKoM9!1m9E{WCsf`SI*;<&L%2tfRKoa0B|8%BS(xV>fv1uhQ|gKT2EA zXWy-*pY?nu!9P&=2k#L6pMWL$+58U`{^9?C2ME93#$V!HgMXy(j}-o2G&o+y--fsC z;}zV`^FR2T^ndWWw4as6{~EltPgU@5Dg0Xszt;I}0E4sknF{`8{ax6X|0ED5=W2NS z{!zfwShjxq-Tt-0|NLXtQ`&3g*TE-r{(7ATU!Q+;=T~n1?4+?@)%kB8$;D5jp|`Eu r*7bf|Gu)-8{C(qTnc?*><*Tw+`N~J|r4Rnc-xB!G7@`VPeI5K4yq|uE literal 90624 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 iIraD6X)WR0n?6 diff --git a/main.c b/main.c index 445a967..28af000 100644 --- a/main.c +++ b/main.c @@ -8,18 +8,32 @@ #define GRID_H 2048 static int g_fbWidth = 800; static int g_fbHeight = 800; +static double g_cursorX = 0.0; +static double g_cursorY = 0.0; SandSim sim; +static void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { + (void)window; + g_cursorX = xpos; + g_cursorY = ypos; +} + static void render_sand(SandSim* sim, GLuint program, GLuint vao, int fbW, - int fbH) { + int fbH, + float brushX, + float brushY, + float brushRadius) { glUseProgram(program); GLint uResLoc = glGetUniformLocation(program, "u_resolution"); GLint uGridLoc = glGetUniformLocation(program, "u_gridSize"); GLint uStateLoc = glGetUniformLocation(program, "u_state"); + GLint uBrushPosLoc = glGetUniformLocation(program, "u_brushPos"); + GLint uBrushRadLoc = glGetUniformLocation(program, "u_brushRadius"); + GLint uShowBrushLoc = glGetUniformLocation(program, "u_showBrush"); if (uResLoc >= 0) { glUniform2f(uResLoc, (float)fbW, (float)fbH); @@ -27,13 +41,22 @@ static void render_sand(SandSim* sim, if (uGridLoc >= 0) { glUniform2i(uGridLoc, sim->gridW, sim->gridH); } - if (uStateLoc >= 0) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, sim->tex_curr); glUniform1i(uStateLoc, 0); } + if (uBrushPosLoc >= 0) { + glUniform2f(uBrushPosLoc, brushX, brushY); + } + if (uBrushRadLoc >= 0) { + glUniform1f(uBrushRadLoc, brushRadius); + } + if (uShowBrushLoc >= 0) { + glUniform1i(uShowBrushLoc, 1); // always show for now + } + glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES, 0, 3); } @@ -43,6 +66,7 @@ int main(void) { if (!window) { return EXIT_FAILURE; } + glfwSetCursorPosCallback(window, cursor_pos_callback); glfwGetFramebufferSize(window, &g_fbWidth, &g_fbHeight); glViewport(0, 0, g_fbWidth, g_fbHeight); printf("Renderer: %s\n", (const char*)glGetString(GL_RENDERER)); @@ -91,6 +115,8 @@ int main(void) { double sim_dt = 1.0 / 360.0; double currentTime = glfwGetTime(); double accumulator = 0.0; + float brushX = 0.0f; + float brushY = 0.0f; // --- Main loop --- while (!glfwWindowShouldClose(window)) { @@ -99,14 +125,19 @@ int main(void) { currentTime = newTime; if (frameTime > 0.25) frameTime = 0.25; accumulator += frameTime; - - + while (accumulator >= sim_dt) { - sand_step_gpu(&sim); // one discrete CA step on the GPU (commented bc unimplemeted) + sand_step_gpu(&sim); // one discrete CA step on the GPU + sand_relax_gpu(&sim); accumulator -= sim_dt; } + int paintMode = 0; glfwPollEvents(); - + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { + paintMode = 1; + } else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) { + paintMode = -1; + } // Re-query framebuffer size each frame (cheap and simple) int fbw, fbh; glfwGetFramebufferSize(window, &fbw, &fbh); @@ -115,9 +146,18 @@ int main(void) { g_fbHeight = fbh; glViewport(0, 0, g_fbWidth, g_fbHeight); } + if (g_fbWidth > 0 && g_fbHeight > 0) { + float uvx = (float)g_cursorX / (float)g_fbWidth; + float uvy = (float)g_cursorY / (float)g_fbHeight; + brushX = uvx * (float)sim.gridW; + brushY = uvy * (float)sim.gridH; + } + float brushRadius = 60.0f; + sand_paint_gpu(&sim, brushX, brushY, brushRadius, paintMode); glClear(GL_COLOR_BUFFER_BIT); - render_sand(&sim, program, vao, g_fbWidth, g_fbHeight); + render_sand(&sim, program, vao, g_fbWidth, g_fbHeight, brushX, brushY, brushRadius); + glfwSwapBuffers(window); } diff --git a/sand_hello b/sand_hello deleted file mode 100755 index 250e73b8e07fea4c9df409576b0db9bce2b585d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81768 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 diff --git a/sand_sim.c b/sand_sim.c index 48ebbe2..1d1c86c 100644 --- a/sand_sim.c +++ b/sand_sim.c @@ -1,11 +1,45 @@ // sand_sim.c #include "sand_sim.h" -#include "gl_utils.h" // for create_compute_program_from_file #include #include #include +#include "gl_utils.h" // for create_compute_program_from_file +void sand_paint_gpu(SandSim* sim, + float brushX, + float brushY, + float brushRadius, + int mode) { + if (!sim || !sim->prog_paint) return; + if (mode == 0) return; // nothing to do + + glUseProgram(sim->prog_paint); + + GLint locGrid = glGetUniformLocation(sim->prog_paint, "u_gridSize"); + GLint locPos = glGetUniformLocation(sim->prog_paint, "u_brushPos"); + GLint locRad = glGetUniformLocation(sim->prog_paint, "u_brushRadius"); + GLint locMode = glGetUniformLocation(sim->prog_paint, "u_mode"); + + if (locGrid >= 0) glUniform2i(locGrid, sim->gridW, sim->gridH); + if (locPos >= 0) glUniform2f(locPos, brushX, brushY); + if (locRad >= 0) glUniform1f(locRad, brushRadius); + if (locMode >= 0) glUniform1i(locMode, mode); + + // Bind current state as read-write image + glBindImageTexture(0, sim->tex_curr, + 0, GL_FALSE, 0, + GL_READ_WRITE, GL_R8UI); + + // Dispatch over entire grid; 16x16 workgroup like your sim shader + GLuint groupsX = (sim->gridW + 16 - 1) / 16; + GLuint groupsY = (sim->gridH + 16 - 1) / 16; + glDispatchCompute(groupsX, groupsY, 1); + + // Ensure writes visible to subsequent rendering/compute + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); +} + static void init_textures(SandSim* sim) { // tex_curr glGenTextures(1, &sim->tex_curr); @@ -25,6 +59,15 @@ static void init_textures(SandSim* sim) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // NEW: tex_claim for the atomic “ownership” buffer + glGenTextures(1, &sim->tex_claim); + glBindTexture(GL_TEXTURE_2D, sim->tex_claim); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, sim->gridW, sim->gridH); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // Unbind glBindTexture(GL_TEXTURE_2D, 0); } @@ -75,14 +118,26 @@ bool sand_init(SandSim* sim, int gridW, int gridH, const char* computeShaderPath sim->gridH = gridH; sim->tex_curr = 0; sim->tex_next = 0; + sim->tex_claim = 0; sim->prog_sim = 0; + sim->prog_paint = 0; init_textures(sim); upload_initial_state(sim); sim->prog_sim = create_compute_program_from_file(computeShaderPath); if (!sim->prog_sim) { - fprintf(stderr, "[sand_init] Failed to create compute program\n"); + fprintf(stderr, "[sand_init] Failed to create compute cell tick program\n"); + return false; + } + sim->prog_paint = create_compute_program_from_file("./shaders/sand_paint.comp"); + if (!sim->prog_paint) { + fprintf(stderr, "[sand_init] Failed to create compute paint program\n"); + return false; + } + sim->prog_relax = create_compute_program_from_file("./shaders/sand_relax.comp"); + if (!sim->prog_relax) { + fprintf(stderr, "[sand_init] Failed to create compute relax program\n"); return false; } @@ -90,27 +145,110 @@ bool sand_init(SandSim* sim, int gridW, int gridH, const char* computeShaderPath } void sand_step_gpu(SandSim* sim) { - if (!sim || !sim->prog_sim) return; - glUseProgram(sim->prog_sim); + static unsigned int frameCounter = 0; + frameCounter++; - // Set grid size uniform - GLint uGridLoc = glGetUniformLocation(sim->prog_sim, "u_gridSize"); - if (uGridLoc >= 0) { - glUniform2i(uGridLoc, sim->gridW, sim->gridH); + GLint locFrame = glGetUniformLocation(sim->prog_sim, "u_frame"); + if (locFrame >= 0) { + glUniform1ui(locFrame, frameCounter); + } + GLint locGrid = glGetUniformLocation(sim->prog_sim, "u_gridSize"); + if (locGrid >= 0) { + glUniform2i(locGrid, sim->gridW, sim->gridH); } - // Bind images (must match bindings in sand_step.comp) - glBindImageTexture(0, sim->tex_curr, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8UI); - glBindImageTexture(1, sim->tex_next, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8UI); + // Clear next state to AIR (0) - GLuint groupsX = (sim->gridW + 15) / 16; - GLuint groupsY = (sim->gridH + 15) / 16; + const GLubyte zeroByte[1] = {0}; + glClearTexImage(sim->tex_next, + 0, + GL_RED_INTEGER, + GL_UNSIGNED_BYTE, + zeroByte); + // Clear claim buffer to 0 + const GLuint zero1[1] = {0}; + glClearTexImage(sim->tex_claim, + 0, + GL_RED_INTEGER, // format doesn't really matter; data is uint + GL_UNSIGNED_INT, + zero1); + + // Bind images + glBindImageTexture(0, sim->tex_curr, + 0, GL_FALSE, 0, + GL_READ_ONLY, GL_R8UI); + glBindImageTexture(1, sim->tex_next, + 0, GL_FALSE, 0, + GL_WRITE_ONLY, GL_R8UI); + glBindImageTexture(2, sim->tex_claim, + 0, GL_FALSE, 0, + GL_READ_WRITE, GL_R32UI); + + GLuint groupsX = (sim->gridW + 16 - 1) / 16; + GLuint groupsY = (sim->gridH + 16 - 1) / 16; glDispatchCompute(groupsX, groupsY, 1); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - // Ping-pong + // ping-pong: curr <-> next + GLuint tmp = sim->tex_curr; + sim->tex_curr = sim->tex_next; + sim->tex_next = tmp; +} +void sand_relax_gpu(SandSim* sim) { + if (!sim || !sim->prog_relax) return; + + glUseProgram(sim->prog_relax); + + static unsigned int relaxFrame = 0; + relaxFrame++; + + GLint locFrame = glGetUniformLocation(sim->prog_relax, "u_frame"); + if (locFrame >= 0) { + glUniform1ui(locFrame, relaxFrame); + } + + GLint locGrid = glGetUniformLocation(sim->prog_relax, "u_gridSize"); + if (locGrid >= 0) { + glUniform2i(locGrid, sim->gridW, sim->gridH); + } + + // Clear next state to AIR (0) – tex_next is GL_R8UI + const GLubyte zeroByte[1] = {0}; + glClearTexImage(sim->tex_next, + 0, + GL_RED_INTEGER, + GL_UNSIGNED_BYTE, + zeroByte); + + // Clear claim buffer (GL_R32UI) to 0 + const GLuint zero1[1] = {0}; + glClearTexImage(sim->tex_claim, + 0, + GL_RED_INTEGER, + GL_UNSIGNED_INT, + zero1); + + // Bind images: curr = read, next = write, claim = read/write + glBindImageTexture(0, sim->tex_curr, + 0, GL_FALSE, 0, + GL_READ_ONLY, GL_R8UI); + glBindImageTexture(1, sim->tex_next, + 0, GL_FALSE, 0, + GL_WRITE_ONLY, GL_R8UI); + glBindImageTexture(2, sim->tex_claim, + 0, GL_FALSE, 0, + GL_READ_WRITE, GL_R32UI); + + GLuint groupsX = (sim->gridW + 16 - 1) / 16; + GLuint groupsY = (sim->gridH + 16 - 1) / 16; + glDispatchCompute(groupsX, groupsY, 1); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + // ping-pong: curr <-> next GLuint tmp = sim->tex_curr; sim->tex_curr = sim->tex_next; sim->tex_next = tmp; @@ -119,16 +257,14 @@ void sand_step_gpu(SandSim* sim) { void sand_destroy(SandSim* sim) { if (!sim) return; - if (sim->tex_curr) { - glDeleteTextures(1, &sim->tex_curr); - sim->tex_curr = 0; - } - if (sim->tex_next) { - glDeleteTextures(1, &sim->tex_next); - sim->tex_next = 0; - } - if (sim->prog_sim) { - glDeleteProgram(sim->prog_sim); - sim->prog_sim = 0; - } + if (sim->tex_curr) glDeleteTextures(1, &sim->tex_curr); + if (sim->tex_next) glDeleteTextures(1, &sim->tex_next); + if (sim->tex_claim) glDeleteTextures(1, &sim->tex_claim); + sim->tex_curr = sim->tex_next = sim->tex_claim = 0; + + if (sim->prog_sim) glDeleteProgram(sim->prog_sim); + if (sim->prog_paint) glDeleteProgram(sim->prog_paint); + if (sim->prog_relax) glDeleteProgram(sim->prog_relax); + sim->prog_sim = sim->prog_paint = sim->prog_relax = 0; } + diff --git a/sand_sim.h b/sand_sim.h index da0dde6..50208eb 100644 --- a/sand_sim.h +++ b/sand_sim.h @@ -7,21 +7,29 @@ // Simple binary sand vs air simulation typedef struct { - GLuint tex_curr; // R8UI; 0 = air, 1 = sand - GLuint tex_next; // R8UI; ping-pong target - GLuint prog_sim; // compute shader program int gridW; int gridH; + GLuint tex_curr; + GLuint tex_next; + GLuint tex_claim; // NEW: claim buffer + GLuint prog_sim; + GLuint prog_paint; + GLuint prog_relax; } SandSim; + // Initialize sim, allocate textures, upload initial state, load compute shader. // Returns true on success, false on failure. bool sand_init(SandSim* sim, int gridW, int gridH, const char* computeShaderPath); // Advance simulation by 1 discrete tick (one CA step). void sand_step_gpu(SandSim* sim); - +void sand_relax_gpu(SandSim* sim); // Destroy all GL objects owned by the sim. void sand_destroy(SandSim* sim); - +void sand_paint_gpu(SandSim* sim, + float brushX, + float brushY, + float brushRadius, + int mode); #endif // SAND_SIM_H diff --git a/shaders/sand_display.frag b/shaders/sand_display.frag index 4004316..1ee371e 100644 --- a/shaders/sand_display.frag +++ b/shaders/sand_display.frag @@ -5,7 +5,9 @@ out vec4 FragColor; uniform vec2 u_resolution; // framebuffer size in pixels uniform ivec2 u_gridSize; // sand grid size uniform usampler2D u_state; // GL_R8UI texture - +uniform vec2 u_brushPos; +uniform float u_brushRadius; +uniform int u_showBrush; void main() { vec2 fragCoord = gl_FragCoord.xy; @@ -21,6 +23,23 @@ void main() { vec3 color = (v == 1u) ? vec3(0.9, 0.8, 0.1) // sand : vec3(0.05, 0.05, 0.10); // background + if (u_showBrush != 0) { + // center of this cell in grid coordinates + vec2 cellCenter = vec2(cell) + vec2(0.5); + float dist = length(cellCenter - u_brushPos); + + // how thick the ring is, in cells + float thickness = 1.0; // 1-cell-thick ring + + // abs(dist - radius) < thickness => inside ring + float d = abs(dist - u_brushRadius); + + // Smooth edge: edge = 1 at center of ring, 0 outside + float edge = smoothstep(thickness, 0.0, d); + + vec3 ringColor = vec3(1.0); // white ring + color = mix(color, ringColor, edge); + } FragColor = vec4(color, 1.0); } diff --git a/shaders/sand_paint.comp b/shaders/sand_paint.comp new file mode 100644 index 0000000..986115b --- /dev/null +++ b/shaders/sand_paint.comp @@ -0,0 +1,42 @@ +#version 430 core + +layout(local_size_x = 16, local_size_y = 16) in; + +// Read-write access to the current grid +layout(r8ui, binding = 0) coherent uniform uimage2D state; + +uniform ivec2 u_gridSize; +uniform vec2 u_brushPos; // in grid-space (cells) +uniform float u_brushRadius; // in cells +uniform int u_mode; // 0 = none, 1 = add sand, -1 = erase + +void main() +{ + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + if (pos.x >= u_gridSize.x || pos.y >= u_gridSize.y) { + return; + } + + if (u_mode == 0) { + return; + } + + // Center of this cell in grid coords + vec2 cellCenter = vec2(pos) + vec2(0.5); + float dist = length(cellCenter - u_brushPos); + + if (dist > u_brushRadius) { + return; + } + + const uint AIR = 0u; + const uint SAND = 1u; + + if (u_mode > 0) { + // left click: fill with sand + imageStore(state, pos, uvec4(SAND, 0u, 0u, 0u)); + } else if (u_mode < 0) { + // right click: erase sand + imageStore(state, pos, uvec4(AIR, 0u, 0u, 0u)); + } +} diff --git a/shaders/sand_relax.comp b/shaders/sand_relax.comp new file mode 100644 index 0000000..dfbe34e --- /dev/null +++ b/shaders/sand_relax.comp @@ -0,0 +1,135 @@ +#version 430 core +layout(local_size_x = 16, local_size_y = 16) in; + +// state_curr: read-only; state_next: write-only +layout(r8ui, binding = 0) readonly uniform uimage2D state_curr; +layout(r8ui, binding = 1) writeonly uniform uimage2D state_next; + +// claim buffer to resolve conflicts +layout(r32ui, binding = 2) coherent uniform uimage2D claim; + +uniform ivec2 u_gridSize; +uniform uint u_frame; + +const uint AIR = 0u; +const uint SAND = 1u; + +// How far down we scan to approximate column thickness +const int MAX_SCAN = 6; + +// How much “taller” this column must be vs neighbor before we slide +// Smaller -> shallower, more flowy piles +const int SLOPE_THRESHOLD = 2; + +uint hash(uvec2 p, uint seed) { + p = p * 1664525u + 1013904223u + seed; + p.x ^= p.y >> 16; + p.y ^= p.x >> 16; + return p.x ^ p.y; +} + +// Approximate thickness of the sand column under (x, y) +int columnThickness(int x, int y) { + int t = 0; + for (int dy = 0; dy < MAX_SCAN; ++dy) { + int yy = y + dy; + if (yy >= u_gridSize.y) break; + uint v = imageLoad(state_curr, ivec2(x, yy)).r; + if (v == SAND) { + t++; + } else { + break; + } + } + return t; +} + +void main() { + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + if (pos.x >= u_gridSize.x || pos.y >= u_gridSize.y) return; + + uint self = imageLoad(state_curr, pos).r; + if (self != SAND) return; + + // Lock bottom row in place + if (pos.y == u_gridSize.y - 1) { + imageStore(state_next, pos, uvec4(SAND, 0u, 0u, 0u)); + return; + } + + ivec2 dest = pos; + + // Only relax grains that are resting on something + ivec2 down = pos + ivec2(0, 1); + uint below = imageLoad(state_curr, down).r; + if (below == SAND) { + int hSelf = columnThickness(pos.x, pos.y); + + bool canLeft = false; + bool canRight = false; + + ivec2 left = pos + ivec2(-1, 0); + ivec2 right = pos + ivec2( 1, 0); + + int hLeft = 0; + int hRight = 0; + + // Check left side + if (left.x >= 0) { + uint leftCell = imageLoad(state_curr, left).r; + if (leftCell == AIR) { + hLeft = columnThickness(left.x, pos.y); + int slopeL = hSelf - hLeft; + if (slopeL > SLOPE_THRESHOLD) { + canLeft = true; + } + } + } + + // Check right side + if (right.x < u_gridSize.x) { + uint rightCell = imageLoad(state_curr, right).r; + if (rightCell == AIR) { + hRight = columnThickness(right.x, pos.y); + int slopeR = hSelf - hRight; + if (slopeR > SLOPE_THRESHOLD) { + canRight = true; + } + } + } + + if (canLeft || canRight) { + if (canLeft && canRight) { + // Prefer the *lower* column; randomize if equal + if (hLeft < hRight) { + dest = left; + } else if (hRight < hLeft) { + dest = right; + } else { + uint h = hash(uvec2(pos), u_frame); + bool chooseLeft = (h & 1u) != 0u; + dest = chooseLeft ? left : right; + } + } else if (canLeft) { + dest = left; + } else { // canRight + dest = right; + } + } + } + + bool staying = (dest.x == pos.x && dest.y == pos.y); + + if (staying) { + imageStore(state_next, pos, uvec4(SAND, 0u, 0u, 0u)); + } else { + // Moving to new cell; resolve conflicts via claim buffer + uint old = imageAtomicCompSwap(claim, dest, 0u, 1u); + if (old == 0u) { + imageStore(state_next, dest, uvec4(SAND, 0u, 0u, 0u)); + } else { + // lost: stay put + imageStore(state_next, pos, uvec4(SAND, 0u, 0u, 0u)); + } + } +} diff --git a/shaders/sand_step.comp b/shaders/sand_step.comp index 67bd284..be89790 100644 --- a/shaders/sand_step.comp +++ b/shaders/sand_step.comp @@ -2,10 +2,25 @@ layout(local_size_x = 16, local_size_y = 16) in; -layout(r8ui, binding = 0) readonly uniform uimage2D state_curr; -layout(r8ui, binding = 1) writeonly uniform uimage2D state_next; +// state_curr: read-only; state_next: write-only +layout(r8ui, binding = 0) readonly uniform uimage2D state_curr; +layout(r8ui, binding = 1) writeonly uniform uimage2D state_next; + +// claim buffer to resolve conflicts +layout(r32ui, binding = 2) coherent uniform uimage2D claim; uniform ivec2 u_gridSize; +uniform uint u_frame; + +const uint AIR = 0u; +const uint SAND = 1u; + +uint hash(uvec2 p, uint seed) { + p = p * 1664525u + 1013904223u + seed; + p.x ^= p.y >> 16; + p.y ^= p.x >> 16; + return p.x ^ p.y; +} void main() { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); @@ -13,30 +28,90 @@ void main() { return; } - const uint AIR = 0u; - const uint SAND = 1u; - uint self = imageLoad(state_curr, pos).r; - - bool canFallDown = - (self == SAND) && - (pos.y + 1 < u_gridSize.y) && - (imageLoad(state_curr, pos + ivec2(0, 1)).r == AIR); - - bool filledFromAbove = - (self == AIR) && - (pos.y > 0) && - (imageLoad(state_curr, pos + ivec2(0, -1)).r == SAND); - - uint next; - - if (canFallDown) { - next = AIR; - } else if (filledFromAbove) { - next = SAND; - } else { - next = self; + if (self != SAND) { + // AIR cells: do nothing (next is cleared to AIR on CPU) + return; } - imageStore(state_next, pos, uvec4(next, 0u, 0u, 0u)); + ivec2 dest = pos; + + // Hard floor: bottom row does not move down + if (pos.y < u_gridSize.y - 1) { + // 1) Try straight down + ivec2 down = pos + ivec2(0, 1); + bool canDown = (imageLoad(state_curr, down).r == AIR); + + if (canDown) { + dest = down; + } else { + // 2) Try diagonals + bool canLeft = false; + bool canRight = false; + + ivec2 downLeft = pos + ivec2(-1, 1); + ivec2 downRight = pos + ivec2( 1, 1); + + if (downLeft.x >= 0 && downLeft.y < u_gridSize.y) { + if (imageLoad(state_curr, downLeft).r == AIR) { + canLeft = true; + } + } + if (downRight.x < u_gridSize.x && downRight.y < u_gridSize.y) { + if (imageLoad(state_curr, downRight).r == AIR) { + canRight = true; + } + } + + // Flip which side is "first" based on frame parity + bool flip = ((u_frame & 1u) != 0u); + + ivec2 firstDest, secondDest; + bool canFirst, canSecond; + + if (!flip) { + // even frames: left is "first" + firstDest = downLeft; + secondDest = downRight; + canFirst = canLeft; + canSecond = canRight; + } else { + // odd frames: right is "first" + firstDest = downRight; + secondDest = downLeft; + canFirst = canRight; + canSecond = canLeft; + } + + if (canFirst && canSecond) { + // both free: pick randomly, but "first"/"second" swap each frame + uint h = hash(uvec2(pos), u_frame); + bool useFirst = (h & 1u) != 0u; + dest = useFirst ? firstDest : secondDest; + } else if (canFirst) { + dest = firstDest; + } else if (canSecond) { + dest = secondDest; + } + // else: no diagonal move, dest stays = pos + } + } + + // Write into next state with claim resolution + if (dest.x == pos.x && dest.y == pos.y) { + // Not moving; just keep sand here + imageStore(state_next, pos, uvec4(SAND, 0u, 0u, 0u)); + } else { + // Moving to a new cell; try to claim it + uint old = imageAtomicCompSwap(claim, dest, 0u, 1u); + + if (old == 0u) { + // Won the slot: move + imageStore(state_next, dest, uvec4(SAND, 0u, 0u, 0u)); + // Original pos stays AIR because state_next was cleared + } else { + // Lost the slot: stay put + imageStore(state_next, pos, uvec4(SAND, 0u, 0u, 0u)); + } + } }