From 0a98d4bf232d2424df3439cebe79441d666864c8 Mon Sep 17 00:00:00 2001 From: Digital Artifex <7929434+DigitalArtifex@users.noreply.github.com> Date: Thu, 23 Oct 2025 01:50:57 -0400 Subject: [PATCH] Added Heartfelt Shader --- data/shaders/manipulative/Heartfelt.frag.qsb | Bin 0 -> 11197 bytes tools/src/manipulative/Heartfelt.frag | 213 +++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 data/shaders/manipulative/Heartfelt.frag.qsb create mode 100644 tools/src/manipulative/Heartfelt.frag diff --git a/data/shaders/manipulative/Heartfelt.frag.qsb b/data/shaders/manipulative/Heartfelt.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..478bc31eab93eaae3dbd8e9b4c4b41006032ae73 GIT binary patch literal 11197 zcma)gbxa&i@NX$@#W}2Ku|o0UaK&8;#l3iOKlEDQ;3!2}io@a3qQ(7y!`~)^#v1PY&uuX57kOE z1vg3h?$cd1YEtnl#x!JiKEA`CG=Giab zB3lFWrp+D;?S0WnrYa&=ZJH1Crb`Se%v(zeJ?QN1dp+#-DkfGRGtV<31osF;S?xQG z_CH;M-`mfr910kKGkfznhw3*8T=HLW$(+@)AZ{gway%DguZp^2p&9AnZn2LssJ6G^d%C>ScTUVu5K36QGTFR`2G+Dl~JJH=*X?qS)3 zv8-*WSg?IWHPn1#XTqX~v$Kp?G}*lbgGZ);48s*`T_1l*y)*Nzy99~rhc!Er6S{Bd z8V&s47E|2;IHuU^<}B{b#3!0nv#7h%^-kCe2BtL$Gj4;IN!w#eRX|GC_!WxEd@Wsn z+4d?)xots=q3uAYKjq^hRyXVZ7LORdYH6ng))huJgO5Uz=`gYgw}^j}&beqHgSSec z!mGs>Fa?1t)(kbDBPH8=($S}jmaKR$Xa)MFUAnJwlaapEd6YfYboJMPorEEt2`jYz z7ik2juS={L_PE&FVh0jD*rHku_6`k^&bw5NIM<{E515hc_S}*1XpMc$!PJl_M06yz zUC>aG@Yjbtl{V6|xSX@F?i;CyHN7d|3x>aW7&k|Fx&Powx@PB5*fwR%t#K?SuhovF zM{dX#i7rF=n*_(Rlx?eVrBj`OCN+WYYCKlV)Tqsi;7%uPvyoOyD{bzxwq`Uh+a{yz zUnlY}2Ruq0ZIr~?1v--l$lPL%Jq7C1^&%DSnn$U@;z{dj@?f+FMf5Vm5u^!0(BXHU zW5KagH`b-k9bSar4LguU7uSb=Lt+u@=@-`r_}8ezrHG3`qa6brzo>ZsBBsV2hX`;1 zywP-I_An&gf7(d;;p%Gy>hw0n_8`SMqssp z#cn43D6aV9Sdegw;`Y@LmF;td+u*lH03w>;6Hz;cn8zYKWOGwtW*ftaH6=OMLz~oC zd&SkIX5)XRrJ-PWS9b`u#4CKL;gHI({{gMP`O?qMo<_y;R`F6^rJz-BY6xZ@9w}Q5JE*@T zV?<6v>bVfHy;R&EKM8xj9BE^(4#|n#wI%fPL7guq!1dG}j$9*Vha}P`W({jOtvA(a3Vsp6rRdj2cZiZhD!2+Q>d}ii{<6{f-D1n(?aD)tM?T;^SKj!OlN1`FpymkJyP} zAw|S6mms$3exwmbRxZ=mEVYJeWy({qVb-pgsM6WB?8L6~2INd6o>wyKK*D57MEUYx zix)H&&RwBgvkUbl?+3S=(<;Ya%rbQf4)cvGgWr)n$xe*3wEbFJln%w{?87hJA%4S-#eX(KAgi z8<$KByKIPe2WSU>4dk4z=Smbj9xOg$mI?efc5Iu!*q!8hAMYB7w|qr98khhZB@Sr& z+2?wdIMU~u&S!#~S{PNX{iN_XgW&05$yP!8rU_l*s{v@WI>!NvaJA0npm zwph!nED`OjDzK@F=v*(d0ZfJ(OgXdb^lEAN?rwnBR)Wbe75sj@L-3~01QP4U4sXnO zK)w!dj@VmO6fJ}&*$6)HoC%uqwxm0}AhM=$KlN~T9xC*Vzt`-n9+^!54^Ul8ccMS~ zU|j1qubx`|Xo?+JRTJHL{2KN&dXcU5gGL1P!6KMvB17+y8} zbZ{lUx=E?*jp?LkAiE4=B{+ku{;Y`bKhb-z2;_!6qovT^*bp#vi+WN<8E>@0V%0 zavxiHx!iScCUY+(6DTr$?b2EOa4JD1NB#6Ij6d1-VGLLE1kC7+VyQw9DJ+JlpM|_} zJ|n9tG0l4TjpRPG4_{=ndCb^)R7XCBdT$Cp|8fTc9#LvC&rZ4asc@#H3XDg$|HkVd zS*7=_Bu&q;$Wlab*R_OI^(sC?OWZ+IBoayBHSs9BlSP+NpM4aEah*^KlxPq}g8}i< zA0Y$z!}M<+&}HFpQdscSVfbd#v!?Lp8>Tanm1ik+AtI9JRh(YyGlo|q^90Y69b$>S z%W&vUR_M-0>|N{?E@{OErOdUAue~>$Z#Ma@EEr!lSr^k?1cW1(V#7NdGiG%JKUF4$ zo>!_tu|QU_Sxg{59S8KQFeAK0j^0aW2q+0&jaM=Kp~snGZ%Jl5lH}bKz7uY{my}mA zJ$0C`9@HKr0(tBXsce=_Tb4HqEVdjv(e&vMwD1TeMbaq;bl&YVS|uQuowGhc?Qh%aIYR7fqcJpk4x&pd)zW$tBDN65@3}zdZzV-84Si4b z_U@%*BSU4|%)cEPKu^h}n+-9&^x%~pzo1V*RVLV>+28nQ4i>khj(05noY;#spK7cd zJAFhCt7VxUJ!QOvZeL8&<;9;k74=3h95^gY~%`oGE65R zt8_9wzN`$!10ZLo@Tfl2ez8;^8g?`4)ky;pFu(^I=Y`$`eaHJOK9=>j)2qwzt)x<^ zp0ZaO-o}$S#B=7mFF`S*Yeim@63qCpw6he9qb)wb|$D5p*x_uMnA*49zN#>v=tj6(!BT$hK>U{w;*^!4to2D3rH; zZMGOGJ*`_Tc#0hascVi#*FjedkX1xH+V;@^T7uh#h3e-QD<>WeG9lj(#?MOA=5WK`M}Nc= zUEy-kYcNR5_uN}8dn=kA{IgSf_xVN4k00O>zZ_lBMU^ly3gO1e+aP69nMg z!+#%*A{Fo+zuUH{GYu;+W0`V)ZXYauw-yM9MtMfbGT5iL{aRq#JSxCLIl~P&x0*jq zU4B_7s>p3>kF$^&T|-ppcNpK}L7ZH&8#5d%=}eit1Is%9b)xot{)$Pr>fay^S-inv zvXb`*T8a&uA-K-olcLN$8^p()(>tKL6Ez@Yz?mN?Sc}%PqZj)01+q!;d35@1kX=gd zoL4wh%1hx$Oh*!^D>+IwI+}Yk#kqknDZ4or<_a;jPJ}Opu3C><*e8O=LV;h#S1miT zYBr`9De5LOv^c}(%}=%GTshFCR+)l!GVwOwBTZRA<$4YbCkcL>`K?KJ7)K}`{-hPI zgUGrzgT5y7DWc;x*a3FiHK*h*ChQPn^q%4hH*TAVBR?28O?376wfI@?6L1nN&vb&2 zC%Sro#)I?5aGeA5#`dlA$&gEoAnEckk%m|;j{HZ_X|&)QT$yK1NC?7ue-iIewr6XU zqsxo#rc}0V6sIkn{4R2<*@$TcSfbMxD7gRf2_=>@_zo{PqW7_Erolp8Xeh8`?NJg{ zC2f)I+b6aWjfWxt_n&3)a^>Itim#4YVBHQxXj|li0 zvHN|I;^6uHcR+nK2<RW-zVX2K zUFK^^z&v&7xHm#RQ3id*1P&4&F4`wDxOC)EnpUrQsI7!!Y8hYf)~ubsyJ4ja%3(dV z)xYH`Ju@)cuBclZe0TGn@^4B$e$pijNtmj6=+vlH{ z&nuzNdl9>#Zf`G4M*JFd-|tEUsr|=Clhyy)oHuBVsJF41JZbQ9#*ci)E17y!B2}`m zSwYHE9UM0RQV+3UdexRpe1eT<8z_{Tp6=*<&Li3Tv%VN#2HqfrB>2fdGSAe+4w zgb09DFFUB7sJq$v8t*9jpCNdjydfH(5rdl;*`*3dkii~ED?Api2Ze{vbYH*yeQq*= z{<*g`AA9cN`pu>^3E3&TWbZhTcEYV&IN!n$Y85H1`B9 zKlS)xopmmaRNcn2#p!F+-ZnNDOnu5nQPK6^m&4Pc%lX@TOyaX2KiH`_D7oEz>`c(WS*K_R~M( zidonX_I!R8DouT41Rha@7!n_!6#1T;`u~n@V3j92tjOyEnICMVsrPi*@@yEzZS!o@ ziJ`6HbzpmeHD9sJ#VnUSX*kbCol6bPLs}-23SLHnDo8 zsIg;y?S8Tbv@$-kVjP(uiWgt6cFuK6ET>=?!A?rCe zz<-210LHqPrFJIPBW>`ztVkdtYj=Y_%*DShy5X`DK1%5u*!I30+pOyn>LJCk1z2`3 zU07p2p%a11(1t%S0j@u_nA7>DkKU{DOb|u~NCGyAW~=)ZmK6DY2YFU_<){yzUjnve zsAv7li_c{{V;YnL%V|rgmE<4wL?r!UqNE`$SKac6yUbl?!cmjTQ*o`n5|c9;S^Bw? znE%*PspN#SGE8|&DIz)Q!a}nKd0e8jdM=xS@6gREq-?iA?*eDqIlUcO^&r zn#vwq-X!vzur>mDeSYpFb`jn8sX@$>qv|?gX96F6Ar4$eycNW@6%;Oz)*UB>`9lR0 ztJT|p@fi_q6B(jnqOF&}vCJO(GM)*sEx}4aNS?!adZ{oxbzvwlx5Tuob)L|ZVvgRR zxerXSkA1&V0y%M`xcVxEG*Osegh*KeAACPvy|N;jqlL@;Hsd-%yWetxNUbPby^zY5 zj>msECBFjIM|(RH%{9YK`oI;@yO@&frn?*dkc`=KKax?@X)w>38ShrhYPT0!()l+W zF$QmvQBUbDW`;Gf@}N0|TZUX=gi(8>f|N+Zy4bxUzw5dm4{|YyR{k}E7k~K@N6^|P z4~+k2_czamG~2;Lc=7q9er6VlPp6yx+O@z3gvTKpz)_m(q9OI7L1k{w%Z5eFZFPeg zX1n}fhuU>q`*+xTE}Q0`kJ6ZBRHQtqkz5AND@Y#J4=c3s;)_{$ptxhUmng@JEPO~t z>u%F335U4&D!_WbS5L4GZHvhcZfNFTkNCS5-K}5Cu6m;CWtZGmeLYsJZ+H%r=(AQm ze(vy$N1Qf+Lc9yZ;AlqJLR9B2MK*1QSg=;#qx3DtXgbiHq&{x1 znV|`~x`T0kLj?K3(7f*d%+9mXq*u9f_lNdK5X@}zJk%`!1EXDg0Z^WgH~4_r^AOF< zXm2=UI%0mF7ovIH0D|?vv2V(vMW05POqRLsNJ8M4@Zq~M+KkPIk!~;XCyk+Co{bY9 z^uQJNgVt3VYp^@w{J$^3Wvv$XwrlK;dv=kQ>-%ckS$G39;o6M|r};@??otM@EwzSK z1E#1U`t{1a@WvnqvC94WNt167q}+GYycZW~wiT>Abq&o9o+G>(d3HyL@y0=`z&i8a z#I-#lps3Oz2qxA{hKo>WR1SqjUUC$BHrTs1ZCJLkY@^glmr( zt@3Mf(VXTlUY`%CTbh07y;|V>RwGFEtzuX;yTVMfO<3YvLni5eDkWTOQH;bK==UaV zcC`6Td^rnAsjVN0z)n2DlhkxME;qJh@6@vLtQn-d*`(>@=Z7U%*;y%!N0>a(BLQ+H zDmyF3nOCNnX#CDz0e7hvX}BWtP4wx^a7F6^y$@UL9xwPx4MTuPIlOoOTa;x+r~jLW zowFA*U~T3j32b&^KTBq+L2UdxIVfz=yV$k@K?;?S-}6hUl=-#i|L6sJ>4CIbA_>=h zDU$3?e5zH@xY37khW==Jpq3z_Wc$!8>uwwHjFsVC?*1^!RM42NRvXZ0tg=(J9~&Ex zCoDas>`HYlZa(*ODr@{ROO|8>e?#G1aUy#XRfdSpJK-1p1BM>OODVtS&OZ3pOobcj z(KwRJ&24_^9-mnF%%a|0E-Eg}JhqLOz}(U(^jj;*;C36Q;!o@>x;&4OxyfGxr5@TQ zJ*Isw5elxv2^@|ZX>grQ_OGv_zq8=^dDbmDSAT0iRw{d%KC@{1!L)St_laW@uD46XD}Bjlx=JFQE`AFOB`=q$W;4-I=CboyG>s=r0%} zu+E&S#R4X&@n9b0DB`V68QOccajFzF3+|h;l>t-R)C=Lf2^#hS5Os^3BCbD!0*xze zPft%PrsC=Sly{!NsitjXi<9Zy8>TrG27qhFx)s?t9u>fuk>$yeJmT;>fV(>_q9kU@ zuv{&#iM?L)4S-R2ay4^5@*vRj)>|tFaCl<#Wc-KUyhM4r_J`|#m**JXqo0$3j%7Wm zFX_Jot0N3$X?PxbSxtEZc1{28lX7nboLDL(pA_@PdHCzJ2!4REuFXVrwVYE_jCOcBuFhs=E?iT)H5e% z%J|ZI0F6(7ibaL2Z~(FJ<~$+QJ4YfbQuAo^+V)Ri&ByE@o#fqsv&NGZdY4CMrsPoy z!!(bK@01vtw&A{bk3HEDs@GllpU{tfonXcBY5wH7D8f&zDPQ{ZHZgoA=J#7#+H`N- z8tA_rHUc}L7-7{qb{FYJ**f;ctdXnh?LfiD)F5LCD?!~Ek&klq|8nomCP3Zmzu3OI z7Kt~d@XCu|l;K|ny-FODdy{@LdUbhyk&nM{ILtTJ3MA0z4e-hAIaqeICMp)#8kFl) z(bQCsc&++3nX8GySYJ5#bpK?3csJ0qNj3g&ucd`wP7y;d^IhgXz`bF*TjjM;AyPaH z_(-rkh8jLH=QH$1&=%4bu-GJYZ|NNEnmd$Vfs7jd*)7!;-&U2i8YaKA%C8Qsh!D>f zuX5F$clvq{$%sp|d6_0ts27Lgpr>qvos{lR$hy@5XGaSOdlZBX_KqX^sHnsO3<>P= z-yIP0D;rVpEB`=*d#8^P1iW#iG2_N(^Xb^P&lU*rZqI$U9Yd?sp4g$p*p&*Wd(W;e zQV4YjAw8Jhh^9T@eEr-G{*j!9hYq_{D-r|IZr6ozQ$hDnr0H7bO)aD+XLa{R^97}T zZc?%pD^jDzPCmV*$~U6VZ`7Qa7jJ*{|~6OGE|u+L_lP<)h8{eyPzU#mC( z6TeW`bMwA$RbpxX!Le-JgcHP4`Y}(RJ8tg4hEsW45aP_Um{)Y*j3(FQ`l$?miE(mL z);fDnzA9&Z=WlF@9#@QeFuo6&;2fqnjop1fWtvvxR=3WlsGnfv0o9K4ZxrL{prSYD^OfKE8o5a>G}n z!-tbo|D!XLjRPS%lf5q<^Wpsid!F=%01!sgW^w8k?#oWX)DBorB*cwomOwIPFT8H? zy&hzdV0`x4txR(OpjHSd!yUF(%o^w6uD8n$7=ZcSSiLz0+CCIHJLqhPecNS~2o-q~ zpxsP9wD-BzmP5?OMm7f1ggW-NpF~UxlZor^gsJwQ9Qq;q!1t=^wHh9(+HrR31!)Qf zX^ww|+}>hFri4@;IqGl#{%FG}@HDpbjPkTcIDWgBUpeqn4evn&9){0qB!z-`1^Hb# zI*lTle(@F8AXO&O%otZC?Hu&l8vojRB74Y`qG{Fs?8Fdp&vlwGEF&iThiJrDRWJKg zI+DswjsRBk;tcHYE#|4RC>x!$q0)r>F@=7d)X(24u>z*GQ(Q`TmS{RyT!3p{?IRS5 z8#L9L{dXmp*;Hk(ieA2TIjqq$hba?!v(AnU02hZ58fuGargk&*>wD#?SU>oUv4_S$ zhy54hX@k~?&tyg|oL%(ai1_xzXWWORS07Ah{v_w(4*vzbSDmye5J{BUhe@Z@+|?S- zOB-X<`^uW4*H{c@@D|w}dn(20_YTtmgnrsTM_DWOIN2=IrA%>$q8GY|WK=+TKld{m{yu@het>4!owLC3M17h@rM!b$D(gFAKFBmv!twRTGR)v+J z1^qK$e103xhXf^*>^+89lxG`Q7=aaM)3DR^q>{sw!_lj^YU%Myk0~Mvf4>;?g7bg7 z4J<;W?7zZn)=1}XyTf2d6LU#sssvEXpZn7gg z`bgPH)5(2pNrFA$5a?`LKaxLnD>Ux5;JjW`zl8D4%q5afhw4vZ0N%*Nv4+*$7QUR} z(Kx|k-nW@`q@}FQ+k$69?3aI%xP121H@z&Ae5EGlqk0vFBVS)yuDBUzRyjdE-ij30 zTz$P2n5+Q_MYD>cJMs4snoOn%5}JNgUxb(A>c7d28doE8mi01%4OYoUF&VSZ4ZeA% z>#{%~<;?4Hx=c8i=Y;%>+Tu@VnZy&+uD9;)&i7l)k0#MRH}$`;L*<$|%2Z92db-t6 zctREF`Gh6HeUsK+9dm>r*);L?+Aq z1bwe{n%T;HZ6`t)?lUt`fnu9}UTh4mrjWn@9N#Rt<$zs&p(w4XB8qAwgpmS4n7QDq zy8u3B(qrP%^H4G6_-ksy9L%h6 z`s{^6F<$ejqN8kut6Irr?hu@AFI2gK(P&@xQQT^uooyY>49kmF6LaS0tOrOk^-Sw& zfpdG54gV%C6}^8&uR0NHa*e;ayeeAj!6dc5ZK!Z*060rbwIQZUtUJ{uRDE! zRoD?@Dy~K{`Nu(8k@s)62NcDpLU-=5vp>(%jO|~==+%c`?-PpSP2y*0mKF)x5O031 z9HjXCBgs;uQXeY}WMa^Pxe#RRwuiFBNo!*1U*{)qKD;+Q|yk}9uc{c%3waamXrf$lz{%m)2!}D!88m(Ql-}WfRrlYc=U6SmNIBJWLvL*D`4oA5X+Df<-T_Sgx8?042-}7B=S6`~a{7ZgB zr+uHgYJD%IOIs?XUn-^1wGF-*3Jc$jPM3aQNNB@L4;Ny)V;W1PA_op=e| zLuPdf^Rx=~S7nq|GNe0DKBRPX6K}4@I;}@^4p22{N~5tm`5Oysg9Jl6Cwub&hx0(y znz-qm9{Lh`nMqjnKb?sRHYw%$sb6icF3N1}Wvc|=+1fMP+Aj=@MA+Dy42&oajMOD& z;`fi#{?3g4O?dITK^F0<+U?NRXwMhsB|VcFu(2SBWia#i@@_3E@gE9H31R2hwDZk+ z)YN}~)5)OVUkQs%hQLZ?IP^JbcKr{aGx=L6hcv&B>dVe11x7NzpONC{ z|0duqlTME3_MHx|BA65c7|C@|&4!L-B0n^1-mrVWbP{5Z$y)huQjZiOlF5yCR$1py zzCtQCuu!O@%?2@#|F)b~XQaFJ5#&O~+2URqH&DPxVJnRJXF@tzw$5FNRTngDnxTOr zXVpzXy>MMC(uF$Xo%R>#)(jLP(V!l>Gv+r|hIu z5pB(l@p=Z>7=n& z?Z=R|ZZeH}DAoL8qT70{MgwvWr_%|~af({YXaNFc(9w+U!ax1f*Mcn6Y(@HlP9P$B zXOQa)TMJOg9Ymz;0e@7r9ob1uwL+%affVGEcdF$w*&>lL{TV>{<4#Vky3HylIaa6pHC6-jp-8cF*mx z)XnW#Bafm98;+u(ld>u7$sU@D$6zT~NJ%VNf6wX!TFfdV@f_Uoj_Gi3inu1KI39uMuyhL5%@j(n!o?LX%znBj0S3{9 zU9U=lpJOB^tI!{@JJRXQMTkX|%IlRWqt`31G!Bt-iyReUmZ@`0BW%V+KA_3rLnPln zmW`tV7vsoNPK~J7IUz2I^Xpb$ln%AWnClV9TD!oza|z`bcG$YiOVy4M7aAjalEtIv zKk9MX(NEW&QpNs>*`q}x*T^(7Y004*WUj7s3p9f$?w526XSH<8UJa4pu+)4Dk=yqd zv;z;>tSl?-_p^@6pn{sFnns~tU)pTb&84Be=TIN{dNXP!b3W>K$Y)`82o*x6qWv}6 zYF&*AK5AhL0xBbulT?@dN|{0%18%Xw|}OL3U~op7>UQ^xoB!!aSSp8KD`K2-~4?sl!uTMh-&tasoqo{vZ=kAZB!+;@IQ&IgA;lDBc z|8yRqv9XQY17m#+zQcKnYB3vqmaY7fIxCgxYlqw8>k3G%>q6VtuQwT)jW}bI4y?O# zbSB*2?e7`ZaQHK0?2qdde1q3l1oZb5>YUw7AL^&S4zi!1tss#3Xy7?mcs5pO(+Dd~ zNAd~$(Tm8SA1+P(p14IroPI%Ip_G@;3ZN~>``}nCE|&c${DLjn=QkJ2Hr|?s-d!9U zRZ7NFqR(VB>zEq*QF~SVIq1qlWQ8V+X`5-x_vkXk;<&`r))2QF|P*6DpkE-TpiB@G{s{32R?u(WPh98r%Cb>D&G`^H92gG!+ur z)Eic^$&mgoGGtu>OE2P;Kd}4CiA=R^&U`i5@1<_*MyLa#k?QO{K%V$AH@cYr(jp$p zA7fx9PdJ|BNEM^+Pldwgg$)4Ap>rx`#PAE|kp1o&i-2{C!q=SlNmCaGK8LbIc_ zWQ%yk_55gd@s4Shoh4sEQz4r2Ll#sj<&Eg5HFgUaVQ>iK9A>$DO&8R*nn^yYX_cy!UE@q^QnW1)Rif)Ymj!;E3*e>;T&#E6ZtHm2d3qYR|C!Afd zPgj3bB^J;dOb+d4&Ym?OjLrbtX% z3p=A{%Wd#<6~&vq=ORlcAO4UNiV1#2z=sYs%>NqI~5M-#kbaRr{{R3j7$L86|pu zRNPv?)_(1ovbgFip`oDyk*8@zh12|Nk4H%hjghpeCGbiDr=m%4dAC*lE@*X*La%sL zWF~LtIc)kVb&nGU0{3%=o95C@0zKhH$6YCZ6O9C>e(ULU+mQCLr8+vRR2cpI-+*S6R*zbi-?flcd0k#ndnW*Zy$gIsE@1v3-#l5H~;_u literal 0 HcmV?d00001 diff --git a/tools/src/manipulative/Heartfelt.frag b/tools/src/manipulative/Heartfelt.frag new file mode 100644 index 0000000..922a70c --- /dev/null +++ b/tools/src/manipulative/Heartfelt.frag @@ -0,0 +1,213 @@ +// Heartfelt - by Martijn Steinrucken aka BigWings - 2017 +// Email:countfrolic@gmail.com Twitter:@The_ArtOfCode +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + +// I revisited the rain effect I did for another shader. This one is better in multiple ways: +// 1. The glass gets foggy. +// 2. Drops cut trails in the fog on the glass. +// 3. The amount of rain is adjustable (with Mouse.y) + +// To have full control over the rain, uncomment the HAS_HEART define + +// A video of the effect can be found here: +// https://www.youtube.com/watch?v=uiF5Tlw22PI&feature=youtu.be + +// Music - Alone In The Dark - Vadim Kiselev +// https://soundcloud.com/ahmed-gado-1/sad-piano-alone-in-the-dark +// Rain sounds: +// https://soundcloud.com/elirtmusic/sleeping-sound-rain-and-thunder-1-hours + +#define S(a, b, t) smoothstep(a, b, t) +//#define CHEAP_NORMALS +#define HAS_HEART +#define USE_POST_PROCESSING + +vec3 N13(float p) { + // from DAVE HOSKINS + vec3 p3 = fract(vec3(p) * vec3(.1031,.11369,.13787)); + p3 += dot(p3, p3.yzx + 19.19); + return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); +} + +vec4 N14(float t) { + return fract(sin(t*vec4(123., 1024., 1456., 264.))*vec4(6547., 345., 8799., 1564.)); +} +float N(float t) { + return fract(sin(t*12345.564)*7658.76); +} + +float Saw(float b, float t) { + return S(0., b, t)*S(1., b, t); +} + + +vec2 DropLayer2(vec2 uv, float t) { + vec2 UV = uv; + + uv.y += t*0.75; + vec2 a = vec2(6., 1.); + vec2 grid = a*2.; + vec2 id = floor(uv*grid); + + float colShift = N(id.x); + uv.y += colShift; + + id = floor(uv*grid); + vec3 n = N13(id.x*35.2+id.y*2376.1); + vec2 st = fract(uv*grid)-vec2(.5, 0); + + float x = n.x-.5; + + float y = UV.y*20.; + float wiggle = sin(y+sin(y)); + x += wiggle*(.5-abs(x))*(n.z-.5); + x *= .7; + float ti = fract(t+n.z); + y = (Saw(.85, ti)-.5)*.9+.5; + vec2 p = vec2(x, y); + + float d = length((st-p)*a.yx); + + float mainDrop = S(.4, .0, d); + + float r = sqrt(S(1., y, st.y)); + float cd = abs(st.x-x); + float trail = S(.23*r, .15*r*r, cd); + float trailFront = S(-.02, .02, st.y-y); + trail *= trailFront*r*r; + + y = UV.y; + float trail2 = S(.2*r, .0, cd); + float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; + y = fract(y*10.)+(st.y-.5); + float dd = length(st-vec2(x, y)); + droplets = S(.3, 0., dd); + float m = mainDrop+droplets*r*trailFront; + + //m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; + return vec2(m, trail); +} + +float StaticDrops(vec2 uv, float t) { + uv *= 40.; + + vec2 id = floor(uv); + uv = fract(uv)-.5; + vec3 n = N13(id.x*107.45+id.y*3543.654); + vec2 p = (n.xy-.5)*.7; + float d = length(uv-p); + + float fade = Saw(.025, fract(t+n.z)); + float c = S(.3, 0., d)*fract(n.z*10.)*fade; + return c; +} + +vec2 Drops(vec2 uv, float t, float l0, float l1, float l2) { + float s = StaticDrops(uv, t)*l0; + vec2 m1 = DropLayer2(uv, t)*l1; + vec2 m2 = DropLayer2(uv*1.85, t)*l2; + + float c = s+m1.x+m2.x; + c = S(.3, 1., c); + + return vec2(c, max(m1.y*l0, m2.y*l1)); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = (fragCoord.xy-.5*iResolution.xy) / iResolution.y; + vec2 UV = fragCoord.xy/iResolution.xy; + vec3 M = iMouse.xyz/iResolution.xyz; + float T = iTime+M.x*2.; + + #ifdef HAS_HEART + T = mod(iTime, 102.); + T = mix(T, M.x*102., M.z>0.?1.:0.); + #endif + + + float t = T*.2; + + float rainAmount = iMouse.z>0. ? M.y : sin(T*.05)*.3+.7; + + float maxBlur = mix(3., 6., rainAmount); + float minBlur = 2.; + + float story = 0.; + float heart = 0.; + + #ifdef HAS_HEART + story = S(0., 70., T); + + t = min(1., T/70.); // remap drop time so it goes slower when it freezes + t = 1.-t; + t = (1.-t*t)*70.; + + float zoom= mix(.3, 1.2, story); // slowly zoom out + uv *=zoom; + minBlur = 4.+S(.5, 1., story)*3.; // more opaque glass towards the end + maxBlur = 6.+S(.5, 1., story)*1.5; + + vec2 hv = uv-vec2(.0, -.1); // build heart + hv.x *= .5; + float s = S(110., 70., T); // heart gets smaller and fades towards the end + hv.y-=sqrt(abs(hv.x))*.5*s; + heart = length(hv); + heart = S(.4*s, .2*s, heart)*s; + rainAmount = heart; // the rain is where the heart is + + maxBlur-=heart; // inside the heart slighly less foggy + uv *= 1.5; // zoom out a bit more + t *= .25; + #else + float zoom = -cos(T*.2); + uv *= .7+zoom*.3; + #endif + UV = (UV-.5)*(.9+zoom*.1)+.5; + + float staticDrops = S(-.5, 1., rainAmount)*2.; + float layer1 = S(.25, .75, rainAmount); + float layer2 = S(.0, .5, rainAmount); + + + vec2 c = Drops(uv, t, staticDrops, layer1, layer2); + #ifdef CHEAP_NORMALS + vec2 n = vec2(dFdx(c.x), dFdy(c.x));// cheap normals (3x cheaper, but 2 times shittier ;)) + #else + vec2 e = vec2(.001, 0.); + float cx = Drops(uv+e, t, staticDrops, layer1, layer2).x; + float cy = Drops(uv+e.yx, t, staticDrops, layer1, layer2).x; + vec2 n = vec2(cx-c.x, cy-c.x); // expensive normals + #endif + + + #ifdef HAS_HEART + n *= 1.-S(60., 85., T); + c.y *= 1.-S(80., 100., T)*.8; + #endif + + float focus = mix(maxBlur-c.y, minBlur, S(.1, .2, c.x)); + vec3 col = textureLod(iChannel0, UV+n, focus).rgb; + + + #ifdef USE_POST_PROCESSING + t = (T+3.)*.5; // make time sync with first lightnoing + float colFade = sin(t*.2)*.5+.5+story; + col *= mix(vec3(1.), vec3(.8, .9, 1.3), colFade); // subtle color shift + float fade = S(0., 10., T); // fade in at the start + float lightning = sin(t*sin(t*10.)); // lighting flicker + lightning *= pow(max(0., sin(t+sin(t))), 10.); // lightning flash + col *= 1.+lightning*fade*mix(1., .1, story*story); // composite lightning + col *= 1.-dot(UV-=.5, UV); // vignette + + #ifdef HAS_HEART + col = mix(pow(col, vec3(1.2)), col, heart); + fade *= S(102., 97., T); + #endif + + col *= fade; // composite start and end fade + #endif + + //col = vec3(heart); + fragColor = vec4(col, 1.); +}