From c0cb056e4d4091478d34f1be459143225dca4f0a Mon Sep 17 00:00:00 2001 From: Alexander Winter Date: Wed, 11 Oct 2023 20:46:36 -0400 Subject: [PATCH] Massively improved UI, added icon, updated version --- .gitignore | 3 +- build.gradle | 3 +- painter/build.gradle | 4 +- painter/res/gfx/icon/icon_128.png | Bin 0 -> 21230 bytes painter/res/gfx/icon/icon_16.png | Bin 0 -> 4903 bytes painter/res/gfx/icon/icon_32.png | Bin 0 -> 5857 bytes painter/res/gfx/icon/icon_64.png | Bin 0 -> 8862 bytes painter/res/icons/icon.ico | Bin 90022 -> 0 bytes painter/res/icons/mac.icns | Bin 49253 -> 0 bytes painter/res/packr_config/linux64.json | 14 - painter/res/packr_config/mac.json | 15 - painter/res/packr_config/win64.json | 14 - painter/res/shaders/background-biome.fs.glsl | 32 - painter/res/shaders/biome-brush.fs.glsl | 15 - painter/res/shaders/boomerang.fs.glsl | 30 - painter/res/shaders/boomerang.vs.glsl | 28 - painter/res/shaders/dark-frame.fs.glsl | 15 - painter/res/shaders/editor-cant-place.fs.glsl | 12 - painter/res/shaders/electrocution.fs.glsl | 18 - painter/res/shaders/entity-damage.fs.glsl | 20 - painter/res/shaders/grayscale.fs.glsl | 20 - painter/res/shaders/ice.fs.glsl | 40 - painter/res/shaders/ice.vs.glsl | 23 - painter/res/shaders/land.fs.glsl | 87 --- painter/res/shaders/land.vs.glsl | 77 -- painter/res/shaders/roast.fs.glsl | 24 - painter/res/shaders/waterfall.fs.glsl | 25 - painter/res/shaders/wrap.fs.glsl | 30 - .../com/normalpainter/app/ColorComponent.java | 6 +- .../app/NormalPainterConfig.java | 30 +- .../app/NormalPainterLauncher.java | 5 + .../app/NormalPainterScreen.java | 67 +- .../normalpainter/app/NormalPainterStage.java | 709 +++++++++--------- .../com/normalpainter/app/PaintComponent.java | 204 +++-- .../app/dialog/AWTDesktopFileSelector.java | 35 + ...tor.java => SwingDesktopFileSelector.java} | 2 +- .../app/lighttester/LightTesterScreen.java | 2 +- .../normalpainter/util/gdx/Scene2dUtil.java | 3 + 38 files changed, 548 insertions(+), 1064 deletions(-) create mode 100644 painter/res/gfx/icon/icon_128.png create mode 100644 painter/res/gfx/icon/icon_16.png create mode 100644 painter/res/gfx/icon/icon_32.png create mode 100644 painter/res/gfx/icon/icon_64.png delete mode 100644 painter/res/icons/icon.ico delete mode 100644 painter/res/icons/mac.icns delete mode 100644 painter/res/packr_config/linux64.json delete mode 100644 painter/res/packr_config/mac.json delete mode 100644 painter/res/packr_config/win64.json delete mode 100644 painter/res/shaders/background-biome.fs.glsl delete mode 100644 painter/res/shaders/biome-brush.fs.glsl delete mode 100644 painter/res/shaders/boomerang.fs.glsl delete mode 100644 painter/res/shaders/boomerang.vs.glsl delete mode 100644 painter/res/shaders/dark-frame.fs.glsl delete mode 100644 painter/res/shaders/editor-cant-place.fs.glsl delete mode 100644 painter/res/shaders/electrocution.fs.glsl delete mode 100644 painter/res/shaders/entity-damage.fs.glsl delete mode 100644 painter/res/shaders/grayscale.fs.glsl delete mode 100644 painter/res/shaders/ice.fs.glsl delete mode 100644 painter/res/shaders/ice.vs.glsl delete mode 100644 painter/res/shaders/land.fs.glsl delete mode 100644 painter/res/shaders/land.vs.glsl delete mode 100644 painter/res/shaders/roast.fs.glsl delete mode 100644 painter/res/shaders/waterfall.fs.glsl delete mode 100644 painter/res/shaders/wrap.fs.glsl create mode 100644 painter/src/main/java/com/normalpainter/app/dialog/AWTDesktopFileSelector.java rename painter/src/main/java/com/normalpainter/app/dialog/{DesktopFileSelector.java => SwingDesktopFileSelector.java} (97%) diff --git a/.gitignore b/.gitignore index 4836139..28df1a8 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,5 @@ Thumbs.db ## NormalPainter config.data -jars/ \ No newline at end of file +jars/ +libs/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 45b4c08..7a09ee8 100644 --- a/build.gradle +++ b/build.gradle @@ -3,13 +3,14 @@ allprojects { ext { appName = "NormalPainter" - gdxVersion = '1.9.10' + gdxVersion = '1.9.13' junitVersion = '4.12' humbleVersion = '0.2.1' mysqlVersion = '5.1.46' paypalVersion = '1.0.2' gsonVersion = '2.8.6' javaImageScalingVersion = '0.8.6' + visuiVersion = '1.5.2' } repositories { diff --git a/painter/build.gradle b/painter/build.gradle index e3c7135..04bcfeb 100644 --- a/painter/build.gradle +++ b/painter/build.gradle @@ -24,12 +24,10 @@ dependencies { compile "com.badlogicgames.gdx:gdx-tools:$gdxVersion" compile "com.badlogicgames.gdx:gdx-controllers-platform:$gdxVersion:natives-desktop" compile "com.mortennobel:java-image-scaling:$javaImageScalingVersion" + compile "com.kotcrab.vis:vis-ui:$visuiVersion" compile name: 'jpen-2' } -new File("../jars/").mkdirs() -libsDirName = '../jars/' - apply from: 'lighttester.gradle' apply from: 'normalpainter.gradle' \ No newline at end of file diff --git a/painter/res/gfx/icon/icon_128.png b/painter/res/gfx/icon/icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..e36b8dde770ba44f6db4e86ca4ecfee1316cb730 GIT binary patch literal 21230 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 literal 0 HcmV?d00001 diff --git a/painter/res/gfx/icon/icon_16.png b/painter/res/gfx/icon/icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..729ae7ff17d65a05a1cf8b1a537a99bd43bdc615 GIT binary patch literal 4903 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 Tb_IZ+5d@v)>0CT-dE9>hd}z^5 literal 0 HcmV?d00001 diff --git a/painter/res/gfx/icon/icon_64.png b/painter/res/gfx/icon/icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..708e4fca341a32a474862a7b290a3610e2773977 GIT binary patch literal 8862 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 g_E)=EYu7QPoVNJFtvp4ac5X6fqLX>7vYwc}uL zi0w&wRUUYe-*_{fbmFy+z|L`N-2-w11Lc*Jm0ZHUs^u)}2bT(ty*H7fx%Nk};~u94 z`^P1C_$l3@W`|T6sq|jB#Uc@=t5{poKC$beH?w_m?`3_B@Qk6He9@yk($0s^l=i#R z&J6h-Mhsc^9Vhb8WII`rVKQBCc%tk>iFHWg9FleL+s`7yGty(5j*i%rBA)9e` zzAmmc17^79i;cCRc=Xfv6E-DNlIt$qb~NOKC&>De9}AaCG(IY9`ER-g&T6q69?y-MV8m7%OQ&kAnT`d`|dH&$_H*XW; z-??;Yo|id<;KR`5La!ccytZ#G`;?s8)!o^u@pK()q&xA{Ay zm%oW&w9D;hJVtVv5|gf1)Gr+$!HG9;XwiXj(BTrP>^{g@1eT7>O zOT$CRy&BGw{Sm$y1L(=r*w!eOw<`Up8>%`YeYq>UZxzay&Qq2@83i*;I?eT_#bM&+ z5;_A{qqa$~II;AwfFX5MKR8g8#WY#eRf7`u z4m+9*mJl?x%q=~x9JXUmyLob}B0X>v;y1p2P4Z4hpb ziz%d-?pStW^!l>h1Xt^rDU8*}=4m ze8;0J#-9BIElGC>BYaUXepD_dD2|;eR@*n z2=1GYOJhL^!W+GO;a;qq98u>v+nc8}FGacg7>Bniwb?_Iv7x|%OJ(AC)lARSha#-8 zwMiEzrav_e+gxmp>5KUm{i6TXvYbmu-;+-vtH($0>Q50ISMu+U4{B;e(goT_!|;l= zV)5%d0cgdCoZR9TQ^+CPtUkb}vk#C5a4_iUc!$pq)Z0h9kH31tU`(f9oQm)B`v5=t zj^%XZ{VO8-{f^r^@ai-MDNg3x3$!)JX&m6e7DO_#G&!oF<<-O%-rDQBLJvv?fj@d(@WksxpT->esPHxxJ#a_q~$1OMGI+09_g_e zLyVCX-O3f*Q>u+fDpUH(*^hVYy*41K(&ugSpVr)eH|*6hwsZaC`u4}}olf1^g?!OF zzA|eG$RHt#6F!P!-3WMsZuw%_Y;J|;)Bp#piWL}IFkB?>W`eOTN}Zt0-Bh>iunSmT zp)>Gu2PaCUmH7@;%)i3)xjlKfXDKJWDZE+qanz?L8Sy)Z$5T6~TmZgs$^FFIYww5c zl}0xg>y?7W>$@XcLo9DIF1w^^+=%|&}y8=-jWA{4l<+q*l>q2iHsLn z4?ML{!#Ac}ouWq-pBj%ZR{O{5Iz>s_>h(WwTHje8^ZN5t>MdKQoWwOLiy&%={j<&uMyHha+r#yuhlJ8G^dQy}9qKp^utA%j<#W$;8=jfD|=2ZdHw^Pj9 zS5B5T44Lpa7MT-E?lHZyip;6J)4tXuwCEfsWuqc|qy3FszMizH^J4DVa_>mlnVE8} z2V+?)e(%PEYrG$S;4VGC?o6pdG9n4D_R1zCC}j#_PRC9_GFvCtkHJx%SyoN=c1D}S zEAFH7Rxt`k`0Q4vJ?B5xKU-g^z!WQ=PTjB0dq?>qMsbIz+~7fj^G|5y-#CixnT!^% ziOne{M`(XxO>(B^G4b{E*B>lbXM%Cz;Zx+%o$o{-o%yRj& z(ti1niDLwAIZ1;`ztfOZPt#}2&b$UU4md*{XC4ZFUYTW=Ucr#u6k7IFjJ`apZ5x@h zG`yU@5jeTM$V@ZSI{ll_sNu!Sv+sD|aV~_tIe85Z*AYtllO#D!uSuG`C>DL7A$xqS z`pQPS%_%KrLG}wm_ZL=Wj!x#Q&-*tgc7Es6yh*?$ezF07ya5141}6GNhwX#@2a|b^ Af&c&j literal 0 HcmV?d00001 diff --git a/painter/res/icons/icon.ico b/painter/res/icons/icon.ico deleted file mode 100644 index 960dd6a5caa77a0ff58d21d41d8e36933a93c13f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90022 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 diff --git a/painter/res/icons/mac.icns b/painter/res/icons/mac.icns deleted file mode 100644 index 0bb3b4a2b8e9179f2a5ff7f0e92f4a30b103df0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49253 zcmZsD2Urx@)@>)iIQh<<>^Q+P>*%PX4k#c{WDHh@R=px)A9pE&Sy8{tHFMy15)#RcamMpU7bQqc^1Jm-@;63YBbw}$jMHY$D5sC z%D3FDkn{a6$V)}VCrU3yuyVeSof2}+38Q(l{Z086dx=~SpzvaN#-^rbbL<_gBJr<( zF>;|IHYJpii(*ckOg_h!ic(LPmgY(1B4*A!Hw(Ebf3}T;9_UPxkDWSm?4aD0rTI1% zex@dyU*Vr!!a_K?sc5!T9#;b69^{ezL~i0265@|9&kr&cnYk=+^I_zC z{}NKl%azLo%3wvXoVP&&JFxTsyO_#Lmo8UTRg)`%>U39z7Ff@jB{vn?$>pcn(vMG( z?Q(ha(TkAhc&uDD9|p15E0^=m0xUT~ZY*Pb_j&K~B(nr&rhpsjQl!z8mseC)0#oD3 z(0r>|{-y$Fxu3LD809U`mX!)~<*Y@3DQ~etE>s;knHeY_Al<)n)4G*Q<~s$`LtRf@ zEGa21EdxwNi3_3m*0Ut0gJ-YwkxISSIylMY7Z4x`Bn{qAlY0WkEnIFa-M?d_$0Fw_ zdZ5$s^XD&IC@#LpX}f}^UG6UyK9B9I2_$Q?9(AEEVKR68%dBF>*(RsmyGM z%@&D5CjM*1BKf{`^OEVIl9MNkicX#49zS=^1y5Q?O$B0kq<^VVVP1?}mW^i*DpoRP zl3CK(j5*JeG3U>5h^0mIS9`8jvYIG{KN?k<@7QXIp9Kdl9V=H9iZ60 zk(5X^Msc}C=Z{86{6zr)afeG~8r}t_oUb`Z0(lH;N9_4_wo3Y=Eirk8K+Vm~$eW6E;g zMH0mGV;uyJj`sfa=XSdj3JtIZ)QpVu^t3cdsx-xD6-~RPrjoS5>FFGJHlSmiB(%_W zgEEdOA9A$#d}?5Hd3mY2tTcxxnLyrYX}PRI7!w@AGEM?#XL~>TljV^G>cRrpffG0b z$Rw3X@k1oUN!RO%4qSO^{X&NXm9LNG=J}RDN@1^q21a3UCaZ_#DB(gGoswkt8tj{E$Fdyl@L`7f%xSiHRiX zqk{*NlR0%bd*vh|`3Y?|hla^QjY2|-Dk{oJs4Q%tN)?`S`rPqERVKM4$P56>=YfaS zh530PG1#b|gCz135=cA~CyXK9q4HQ!&{joU9EtxtAt5m_DG5vloj9`v*<+P@%EOF8 zLseM?i6l&>l7}0`o-048oGYcja!NbIIk+Dk8bE=>#Ds+ScoIiqjbdUVe7wEo(QFKf z{a0LEeEbLeennuhOm}46Rbf(tOW4Qz+N6A374q&;o-6fc_fMAN4iVt@7xL? zb53?^5w1KFr1`W3U=@;aH%Gfh0UsF7f6ml_5;% zx2(5!uqr&fpM*1BR9HVpYT9~23mjIgVBE=a;j(4;u#&7|JlNF&&()3+df3vud@h*! z73sIu2Zr!)mCD}-iufpl1A<5}KZp?TuR_DZ5c3fcoHjAB(cTB*`*o3VzF!6K6+(CS zRjY^xvzqZ_*YMZ6O6VV4Ig)>@#i`M+1uO;Q1Rjuh^I0S`Iezh#Eb;!DgoK20MEx2? z$9Q}DL>u%81B>fw#)GU9u3Wi#HSuKDFl(80{Pi9Z`g`Xb153Z-ITEl$aqdusg$4S8 z#K)f~B>{{-_9CU2@%~s56dY_|;xvLnjJHu7M-v0DxU5-2JjrUIhX+1@f?3aO;BR!6 z(BGzia0wS){i^&!5+s&-3t7K_0OGIo>{QZd^qwtgS_OsrfNc;n^*8~Oy zahkwlVXU_(0u*s5B-d{s>&ZId+O-=t5P*}-%ohG;2bTWY=O2du(x9I}<}Hx>`4I(? z2grCnh%Y(u8^Td=dTmj$6f&vzr!u+RN8tyh{^8gQ1|KfAIEF1`GudRkapQ>-@mseN zFJ>Fr&iAro=`YiA4L16~!1?(Dfruy+a=BE--|H=x@nEq}_e*`eeWY@Q;vW(@%!ud> z2f=19vXyM%Z{0;s@^=87*+F*lR zf5Z+s;D>eFk9a`JePj@qe%?Z#H8j!@kCn@W0W`e_5`+Xhvh?63-rjrV>3LVKRb+Y- zAKrG<2b&KZ*v8NwFG9fdBgbJyg|dO(0+#s5y+thaCjo$5M|*s*hQgJm5jukX!K+bs z+wO#L2e1j{q^IvA`+4gC>bO_D8Z~AZT!=(~JP;Ed;Tz*EA~G4XPl*WLNYi5MYI~(p znUQ=QO*_K6eE)5zlRd}*!T$ZBYzFTD@#U?-am{7_5{4d}2F(#~Sn^S9G{hmSZ*V`5 z55zhEx5+o5(BjM>@keRp>`TPNcc3!g=P9XJLgJY8`{KVhqg9lc#gdv28jeUI?hGjSU(}Q+!Dj6K$m+x)FIMKArekl@{ zRLV#epg0lZS>i6E`7SJx5Ik>8h(sclZt5rq8c}%&uATTcgDCUy#z6!5kT2fk zNAtE28Icl+G0V!Z_;=u`ZJfB!JK8S?+l{Xy9$P2JC*p!=!AjdF_FW>Qc~1EIxm<1{ zGGnO}>)Fo-`$5Jk9H5;(NF22{%UdA!v4feBj0NKTj?mF z90@0YMHqA;v?%0IVJaju4(ALW9Tn~^JRoKWD>mA@b@hsc^RsDzyF8as^K~3^EZg&C2GOx2N0LHi3F1% zVPIH6FbUy@u0;r<*33*@fdGr+;yMVv*_=ZQmhR6a>H%5Fana!s@hLenwdh3C%XigK z!8J6Lgct<}A1TL&Fy0S3OOgpp8TaRrV7;rt*NV~87<(~=MFqF2R4M*@gACD4Ke;@m*> zH;2z%)|@=7R!47grEP3IlxJ(Yb-KHcp1t}TXGy`f@Nl9s3M)oy;5>=sL9xO65XJ|S z;-dnwS_7~E{Ar$}!j(T4b{y)kz}+XVP<_0#Qgi0`A)+=uboA84s%9;#Gp^fC+gMs! zb5UMIbVhd`Jbn4D9&xfbl0*y+56`T;e7PzuDmn~48?@ID>9AX*ufI2Mzl=7r-zxWu zFYs3j^0RSd=AWpS>I6MqJ$||`tC!Q3=9bo*GBbQ^>1dtq_WdU>-oCO&6Ur{aP+UgD zk}9J#)UK>8Ll4-MjCBBWDgnGtN)NU7&QPm!L%g?a+9Rmgon!t*65|qdb43ClFbI%9}(9tE*4Ok~n_64@;ZuFhp6TuOGG?9BJ33sMT4! zoTT)i#bFoADlT8Cz24jrr5m8#Loc&1v!q*C{h+?B^a)+>!)LGGABWHOkXV!C5=}-* za(ptIz*~xsGnhiRt`GiJ@9`y~2- z58RxZVc1uHUkBnE5lkE7cDuu#w)r4{tq2Z^0Esa`lxfn#AMes_??W zB$UHOPSMek5x#P7-d2|WXjP8-@TDR^E|_I$z5gZA8+GiX>G|bxx`Bu3RdRq@(?_NH zfsbr#Zg8H`FTugx4#%2P)s@)?Ph737y^zAdf^(ywdpNc*9R2B!*5#{Dwbw78o!9

G}rfrtqh}8}9rOR9lqON`eS(jQw-h2G~E$1d2e{oDTNzSgWtKi>*fySiZcx{rKnwp-Qp1uPnv5$lA5vmakVE1E3^DIyw?a~#|bf)pJav+B0@4=X2-Rz&>Mby{I!vH?V+CY#smSAEp*qV;}9TdZTYy zWoc$r@Tl*#zLcSdsJMzqpNeViURigIxL7VbYVAU{*a2be06%YI#Hu60g6M`Wr&G%&PJs_?XnNNx ziQcHsdbSN+5vlt&ls?|Eev-@d8D{2ltZi(p=hW%-`Sex}y&0)2EQjm%WYrYrI-U|FAI^oLAX^TDPPMh(knb|Be)0uxv$)`7SiKvT{KcDNa z<#G+WdD!Yw4Y@50nN19Qg<9RF(`C@k*UHB&v7HX_Wwi0h_xD%j(hDMWLypn1l9q_6 zt32$d{xO}*7(9L2l({YRb}r1@AbNOVK>^7ZqLY}9R0PoxENp-`oEfB6V~2>KH`R1b zm@;+R%vlyz*w)t)y{Nc~rW+%4LoU;cPgJ+L&Rpr?Jar10I(W+D$wlR~f?Jo!_<|U< zniPr((4ou*G*_m@#ri<#4TSz8T&>n};2SS@x{RMU>yJNYSNO)4OI!r?>MdW46M9LYe*Y={^YHmZ4m zk5;RDbh;SYQ_~VUVbuz=>3^Edwjh=REoKMx*6sP5rq4&}z9Dp2VafG|h2!Qda-Tka z{DjD^Ys=_jBtZQcNS}J-2stb|boA)4beP=E056C1C_}!$^6b><66nR}>ubl)_gFr2 z`b<+ZGHam0Z&~kY`ZVAV(R0%dm(;bTIE@)Sdh+JdwwlxQyk6|L`h2czI!2E2L2;bN z!OsJHe4Lous{o&>R=4YP`80j1s)PM)g~yWV)27cLGY8Iq@8G{Nu;TG11Wz$B(Bnw2_@5;9(o2t>HOpb+b-aM$?B& zuD4qKvBG27pHrqy`(xU)DZfuTs_&!evMAlx>9k!!Lhgy;$}3l|R#%i1CDFE5xRB8= zNATJm;rigmj~_dBG>xT29C$3ieF2U*s@Lh-Z0XdZszp zN_ux;@wM(6>#_6-k5#iLPM9!$-#h)=WAxk@o#?V7y8r9I#$-uqZN-IJ7e~Qv182(^!hjV*U{Tzb^KgZ6Q9_- z?^ML8%bK|2fHUz~u0BHVy#U>9bzvdmPm}=M5Y*V*IS7~NN^XvdVCqVl@-JC9$!eXoE2=J`Y2O$BXRm#s4~lw5kQG%~Tv%**4N z@FY)=n*&8eKEMlOyoJ6Y!31Z;)j*6@t4}k!e;=ah0|_}NO0G1vYkTkCzjvpnwE@!a zOwbMNtSvsA9LDbO2q8LS&H#Vw8?lY8%Hdk`IXPV4KO1foxd9wK7Lg((I1p_F)H<{< z6qN_iwK#%>$7LQltEsMQY;I|;EzVUgL#T!82DDZlO$lSStwOtcsJ#=ty}z`BoeqLy zUl#HZM z*Ph8wQ0!Q4E2lqOurubEra?w8)p6z6t9SZWNT$4SCUZbmR!%P0uwM&{$6+Dj03Zqt z4Gs(d0|SP&xMz{*j1SVZ-TrkB3R*aCgXAjw)}|?lm2O*V=SLektW)MzJ|y~&p5JMW zqHR&0V!6E6mm?HhQ`0j@rU}&Gt{ZS976oE+^CjL!Vc<{_oG!61iyRA(6D2yHFVeTY z`|gme3r*m>S)3WRXSuCEZS1g4mR{2RPNx6p)xGv?n$|)Igt&e;EWelQlama7W)?~3 zr{Rz<@&bjNcpIxigM*a*d%-cc-`DLD-N5es^xXCPqYi62P_V;9$%-9r0bpGzPCVQ4 zTB#rU`ax$N7@z}!y&ILfT{K$cN6+Tr_JXAGQ{`Z=M?MevUksbUpj4|Hr8<63lT>Ha zcshZswg>QhFJ)Fq*V{1t&^PzXB((hp$>rQmkM?_NRn@svCQWcOJrXg1Zh8#5-C?02 zLCOGM2>usf#cFk(LTA)nRT%cyLe3bTOv1_fN0IuEU(_Fy%!6aRA3Wg<-pOq8TNxG-6c`|9vB3#_)aq(hXViKjRleR)L7O94c&r}BeIv2h7U%2Lq=HFTQ~Ky-VqSb;^nwgm+${b3N4(p=D20Uk{>!8SQd z!Uja8sn1?&?0)nvMF0Kk_KM@#Dew=Ppi;3kL?v4vodt(%ZC*&}<$Q`EN2Sw1%5USMbYhXG8O~pwu=nF@r6r8T8@4WxoU;oovUG0UV z$W{GKe^d{s206wxbnN}deOP1qWOT#)aLW=M9h;bZ5QGJW4mz4RA_lca7)k?pgrIMP zrGo6%%m`PIJ$+FtE-scki$S18;=Zj7hohmwc;`q@cxUMqvQ8_(9 z8nTg&c=YTg4B2LfJ=8raAt5$0io4xOh>PoQM6=*r1ZQEH8_NO%{Puc#L02d2Sv^>H z=$3rBAU8in85S0wb*#9$x#LECW$~Fa#igaBIV zyB=ccFCvc|OOeR}!jrO&oWs>RF6b{^zDzFtR9RV3t|=?Ic;VdH(;z;qF5udt$%*kX zyJx)jGJzrc>R*pk|^_7~dSEaSXuhv|tuDV=Vp(zFFnNu8Rer{HJN>co4+823W zUjV|zN<+Up8dr5g5@BRjByM>^f`b;|^cthUNzo7Mz-I$WVcY1NePFyrbi7QK{y2eS z16fV&wYrAu*Bd2GKQ&&z-cWa~wx+u3Qbk!w@wqcaCypK}%*)P5-A&VeAgqHozqV%r zfvEw zzIv^`q47p@%gxrdHm3cjwzk%rEzM1h4RzqXRG}%kaPIU;PSYHJTms+I>pQTUeQUeJ zPZ?WKpbCynD6XtL77-p18jiybc>EVAX<;COR!R?c?F8M!ySH^Z4T5&cU)8mB4Nc8A zTiZLeckVxW`mFES)5j0)>N-2xZZnT?R zpD!=Z4vswp{~yQg3k({p1exD1u_^xS*; zIza#3>!+hfk@clxGR2Bw?gpinv6&01ZSPlz93k~>w z3c0(I<|_{<=s}CxU%z+;fp2SZ95?c7?d*N}HdO!Bt4F$y*5;;$Yc*Asnv3U8`{Ga* zsn_?eVCiAbzN|#5N-aO842j0&@+lR<5x4#VXk|< zJv-qzpSzLzp)c=uwY3%66i8rvz;|KS8_5yb0^+^5TcaM77IoKtAE{7Ak?7CDad{IABgAORk4h!1GHfgQXo6*bZIaQVd1CRt`7(N7#7jv1 z^wERc3alwx<--X5fOikO+L{~d^E|MW?bQ1B$I!X?$Zm(cu&gYagyMouE{h;hUn_B| z66Eiv2v0teEMp}qm2#US?cysI;}+9sE7vu4gI9Olef#=F-;+lV@{xoE^OGNk>y2La zb~I< z^LC=8I$tbiLsTKlfbVkh?b}x``kp?n-GG4xXJzgCP`&W)szAh@Ncu#7b&-S~<>0Lf zP^eT1;S3WJ=EtI;ER*BHBOm~`A@U%lgdtLuDo`x;-NlHx8yX>`R}vLJ#6ylg-krbS zzJB@q*`qWU%wjB7p6&YEPcM4WbWFZjLW@>c=^unj>4EdTgH*u`@?!`i{)+Q0I3f!( zlmT+MlSLa`O#GCrOw2!ko(Q_Q;$TK42wrc??V#z;zyKYzVUBWoi1WJr!BL5*WTmL= z&^26n?Cr~ZP!C$fiqYK`v&sO56@N<5QkMjkl$U4ve_)V_g|f7qAR-p+Km@zMRDq02 zC=>4l>fm_=FW$f7Si0jEMbXB#d(OXmacVgl?e4xo;?D^rPSj}13;aF^8xvdeoH-dJ6^mr5wBi&Z{+f|Nep!54giJ1x zf-f0Ux)6IyhwnD+zTzIxgB{Kc!+f8*HyviaB^ zTLnGAX`}dTadEa-g26GdI7D^2?2J^5=nDmf)K4Z;FexV@#U$)#aa4dfKy?fZ`Ctgl zD2xb`i}_pUDQS~MzRAU{_nyC(=uQ59++KAub~8#`k?rdJ;y_aNl{)2gNm0019Hu&3 zb~cEKD>|Dj!({*~A=07oB_;kcY_X~!aj@z*up{MSBlbjDS*prU%-`Y?MjI_!yLQnc zw-9=??ee{1u^a-HsZW=bm1T;>L8|=HlT!bpva$;b1EN%(i_UGVf>8|(6o;zP%gT;p zStjO$Gh(BF<7H)WLH=-)*Zd4x;JDg{5gUi7qVv=8ib>hGsbaAtC@6px2bY$WmBJKU zOy}|fk}|^m0Vjuis<;$|Se$hFG$O<(IKD)!LUam=Uw{Ni(r}e>ue*IR?gTdavSJ=# zRlGd-yf{QA7D1?z9PYaeDya?d=amy4-Jp}R0BKm?k3$tS3PER+7FhueqPp|!Au@wI*XT*bIjuvGE zh(nH^%L$P8KMPp|iZB(+6UakZrC5-CDoz3pIU-mHWr9Hwl2dk0E*F2kZ|CN<9xIkD zc5_>_c=0m#)f=|%@R9bfBtIe^M6xfOP2oJJQu-MX7%^WSDCP0DkU%ky4HOHdsEV-y zm8$rlRIx~ctrHPG$S>hgp`42$RY(YoixE|lj_0K$4T?)~wZsc1Dr2|}ZVCtz^L#Pu z>Pkqhm=}bv=0d+DOsen;iiw6_dIDd);D_3VQvBb1 z{1f;|=ZHp7QFfT&kpxk4dU|qvWCHIrs}a=HU1l_*n%1_u4z^oV*WTUTB+-aUlCyJ@ z%I#aFQy4SHNm` z&7_-mMXDKexwX5yvrbdj-dZKqh)Ok@i>cBC{`S$pu=Z6b_|h2+&{%nbFsW2?mF*VR z@-AE;8k6!%D9E<=cA^xTEU=8oMB!RzqBts1ty&c(s+p`!2 zf8Xy6C79;Mp`~dw?QFM6Tka_c+0sS2MK^0SW!bPs(mAk4s0&eJU2M+l98rCx~tq3I;6NeW0nYSRd-TG|={ zH3Ki^MM`$9cC(r~Du^076;m@l`}PA!OxWKD#hA4xKALqb~N>h}40b?8YTfL$+<-v12DD6F8i0429XL1mO`) zWtXyB&{$gQ4}MLCe}W`&NXiM$Cua`jYch6PBvV6IY}vZi%d7wKUBBT;FG+$h{-UPZ zzuV|WQ;kM*9S>jA9Fis~l9+?M6mn1yzIs|LCHmdc%5oY@k&Od4ZQ8tL%T{1`!O1%@ zMkq?rXev7Vx_MXO#wNdRVb9g_<0MIb&@WjaaTygs8BJS&v9dL+CvP2G;JkM2x^?R} zY}m-*AlPv0bdaBvNy_<`dKBHfdc;{{celJ}P-kWN>6C(#XU`l?m8Y<&`~aIU%4nqJ zQqQ%kiHB?zZh)vEo*t`Ld#+hyK-?su`14cPWKmW{Mg2`h55F5h&#Uq4F>bndB$uV|F^5Up|d<%YJ7n>9=&uSk|I%t+fbCXgCBWeFfTMEB*(mtp!$ zwuI--P-NMl<;&gOfehfaQp$LJdHI{7WFs8&F-%HOEp?;PfJfvBdKIA@!PdZ zov^m5s*Gd{b8@nl`%xd6ty}B48WMBBz$I={SH%LM8%0f!xDvO43;U5*EFLMLjLedn zIyxHI%lxVyiB{0qaD}*W&h?-*cH%4M1YDV{fQB2L z)n`j8j+)7-k6btO!!BD2S>4^@zI-j0;70DPl2wx`A#Mt^(rW1yTZZ6LX%6(wTK(<`M&viK{Co zm^%%zc2-Mk*egtp|5ZsXzh)uinYIqSH zmNAF5c$q1oj7No?t>n5LHD4aWMaIdZQ+S);LUBoH8P?Y2%N6I>LCW9PuZF6ekpE!w z=K`E@CQhum(vfxI;~LMIxDbdcoIfA*u0pp(%f|tJ*5M;Z(KI4uf|7G5*dy#w$uagg z&sRzfnz9a>a{_aE<0(i6^ATspiSKB`n)4h399Tz@y}5&v6Np^ENahXTz&Y7hz?v*M z%HmdvfAlDOOmbXuLU8inSPAvXj5RRCKeG12@?8wL7<#vp09a|yI0)>_&50v%;=~rt zo$CU#7#W})2*sN%ImR4CkMB5pLUNK6@sFBIsG+97?H6~!g848n_Y`0O;mCKeV9kXV z4)*r`+`zsAP0aZY4h-PoN5*BKx!t^e!6CLF>BI>Z*HPpIdy*}ZoZ_vQP@hj-!Qx^RTyLfX{dAm!Fs$Tg~I}mqU1` zQ>U0B<|F|&dxkm7KW!zU{?XKkJ%z%39aOCk8t z;$rp!zqo{S3Q8_sWJ{P*oU^EZE&qSuA-EmTtqtfR%o>?nO0D^}7BX|51@egv0|FzO zwzYMgBQ+N~IXka_=qH(r14?f6bTxK$HI}fYY#F~4S?`NU+-c^&gy+lxaDk1jjSQGp zc)N&}z%Fvw$r|8x5`YV7`*D^Ww3G89mKuBpukdKP@87%Eb)%Lk6P!PgjH11~JdUM4 z@?5rfVL!SdLWmR39^kgt95{{O;MuYOSD5py#(@ccE3&b%q%45#=;%BS$#hf+PJ)eh z$bH@|RwFuPNsY^ADtK!c>NE4@+?wo<6j&XC!vF;Kh9!_lY#Hu@!dzfEm6O|2VJ&c> zCIi^Q!O?<6?kg|XR5jlMa1SXLR8*k!Fj|3jb_I5DmC zm-v?z+`fIH+8ZUFN52cPWL#XFtYTnDOn+JgnhVV+%9b&gSsU4yQq)+Om2W#!Lh=4$ zFZ1fyn>Ik~m$;$jtTF0{0vt%DJrcoG;qD;U-W#XS!a@$D8$ zDB+Cq>T0Hnf31n!V5)g?DGo?~5NsVE^n=3-%tdpiQPd110Sg<}n(sIhhlAhX9lnJl zzW!GYbA>@+Ai8p;rbg*c4RZV+qtp53Mz-T9Y8=dMWNm9}V?P0T$QDo9{vo4;W|yyC zW$-kwnz_mg^`{1MoLtIqamhpOvEsgqpt7Xl3kR^%v;8Pu5L3%sB{ibj+H2S1@r;wf z!+69t2T@`-3J?7$=d3*r;bDtKMLSHEQM`$W!r8U#HBu|Mdi5Hqw4d?O4ZR$CvmB?Q_>&e9FWy>+(;JbGnS!@ z-TS%2%|%u;(iCH7WMe*K+Hd2tDZz~Sn;9>@WE4(=BTqLn*GU7fo-_)Yny`Nw&Gua7 zzVdhM{tGyJIok!p)wcBXEJ`@abow|sC7fvHv`XS7h;7>qdNFAtjl%0qUGOr$c?W`R z?aF0~mmsjlan^PYf=6cOP=YBA8;I9{jcZo9FI~23?IxL*C`41=b_wcQH8+zRMomrH zd-%|T*8!=a>)es$-E1k!nG-;XmlsVFDWh?A_U1GGAX7v$X4$!|3-ID^-pqOp*pqbf zTzPFv&vXb=Oj=0ufZqF^oi|%suZ|`te%zP9^RDx#DUih?0K(ufDVXTAIne8yeM$K_ z!F#;C+-FUsMo@pQPd$RUic_Umn!2JPmb|r<+#J|)_rb%5ciY)mY$s%BiTv3jCPRllzcUlJs>A1-H{sc%daC4 z`;#!?bm&C!<%afZ1c^&KX%n`#c0VAGL_HmybyFE?5DFwNXdvwze{PjiMx)H<&tJ9G z-%GH0(-zi?ugqgpP14dbvNQ1NAa@tf%@jmTr^v6LjF=l0;W3KZq&{@?T*bB49+Ysx zF)bY(q@CZ^K^_}vySlmy5O4$Lx_$^QP}oVSA*1a!czJDJY&UzBx#J2yFVRjWDle6! zFsZm8Pd8k~XOgUe*~x!U;p4~mx=9aT zi_~u7{6P&rSPBQgKi7JBZL}GK+HtB^az-}ymS%3IB$-Jesr4<{)p6&#ZCHhl^AR9UR#?h5TmxqG0^((|Yyw4% z_;thx?3k93(84d7fJ4Z=u1GS5;C;v}UuvxDaBw`u{OR3k<1h%?@! zCr=(}Z#{nYU0efhj3WGJB8# z6CV@IX`f)WfP3>Y!;}gdHvE?nR3|1flSV8%e&JGmdlz(>gK^>~PoF-zb)P&FKE87s zhXA8-T!A!4jy)Rt;`EJPdkSR0p7HaqBRpSVCbeZXj*|W)S&sJ>`6>U=XmDnxFJ-aM zK$C$f)%(=QtaFuhty<*bfn#sqzV+bglRI}Fl0L!XyQ!f0Z8drH0#$*XUnhM z)NY4HUE~hAZPI(M?|I*YRMaLRq4^QL5N-wo$%K&X$Fl)Tp0 zTY*nfR@2;5jP{P^!GrtvpFV&2^7*63anNsi|BmbIWXbUn>Qgf>uY-BH{uFh>#z06F zLc@o!)H=L=DoEhoH~orJ@xLh7QYpIcM*jrySgiHmR&-TO|8+}Q&5fQKtlW!_$U~DS zFJ8TR_T-5z^z-u3INZc%Oa z3c`5kACyw_J;w&Mmc7Ju>{AMhNb!$;qyHbi>|5^NkN9!;&sdWqu=uO=`c%q}qXdV^ zg{M!SJbC>173t$e;*Xvm5-jqP4WT}n>E)%)%Zs6?3#Y#0M21`V3%aoq!hikQidy*} zCyw|pi+WHgohd(z9r@$%pTPO+FTdDVp7)|?&WTM7CA`r0>=}6~>U;kD1-59D{=A=>gURy$#Oc5OCvdh<0XI6R zQDdR!Pd|~L2mdtu$6=`y%>`;RHm9@CpZ9_G#fz6Ou@xFG>yH~Gcm> z{6>%Mu(4xD{J;(y^uw^@MbxSa4?|ulm)AA=3b*Z6UdlJeu zzkN&Ih+e;Y_jemyHpUQ`^8(Bn>T?g+F+Xn$HT_Ck{SVaG(LciJ?9T%@pC`RT4wYi# zG;B47{rP+BHT$Vahf8Z)wMiD=e)G*Q9%Z_g6V$}>Ro6SUnONnWf0K87u)XKCf@zY0 z$pL1xz(Hx9m)EiUJRgcGZMwDZyD=OY8IJH84!=)&_KKqRv&roL+NQtF%9u(WIDYX; zQ^&2Fmn(I;_Uo5jspVYhq&)z=?!SLe{uW_k>U|5?MjF^~8shTv7n_hT^YcO|>R4?@ z*B?KO89Qe9uwmrKfy0LV@O{4i4n-+s|0z6v;I`}R9n`3#Bj+k=8g8_-+-SITN=1!4 z$c4Su5(Vt&H}8p_ychiacN$E-3Q2*M(*)KnDCbhh{ru9zE^lyJ2Gj{Ax%y-7y-|FARQsWgE2>ejr@UyZ=wkTpO zHD!i^8Zgc*g)0TMFp_BV+qduDk@v#4Z{BnR-yM1&5_oeM>PJ}Y5b)PfqmJR)>F%xF zKYTlE_;=raJ1I-AfBS^Su@VH#KV_N`H^;gc?axm}Pq$yTRUDS~p{UiKMwSSD{pQWP zzu&)q{rXieXt>R69!}Y4MnFdtfq5kV>oA1!nacY1-lrW2YZor@J&FO=SC3$@t$qd? zl4(!_g>CNM4L-`mTv9N^@bf#4a)r27yA$dZy?XWb9r;`M>gCIa;91S_FgU#0+s?+> z8-?kQYrMSD@RK|8{-nkio~x{H)jfFj`W?o=-oC{2&t__5q%4h1XHXLw$ViS5KYs(# zVVbzG5N{P3s^~VY7Eq$G_g}nv%e)i4c=5as<*kTYw)4S;Q_mc*{fV!v^zw?s%K`uP z#XgBps=d*9>+ZwHPcYD>ZNGpz#2-`)%1?fFHp%%CR9Ue>WarvN_GW7^8-2Qri)>Vg z!llojzh>U@-#o`=G80r&LFED}CwqIYJ84U(Vc0N3F-Dl5i(b0l;WMRGbvIf&ySr~* zIug0#Pm1zaq_YO90V$#4?Vby5{~U+@K>m>dlBUcbm@>L@?TXpH@yxC{3%GyL+QHiQ~^ z=ooC=+{2~RfpZ=|e)8(|tG;J_wD$@>*~*O#zkYNplcLn8N~*AYwAf<@svbUi z^tA6OifZzb{~YHJkt4U~5_Bm!78=@2gf=UXR}I<>%$`q8G@rlTHzbW@V`@4hO6s-P zc2WRkJbKRhz(WnMFgpD9$?a^4;=-l5o7-bQ9@FyR!9%b-e)8ny%NNg&fo04Bobb_Q z?PoFkupL5~xN!U@O#sMo!2$SOsvs35Mb$4tUy6=FCHH z49LBFAA`hwm2vAP!&~z&R9*Sw&JWbYhtJi}&q^q>E=vpFWl{-;;G#bEOf1 zgzEoO=f`(@dmlc3{`CHX2X`Mly?tL9_jZQPLoB?p7!49ee{gn zGrr$@=g#fh8Bk!PD_m@4?&{()4=#oRllqN=cSA`bZyip6V<)(h+yR9}B{<>rKH~<8 zUo{l^c}$-$apG^2CQtsI;~G!xJb9tAwz*5&FtHd@Zr|24jlZ9 zmV?S=E)nT7*mC2%;7g7D}M+xCv9Ub93`?ysvM&{p1ZdQrvbrdYk>&(WA$V z89Q#=_z4q#11UHsP+sTCYdGU2vDD`m5VW^%-F^%Wd+*-Ah4ZcUFGLpSd9;0vo$YMr zn48Z=vf(yrye1?l%tV$Gjv6<1Kgl&YcAhJh9->M%eo#}O++a15{Q3i`AZp~OQ3hVn za=QLLp4wbi-2ihoqLcN7`|Zb1?rL>Uo<4i_;0}f`aq{gx4c0&4G;6Rff|lmy=*RKK z6EDH3!{Fr?;>h^<=uy*>a&wQI(bU}N>Z!Ys;kVgoJUg8HH0W&`n9gSwL@ zkEhmE)!*#V_RK{g{99EgS`gj$nWuvu-oDLIc6W8zz)f!R=1UBkf^#y~&$Mk`@(V@W zJ5fBexQC9UM$DA5cnf-g`NSW8WQP&diNjD5p%CUsK{*n-_EV0dR$qfFwPJMM!`nML zJG-=4u1}vn(B10k?&<03>_|2cE(PH@ds|y;bF^Is@AAqnzfg4ekXl_Z6}{A7fBE@m z)LK8HDEWbWFaM5w|2uNoRm;q;GXi#f2h_eXIB4MD~k;i7Q31sWaa&Uo+nf`}W)K zz5}O$vY*kQ;23OQh?*LB@ayplfLIqdwaB9cOE}_)YaCS0zxY*5ITU6 zMzeD8#MNa^oNf45w+I$-=R+Y`s=U0lK-20_5Ighe*;PoqGmU!$QcxD zQ?4LhuvL6JGNHNk!IOLKosS;jtk|dP?&;J%e00AZ!K`R{*!#W8Y(ANUY8SX-G|ZHpO84cdL< zMt9$f=en+kNasWQI=i|%+S@u?(cEsy2HSi)uFE^f;(Al$Y{0wvL>C#?C{`+~-;jUXZ>l<$0y?5_s z`+em3+n7La$MAXg^XHE+`rdgf4h>hqnu2f{C8Dp@mD|avfuZfq4KO6kw>TB|kKpw;Y|qoC6_-ws3f#b~rjt38Sv&jI7}S|0U1>#Wz2yTh7qaIbK+ zp`qK>(u`NdpFFtH(AeACneqoUtG2Gr6>*+aS66?<1p%?E-u$$*Dn`um+myuU3^G7%!#17(HdSjU#tU*-tme!gT7y zQ4qtjP}>n8r3PA@>+bDtxZT^UW9q)Dujk%UXzICt8&pl)s|xk7T3hFJSsnk<48+o? z>bg2D(<|svj^Q@uv`$QgU$3hW9sj}jmIC2W=C~)=S@a5&AU?9 z&|F{F$W61sJy+|>f1^8xAO-@`qO;OJs^{NCByCaL4Aa7**ic1^cd+l@@#d_?JKH35E@ zXuV#kZq;_2+XHKe+^X)}Zj#oCxZvc3X=o<(Uo_o&c)RIFznR;f^*v~)YwUf5L&JS( zoe9FWzPq=#TMEJ5{VCzNT_;*t?GSD^Y=?K6TiV*Yh*s27e{|C*1@-+Os=E7+A9dH& zUq`IfX?vgc^)}SC-08gqO`2Ls(?{1k@3hp_qi21$g9~M_+)~tuIv+f`ds|+|KWMF_ zjLmi>U1)6YmTFCUIvOvXh+IC#pAwDpxm?#t`bOR9=zaP`S66o%(06ZKzx(9r{pLDD zyxeGPfBLiq3*cUFcU`vux>a6h)cB;Y@4+2KouD?_92070^XJbp`!j?ZJ!akcy1Evi z^xegZ>bqZ8*MpMnb_2ZD_v|KKwnx1GpZ2~xKC0tt``+D^KooyY;@7d`B)_?C#oep`(L_^S^-i%9 z-e*>j*Wd5U-{$*c^#>~Kea@LPXU@!=dFI@UNAC;{x~}oAQMaAVuCBqsZq6xbxO0_v zdfmh`9gUSvN&Qyi!3h%OTYu-B-c7D{TvveUd^(&?(B!Vx!A%Fp!xB`(hs>?cE>{EZ z8h;IzadmWFbvkdjTyPPu#*V>W*A>nwt!ub>2|$` z1rD;a?)=DK@zBLvH!rMqvgbh-ru1ZI-EItS!Ns-@@PpEUPS@34PU(3Z%x(ml6Aa~j z2qm0L+TPRKCk#pkT+Q2@vak2fOIC42YM5U8(MBie$(MIKy)Is^ud72(GyV2KNvErR zvs3a#g~m>#s>wB9u5+@N8qlt~oq)aGf*xxHD$_0wj_&SlYHVr5MWGreTdS=m60Pvr zY6PH$7HkQ?PQJJk1HDmnF$4BNDcrl$>SCKOp$UI4UPsu3{gwFdCe9^kZ@U3*vQvu1 z#)VoZTi0lHNxHf_c$ciRzrTCHIw-@2aem*$J|Bstl6Wg& zRhvZTs9Tsc=qJHVuDg1t6_L8-#)S<|knmg#9rB$v*O(4E&bnRQ{rw#_0K!8rBF1cu z0!GR_dkH&7#{=zMgk^YhmvvCu#eC4hyS#D4fzMu9>y%!;cD0#@1^7Xxw*uyGuR(yN zfl$d4FXgP_;@8}z^4Pgro=D6~P5}y&alduYyYC|WrL&J8l=WeQb_JHW&{P5QK(+2o zuJNr`&wu{WpUSvE}y2a&+J_D=Im^PY7LQUUn?l!3R#N7mc(#q4ezbjiLuELfs<5@s4Vh0F8Gr%cqDJYkmDoYEVv z_JP$nYisSQbo*PC(^@y~;_Wt98=k$^_VMaTZxf~fbkcOK3YGR%J0;0nO$wxNhHX@^3x&$kc;Bo<%&E-fzf-SKUX1h=)Ll?9Uh=WWQ zx~|tP_g49Z)fc6g-klS%iFhaEOfAft4ALa3t`W&(%JAbR%d)|C1s=0WxFEaS-tL0e zGu;5_;RkI)V+K3g8!uhQ#CM~<@{+W^{=LHWaDX&RQQjm0R7r}&7J_rhQZ&@X5(xYH zCF@0s)YD&1(_`>)rb`@>^>uW#wOKpZ*30GK2uMDfuUt!{u~Uk2D9KSwXYyfy$&@GQ zMQL`EwN~Pz+Shmk>(=_N0bz(4Sm^mp-{%pL1x{)(i2ZB#WG zrL`1hQ$84~e5F;SGTA(#q(d%43VvFFjd-ncwKv^vEg$mg?j0EH2bQ#>qr2Za#18{~ zm>uc?%g4oCv0i2Wp0}EKPo7GXZ9t<$&Qxnkc{1u15GHzra_EILK*q!(8<7=#-Pze= z8}=IN>=+p6wsk5cAgp-a-;+asWPe3uO8lf!*nH`8mo zJGy^hs1we6A-#f(oq}U0HyYShLh{BWDY7|QJJ!VBKub$6hQG18_wC)1{$UuO@Ag7( z;$W5QVFs;t*}*>UuC%YGKDnHXH9gh~NY>;^&{sg|D>qAG38?~}7vR#0G(3?h*CF#) z(d~u()o^!rWsi6N5Ny{wz}=G!TmjM;caEE$;ishV$@U~*Bxns4W{DwAXEj$m!b&ej zOk$UJR`kfa5k7luJypG4eZzN$t|r^anCK$ftVa;#Np>^Ny0jbcFrO@gBH2bf`&JLv zYsXXeKDH&(MttM8Dmd-NW%HVGho;6Ymp-^zXgclJ)qfq2?> z>&T-sN=8TLx-pWWf?yU*QtTh;NkaU3VDQ=s zAaGh%52h_M1rc?c%O$k2t(cQ-?Lr6Z$`y#L001ep306E0ww(^FID>wjfmTUJL$C`* zZ8xn~DrJynp%n>SJ5~^FZCtyp!`8{X4T$0q2p%d}sR|~F5pT3oj;jGMHh^$Q(^Kwj z1||mVRuLsZju1L+U2KcaN@V$uA*_Ia7K!CxK&}I?m(f+=Z2^>ii<1O1gU7Kx>xAoux*Nfx5mstQJ=m~%LxrBzQ7$aVY*M4%sI4&Y=v_3*%uEE%Exyaz zDH*;yz;_EhU<9=wqD%810r4Y1AZxHX07;SWGgd$!S-b%$4qk6FutprD$l^_Ab1uMc zSi89{riZ)B^$5tTF>eE_=to#ID4;QR@Ipsuu8fhM{t0@kflZ|}QIpC18f_fk<-Mmv%Bsi^{i2>d( z+CV7i!@;flwllJe9Xmeh=LeX-q8p|@M&vP2G%&TnKGI(dD316=%O*x3Rc#RUEP141 zG*7%$J)kMv{hs@QdBF914-7PAlb8!whONTFD+R#|lyqo`!DpmAUL88Y0HP!%^Wq24 zSmR?cJeYXUSd&RKh^AnoqTLg@0m;yPph_Nqk}$|Dz>r(U4YB=+2%Sa$i^}M5JZk`f z74%x_W%v<-Kwyn0;H<+EU?~#<1HlV0K$zv+;MidtCBguMpgGjhK>Xjs!=K=LLfRu; zB}jsRy=q7Vd)0u5}I5eG_(-=(%2+xvuV+`hmsLTA2@BlOCFmnh!th;ePVwmQ{q-|tdTXMoK;LIIIF18%Mu8Qq?A>?Rv?n~ckgn;ERF#m@OSCxkz}+k1Vk2e z;b1o=kup}1b*ga#(6fTXay>>Jp|oq(FbT6nmd z-W`7&T+BE^+;#_D)vUqe(bQa+B$)O5eJi*M{C(zVC6Q!P)$xoTL{&p1ArUaEm(F0& z86z;-4TwkvO*vuH2Kfip`z%Nf57_VD#dC!t`cjsv9v=h?hf-q+c+hBQf=HNS*6*!Q zko%tf;anwQb4Hq{2SqjBh&haUf-%BMyfLy8^d@yVVbln5lOFItSiko|hyC-P-{JQe z$yl((;900>0zFk{#EO9iDT&?~iH1i!vZfc_5XhIoIl*UWc#70LQCX)D+Jl?{VVJ9b1-oVvuE4T#g4+WU@%s6YEti?Kg z3Z7<71~60O4TiWwM5@%rMOF}LbbL~lV3Ab)X&L-*3vDa+uTc*l{_s6c)kU6+&dEqi z%`g%YgVlsdLwz&h2$85wxtwM6lx!^1CS~R1m0M&hkG*s93|gMeJ@BIE@}M)^m)yH| z77)`Ae3DZ$2%#G2K}}7F-$lG4FxoV6oLYuTfoZu_7B(;6Y8hR!X6MEo`;Wbcu_n2K zF23)Bv-f-Vu(oSO>&C^U!@rY~b4WCf-+*L@+({%+NqNr2kZ|X;8=BFCH#Df+s|d`dSo6E0F9M|QQ4!|NF_(c ztD@9Kl!jltVe=+&A*!%?7cH=^+ge*Gh+b(#fukW(8BKM(3ROK)Sp=s<#8CxiAGxw72UPfCQ4SXsS?F!mupB^Xs9B8 z*N(X=Qh@Td8+RT!@m?fHMn_>2hx4YvA?}`6Ur%@U`_#K8Qy+vWDXfi*QyNjcJ}<+V z5T9ty6fHQow{5N5AdVI^Br+%@d_Lu#UdrtV7%a)Bbo5j)gRH6$H3L}@mHtjDY^TnF2 zZy!1HHc!UyrYosoirCRfDTmCP7*iSapwxtT#_BM{SXCPQW)cEfI6X>)jf~tbar8VA zImJIXoD6_n+L0JxUAuYDp?Bd;vZYvK4RXUi?bxRGx3{)lG+~jR;YP$n6R)OG$Lq+{WS06f^% z1tw{CbJJ98cOobz^W!lC!02v<~s+doiq}S&W5?#7o^qEJJR=Gisfj8Ps zh_G?z!IKUnM|`4MT3fpZ20B|hxPD1z<9-{F#7@TK!_c|M2vlvprL1OE42j7K3<;0! zrPWP0Lsfp8IA$U_u6LtOq8!nr(1Hy+4jgwN8a#EqxfyHK*2d=EzMjSl*bB%~r@*3s z)c`gQpNO|umQ{o&szFPyUZ4-!ivaTtG-Q0WK*7?t|=bGGmK4x|9@L zdu4)uP#6ZmX3GAo4-ZyUY}@WxfguWxJXP}P{fCz_LW9ER1N-a&o{ako4hxYy z?Cy45yS52hYjFe3tu^gAbi&VI3_%nZ5pp`!4NlGiyhouR#Q-lLyP98C2L%L&S;@GF zxLJ)~$A?edw)g1SV}SkxJ(_93p7G9=D_G?)xg%pNQAF_kd4|QZK@@X|@y-M}+D(YL z50y>TN}}B?j>#hjJ0kpi{Q^V61TyC0!-oeHnH2Q|PsX0Uierj~_RiMJ^?!x4n^N4f zhhEfr!%J29mPMR6UPHuBsX0Rxgv_AK%>hM-f-I{Q$K{dzEyb@Yef@(%5OEY^!@qg> z@Ewx$82ltoL9SfA+IIQU#nsR_Z)9e|@YQkTX^q9QS`=3h^5^$onE>5r#42op(`^q6 zP9v6Dacmyhd2{~+O{AZHP%s}lHYD?l4;K?NHT5ZIejK?39JA1Te+j@Ry1|VAhnb%? zxb3l!kh;IVjDm^5VR%4Y`9<5cwQ(e9B{2d0HBxc;Mqso?uypuYip8>46lW0i)h}L(2}jez%Zc}?f3;`TlIU9Th52Ou zw|CY}&}bBLdQ|W4=R3jhB}t}^eUc|*4x#idveDmt_wAQh0i<(D66_XeoJ23CSu6;) z7Ls+P`Bj{q1_XtMV*;Oy+9v1D5yEd0$CMErXqBmcaT>zO$NS3Vzn}2lX_C9+zzN4; zfFDKO;`8U7-+c4+-~J6wiIrFqJu}f_K`kYKUurSFtO~@zEuAG4KH+8%E8lA)Wa%dH z1$#6(_EkfDlsrZg8w%3xtFNqWtspaK2-yq#len|pPoK_Os9goXy&TEBh$9wCcr#a~S#EB^M~jmv2-21m#GzWmaw z8``eu$#O(JIxxqa`I276{pO3$Kd1Awk_S4jWbot}3`p3em?VDHaN(5*joQx-=k#2# zPY~_2y+07L5ZH5x`qbaSm3-TPGk(A6pEO-LL83P8IsC4p3fK-*EW?Y+{_)vo=mc*y zhNanTgk|CZk9h>1IGd0UFSV?CH5O;3zW)APfDgJ(9yM^6kOhFR(2z36hhNp-Y`*s8 z=PgYQ7eAjuCS&qGOzrhqIWm zWodw<4uT;>AyGi-`Fx@}{qgx5uI92=Uh)li`NbC_k5I;AItI4L&cb5%X_Yp4a?zZn z{K(?yjAH|E7oKsPtwlL0)V8Cz85Ec9xj}KnI>};L4&-HotVH3z8*Tl!_RP*J;Q!#J zIDSq@$#&6eX5p+wEA8dts4A@9jyoLF0BS#Ra$^OmNTE(21;z5$Y_?)Eq^ER7+)0wc zF_!S+bMYkl=s9pk|1UPo;%T zx%~0*4U6!nr5nZ3wzB!aHpfQ_WPGHWB#C16rk$9^9ave9pS^R}Tc`z6b$Hj7?b}S) zuTcUSkK7DvK!%7x3Q?M5v6NWF@r#v2wf@8(&%JkShi!TyHc4sK;^>MMc#GcTB#p9y z{5-y7&8g3>b3`MGHCwPwa-2p!eEg~{n^7vnx@Z52W!#2L2uOb<0Nc}ex`BiQt;I4` z5WQc+)Kiikfdic=L*`rPG~2Q{MOkKzQY2oH*{k+`)JaK+U5&LwiIpf;<7XY~vCxRE z-mtN3*;`w7?I~TlgtMSaDT5PxeGW+~1LY(l4$4$W?AryAnTV*Z)TT_Cv6zQQVeyQ- zRE@Hdct>V0+kWom2qCfg2qAXbhEom)63|Z-RIjO7ZL8V5b^DUViH?m;wNtrRn1nn+EW3W!dY$Np+kGt)U02;fLpQ*uqiP92rNv4%!u2C(80^?qL+R7 zoV;W;HAUv)4ex%_4f5g8R|gkqK;lcU-FX1k-fbmM&se@P--c*syIc zVzmRgx1Yt$UcBs0ao_$ewhD2DeYm;kYBC z=+(ljZoZ+s+j(Zgq6{Utg>D{YRCG*ioE9V>gUJk;iKJ-T+lNj%99t3Kp1~5XVCwW4 zw06y$C8bp0wBl@tjD$$+d>9>PkZ85BNT@_`7PhJq4NS&kN_Dhh&ygdi-~afV`rC~c zJ~~>nc(RJajQipUpAxdc;EY1ea*bPXQc40T*nRN$S;uiK&!1IgPRTDQDlVdBXl5)} zM1`WfL^S#cVv#G3qhuB5;|e2IUNd+B$N=#3yZ24M|0A~jB%pfg01 z?4ze0jzw1TTy#nr^{Ct_?!q-Q=M-5`eDIz9F^)1aHk8SoxjjJc%By(1!->5v#o zmqfUkN|}z}IsWE1{I6dg;W@e~fD^DL#lnv^6_xxo32H(LG3eHyH=kD~nvzo?H9N;r z`c`q#v>EQwEE#Dj^nfQenkJCw(2KN^vC6fF_wK}NS5TW1Q~Og2KPvIX-~E;Vi#wB=VZ@w$m~7G$Pp<&@W~D=V1# z8YF~atTh=38vJ6fe82rjh5Cuv94+D`h+&CT};#4#>;J3eqpvMfvXCE;Tpa0d!y)rZl>wkg~EL%7tv%PlRYWpNB8RPrhf1)7%^7$9s7v?5FNGSH3 zve^#DE+pQ6s|beI=<>vc2`JWCUA=r#=H#4IG^3z6NgW*t8D0_Cy@iL-Q?Av=-r2iz z%f_{0GR|OwL;f=Z@i{aHch=;7f!Kw~O3peqz+Asm(0y*0&X|*=(WT}uoS&JRJ}CcMXCNb|L~a~^A90}UNa4%EtzxHAz~Tw zqF-u(CLkyQK~$|tMifmor=(3pcVybdH$Y5_)`nnz9^w~>+*CM*DwS1m?ASZ|P}X-{ z4n|&7!hfb8KKH_Jem4Qze=p?<$Js?lgZ|z>v_udLesPFo5mBb3loY+eoSc>zA7{#) zpG87K60{Kk3ZmfT49w;g0AWE2I78B|lSdBj-L-A=d}PSIm1?Zz{sa5)m%n-yipt_Q zIu0V~ELZr;Rf!9PdA>^UF!8F91U(|C!ITUKK>dz{(7;fzu;soXA&}-TkWpbsg2ZYy z)cGQoo;m@6ySG;$u*j4#y8l2v448oYfUNMCLyQ&3s|un~hH16w@qT_WT4hKW!86c6LRCS=r0za_0$eRak21LsNtpKP9L8i0dG}> z!zyxlfJ)_WmH*5y0NJg0n^-w5aYTS`xm+5WQxrwb1_|N#9*IS8h}>TR(CGQc&c1u% z=%IsKX2oqrUuD_P94n1OK0d^Ejrqlxe+G}02rUqPbp29`DHA~ zXK(^jYLkybBEUBay=GC9Nc7EI@N&HvrGZJ2u|>wp0$))tdYy(v}MAwX#b?e zWtkx~p`lPk1*Y>n3D(A&wNdgYH@2=q?j5*L5VAGCcA$rYm7><^V%4y@ZwT5J3I&Ck zrFe~8zFZ!yon~DaL>3Fe$}j{hIJ`1T%Lz$gymow~T&>L&gay8Gzx1MbUhbt@AlQ^HT3%`b-8?O+$zCZ z1of$sU?0Zf8iXPa`6hW7ryUiEzb`2*oyp5((b}~5D129HwQ;_j+$TaehfjxQp^ELq zD>TqgrHK;cOdPNk`M{nMTrQWz#2XTj0+RT~Xuv&;jMIkOppGIG;=;I6?BV6Ia12yB z=GAcN01jf_fQ7AcDNW?9C!;|74Zx=Z@xO4-F{XKoa#g-D^WQ8~R*XCgRV1NWHK>Q` zSpO=txo}Cc0G|)_1xO4c7-Xo6Y-OPp7)Eq0gCv~~5qez`KEm$rG zFF$6joSong_Mh*5G&v~f0R0y*pzmnC@eydFS|evBtY%nW-}57XMwknTQ2AEjNxyPt zam2hi45Jy%jC@IASAX7k|KU&ab-(em)v=OCf=9k3p8rWC|47J{%>Pwj-02_v$4~a- zAD()o!LLy~;7NQn`?(bu_mkWHabsQ2QU#uf@kdl>fcS}zwRsZX1xD8TB;-Dp+2@_} z_{S&nede(zBloe?zubKM)06q$*!N`QK9<^pH+DS9FTDQ6^X0LC#{wP;cr4(tfX4zJ z3wSKxv4F<{9t(IZ;IV+m0v-!^Ea0(##{wP;cr4(tfX4zJ3wSKxv4F<{9t(IZ;IV+m z0v-!^Ea0(##{wP;cr4(tfX4zJ3wSKxv4F<{9t(IZ;IV+m0v-!^Ea0)g|GyTv`(#zS zd;7`Czx~;hmH*l5Co8`)_Q}eRkvvfcxF}(!JxK#3;{qt&qy415KBxCzf8PA0#J4^} zwk3r94g@c{b#p-qPgJi?awM>9^b$J EAHT)Yng9R* diff --git a/painter/res/packr_config/linux64.json b/painter/res/packr_config/linux64.json deleted file mode 100644 index 30f554d..0000000 --- a/painter/res/packr_config/linux64.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "platform": "linux64", - "jdk": "out/jdks/linux64-jdk", - "executable": "MakerKing", - "classpath": [ - "desktop/build/libs/MakerKing.data" - ], - "removelibs": [ - "desktop/build/libs/MakerKing.data" - ], - "mainclass": "net.jumpai.client.desktop.DesktopLauncher", - "minimizejre": "hard", - "output": "out/packr/linux64" -} diff --git a/painter/res/packr_config/mac.json b/painter/res/packr_config/mac.json deleted file mode 100644 index ff55007..0000000 --- a/painter/res/packr_config/mac.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "platform": "mac", - "jdk": "out/jdks/mac-jdk", - "executable": "MakerKing", - "classpath": [ - "desktop/build/libs/MakerKing.data" - ], - "removelibs": [ - "desktop/build/libs/MakerKing.data" - ], - "mainclass": "net.jumpai.client.desktop.DesktopLauncher", - "minimizejre": "hard", - "output": "out/packr/mac", - "icon": "tools/res/icons/mac.icns" -} \ No newline at end of file diff --git a/painter/res/packr_config/win64.json b/painter/res/packr_config/win64.json deleted file mode 100644 index 4d7e5a7..0000000 --- a/painter/res/packr_config/win64.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "platform": "windows64", - "jdk": "out/jdks/win64-jdk", - "executable": "MakerKing", - "classpath": [ - "desktop/build/libs/MakerKing.data" - ], - "removelibs": [ - "desktop/build/libs/MakerKing.data" - ], - "mainclass": "net.jumpai.client.desktop.DesktopLauncher", - "minimizejre": "hard", - "output": "out/packr/win64" -} diff --git a/painter/res/shaders/background-biome.fs.glsl b/painter/res/shaders/background-biome.fs.glsl deleted file mode 100644 index 54c35e6..0000000 --- a/painter/res/shaders/background-biome.fs.glsl +++ /dev/null @@ -1,32 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; -uniform sampler2D u_biomeMask; - -uniform vec2 u_offset; -uniform vec2 u_scale; - -void main() -{ - vec4 color = texture2D(u_texture, v_texCoords); - - //coordinates in the map corresponds to texCoordinates * scale + offset but with Y reversed - - vec2 mapCoords = v_texCoords; - mapCoords.y = 1.0 - mapCoords.y; - mapCoords *= u_scale; - mapCoords += u_offset; - mapCoords.y = 1.0 - mapCoords.y; - - //alpha corresponds to the alpha of the mask - float a = texture2D(u_biomeMask, mapCoords).a; - - //apply alpha - gl_FragColor = vec4(color.rgb, a); - - //to debug the mask - //gl_FragColor = texture2D(u_biomeMask, mapCoords); -} \ No newline at end of file diff --git a/painter/res/shaders/biome-brush.fs.glsl b/painter/res/shaders/biome-brush.fs.glsl deleted file mode 100644 index 8338e72..0000000 --- a/painter/res/shaders/biome-brush.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; - -#include math.h.glsl - -void main() -{ - float alpha = 1.0 - 4.0 * (pow2(v_texCoords.x - 0.5) + pow2(0.5 - v_texCoords.y)); - - gl_FragColor = vec4(v_color.rgb, v_color.a * alpha); -} \ No newline at end of file diff --git a/painter/res/shaders/boomerang.fs.glsl b/painter/res/shaders/boomerang.fs.glsl deleted file mode 100644 index 2c013c4..0000000 --- a/painter/res/shaders/boomerang.fs.glsl +++ /dev/null @@ -1,30 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec3 v_texCoords_h; - -uniform sampler2D u_texture; -uniform mat3 u_rotationTrans; - -#include lighting.h.glsl - -void main() -{ - vec2 texCoords = v_texCoords_h.xy / v_texCoords_h.z; //2D cartesian coordinates from homegeneous coordinates - - // rotate - vec2 rot_center = vec2(0.5, 0.5); - texCoords = (vec3(texCoords - rot_center, 0.0) * u_rotationTrans).xy + rot_center; - - texCoords.x *= 1.2; - texCoords.x -= 0.1; - - // manual clamp to border - if(texCoords.x < 0.0 - || texCoords.x > 1.0 - || texCoords.y < 0.0 - || texCoords.y > 1.0) - discard; - - gl_FragColor = sunPreshaded(u_texture, texCoords) * v_color; -} \ No newline at end of file diff --git a/painter/res/shaders/boomerang.vs.glsl b/painter/res/shaders/boomerang.vs.glsl deleted file mode 100644 index 602c906..0000000 --- a/painter/res/shaders/boomerang.vs.glsl +++ /dev/null @@ -1,28 +0,0 @@ -#include glsl3_vs.h.glsl - -attribute vec4 a_position; -attribute vec4 a_color; -attribute vec2 a_texCoord0; - -uniform sampler2D u_texture; -uniform mat4 u_projTrans; -uniform mat3 u_perspectiveTrans; - -varying vec4 v_color; -varying vec3 v_texCoords_h; // homgeneous texture coordinates - -#include lightmask.h.glsl - -void main() -{ - const float scale = 500.0; //size used to generate the GIMP matrix - - vec3 scaled_coords = vec3(a_texCoord0 * scale, 1.0) * u_perspectiveTrans; - - v_texCoords_h = vec3(scaled_coords.xy / scale, scaled_coords.z); - - gl_Position = u_projTrans * a_position; - v_color = a_color; - - setupLightMask(); -} \ No newline at end of file diff --git a/painter/res/shaders/dark-frame.fs.glsl b/painter/res/shaders/dark-frame.fs.glsl deleted file mode 100644 index fcb37b1..0000000 --- a/painter/res/shaders/dark-frame.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; - -#include math.h.glsl - -void main() -{ - float dst2 = min(1.0, pow2(v_texCoords.x - 0.5) + pow2(v_texCoords.y - 0.5)); - - gl_FragColor = (1.0 - dst2) * texture2D(u_texture, v_texCoords) * v_color; -} \ No newline at end of file diff --git a/painter/res/shaders/editor-cant-place.fs.glsl b/painter/res/shaders/editor-cant-place.fs.glsl deleted file mode 100644 index 561fd16..0000000 --- a/painter/res/shaders/editor-cant-place.fs.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; - -void main() -{ - vec4 color = texture2D(u_texture, v_texCoords); - gl_FragColor = v_color * color.a; -} \ No newline at end of file diff --git a/painter/res/shaders/electrocution.fs.glsl b/painter/res/shaders/electrocution.fs.glsl deleted file mode 100644 index 7e66da1..0000000 --- a/painter/res/shaders/electrocution.fs.glsl +++ /dev/null @@ -1,18 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; -uniform int u_time; - -#include lighting.h.glsl - -void main() -{ - vec4 color = sunPreshaded(u_texture, v_texCoords) * v_color; - - color.rgb *= mod(u_time / 10.0, 2.0); - - gl_FragColor = color; -} \ No newline at end of file diff --git a/painter/res/shaders/entity-damage.fs.glsl b/painter/res/shaders/entity-damage.fs.glsl deleted file mode 100644 index d51a47e..0000000 --- a/painter/res/shaders/entity-damage.fs.glsl +++ /dev/null @@ -1,20 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; -uniform vec3 u_dmgColor; - -uniform float u_dmgCooldown; - -#include lighting.h.glsl - -void main() -{ - vec4 color = sunPreshaded(u_texture, v_texCoords) * v_color; - - float alpha = -3.0 * (u_dmgCooldown - 0.5) * (u_dmgCooldown - 0.5) + 0.75; //y = -3(x-0.5)^2 + 0.75 parabola (75% opacity at peak) - - gl_FragColor = color * (1.0 - alpha) + vec4(u_dmgColor, 1.0) * color.a * alpha; -} \ No newline at end of file diff --git a/painter/res/shaders/grayscale.fs.glsl b/painter/res/shaders/grayscale.fs.glsl deleted file mode 100644 index 289baf7..0000000 --- a/painter/res/shaders/grayscale.fs.glsl +++ /dev/null @@ -1,20 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; - -void main() -{ - vec4 texColor = texture2D(u_texture, v_texCoords); - - if(texColor.a == 0.0) - discard; - - vec4 color = texColor * v_color; - float gray = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b; - vec3 grayscale = vec3(gray); - - gl_FragColor = vec4(grayscale, color.a); -} \ No newline at end of file diff --git a/painter/res/shaders/ice.fs.glsl b/painter/res/shaders/ice.fs.glsl deleted file mode 100644 index bac3f7f..0000000 --- a/painter/res/shaders/ice.fs.glsl +++ /dev/null @@ -1,40 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; -varying vec2 v_worldPos; - -uniform sampler2D u_texture; -uniform sampler2D u_player; - -uniform vec2 u_player_pos; -uniform vec2 u_player_origin; -uniform vec2 u_player_size; - -#include lighting.h.glsl -#include math.h.glsl - -void main() -{ - vec4 color = sunPreshaded(u_texture, v_texCoords) * v_color; - - if(color.a == 0.0) - discard; - - float distance = u_player_pos.y - v_worldPos.y; - - //reflect it with a line just under the player - vec2 reflectPos = vec2(v_worldPos.x, u_player_pos.y + distance); - - //convert world position of player to tex coordinates - vec2 p_texCoords = (reflectPos - (u_player_pos + u_player_origin)) / u_player_size; - - //find player color - vec4 playerColor = texture2D(u_player, p_texCoords); - - //adjust player color opacity from y - gl_FragColor = vec4(color.rgb + (playerColor.rgb * sigmoid(100.0 / distance) * 0.8), color.a); - - //DEBUG - //gl_FragColor = playerColor; -} diff --git a/painter/res/shaders/ice.vs.glsl b/painter/res/shaders/ice.vs.glsl deleted file mode 100644 index fa71aa2..0000000 --- a/painter/res/shaders/ice.vs.glsl +++ /dev/null @@ -1,23 +0,0 @@ -#include glsl3_vs.h.glsl - -attribute vec4 a_position; -attribute vec4 a_color; -attribute vec2 a_texCoord0; - -uniform mat4 u_projTrans; - -varying vec4 v_color; -varying vec2 v_texCoords; -varying vec2 v_worldPos; - -#include lightmask.h.glsl - -void main() -{ - gl_Position = u_projTrans * a_position; - v_color = a_color; - v_texCoords = a_texCoord0; - v_worldPos = a_position.xy; - - setupLightMask(); -} \ No newline at end of file diff --git a/painter/res/shaders/land.fs.glsl b/painter/res/shaders/land.fs.glsl deleted file mode 100644 index 90751bb..0000000 --- a/painter/res/shaders/land.fs.glsl +++ /dev/null @@ -1,87 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; - -varying vec2 v_texCoords0; -varying vec2 v_texCoords1; -varying vec2 v_texCoords2; -varying vec2 v_texCoords3; - -#ifdef LIGHTING_ENABLED - varying vec2 v_normalCoords0; - varying vec2 v_normalCoords1; - varying vec2 v_normalCoords2; - varying vec2 v_normalCoords3; - - varying vec2 v_preshadedCoords0; - varying vec2 v_preshadedCoords1; - varying vec2 v_preshadedCoords2; - varying vec2 v_preshadedCoords3; -#endif - -varying vec2 v_biomeMapCoords; - -uniform sampler2D u_texture; -uniform sampler2D u_biomeMask0; -uniform sampler2D u_biomeMask1; -uniform sampler2D u_biomeMask2; - -uniform int u_enabled0; -uniform int u_enabled1; -uniform int u_enabled2; -uniform int u_enabled3; - -#include lighting.h.glsl - -void main() -{ - float a0 = texture2D(u_biomeMask0, v_biomeMapCoords).a * u_enabled0; - vec4 color0 = texture2D(u_texture, v_texCoords0); - - float a1 = texture2D(u_biomeMask1, v_biomeMapCoords).a * u_enabled1; - vec4 color1 = texture2D(u_texture, v_texCoords1); - - float a2 = texture2D(u_biomeMask2, v_biomeMapCoords).a * u_enabled2; - vec4 color2 = texture2D(u_texture, v_texCoords2); - - float a3 = (1.0 - a0 - a1 - a2) * u_enabled3; - vec4 color3 = texture2D(u_texture, v_texCoords3); - - #ifdef LIGHTING_ENABLED - vec4 lightWithSun = getLightLevelWithSun(v_normalCoords0); - - vec3 light0 = lightWithSun.rgb; - vec3 light1 = getLightLevel(v_normalCoords1); - vec3 light2 = getLightLevel(v_normalCoords2); - vec3 light3 = getLightLevel(v_normalCoords3); - - float sun = lightWithSun.a; - - if(v_preshadedCoords.x > 1.5) - { - color0 *= vec4(light0, 1.0); - color1 *= vec4(light1, 1.0); - color2 *= vec4(light2, 1.0); - color3 *= vec4(light3, 1.0); - } - else - { - vec4 preshaded0 = texture2D(u_preshaded, v_preshadedCoords0); - vec4 preshaded1 = texture2D(u_preshaded, v_preshadedCoords1); - vec4 preshaded2 = texture2D(u_preshaded, v_preshadedCoords2); - vec4 preshaded3 = texture2D(u_preshaded, v_preshadedCoords3); - - color0 = sun * preshaded0 + (1.0 - sun) * color0 * vec4(light0, 1.0); - color1 = sun * preshaded1 + (1.0 - sun) * color1 * vec4(light1, 1.0); - color2 = sun * preshaded2 + (1.0 - sun) * color2 * vec4(light2, 1.0); - color3 = sun * preshaded3 + (1.0 - sun) * color3 * vec4(light3, 1.0); - } - #endif - - vec4 color = color0 * a0 + - color1 * a1 + - color2 * a2 + - color3 * a3; - - gl_FragColor = color * v_color; -} \ No newline at end of file diff --git a/painter/res/shaders/land.vs.glsl b/painter/res/shaders/land.vs.glsl deleted file mode 100644 index 58d929a..0000000 --- a/painter/res/shaders/land.vs.glsl +++ /dev/null @@ -1,77 +0,0 @@ -#include glsl3_vs.h.glsl - -attribute vec4 a_position; -attribute vec4 a_color; -attribute vec2 a_texCoord0; - -uniform mat4 u_projTrans; - -varying vec4 v_color; -varying vec2 v_texCoords0; -varying vec2 v_texCoords1; -varying vec2 v_texCoords2; -varying vec2 v_texCoords3; - -#ifdef LIGHTING_ENABLED - varying vec2 v_normalCoords0; - varying vec2 v_normalCoords1; - varying vec2 v_normalCoords2; - varying vec2 v_normalCoords3; - - varying vec2 v_preshadedCoords0; - varying vec2 v_preshadedCoords1; - varying vec2 v_preshadedCoords2; - varying vec2 v_preshadedCoords3; -#endif - -varying vec2 v_biomeMapCoords; - -uniform vec2 u_biomeMapOffset; -uniform vec2 u_biomeMapScale; - -uniform vec2 u_biomeOffset0_f; -uniform vec2 u_biomeOffset1_f; -uniform vec2 u_biomeOffset2_f; -uniform vec2 u_biomeOffset3_f; - -#ifdef LIGHTING_ENABLED - uniform vec2 u_biomeOffset0_n; - uniform vec2 u_biomeOffset1_n; - uniform vec2 u_biomeOffset2_n; - uniform vec2 u_biomeOffset3_n; - - uniform vec2 u_biomeOffset0_p; - uniform vec2 u_biomeOffset1_p; - uniform vec2 u_biomeOffset2_p; - uniform vec2 u_biomeOffset3_p; -#endif - -#include lightmask.h.glsl - -void main() -{ - v_color = a_color; - v_texCoords0 = a_texCoord0 + u_biomeOffset0_f; - v_texCoords1 = a_texCoord0 + u_biomeOffset1_f; - v_texCoords2 = a_texCoord0 + u_biomeOffset2_f; - v_texCoords3 = a_texCoord0 + u_biomeOffset3_f; - - gl_Position = u_projTrans * a_position; - - v_biomeMapCoords = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_biomeMapScale + u_biomeMapOffset; - v_biomeMapCoords.y = 1.0 - v_biomeMapCoords.y; - - setupLightMask(); - - #ifdef LIGHTING_ENABLED - v_normalCoords0 = v_normalCoords + u_biomeOffset0_n; - v_normalCoords1 = v_normalCoords + u_biomeOffset1_n; - v_normalCoords2 = v_normalCoords + u_biomeOffset2_n; - v_normalCoords3 = v_normalCoords + u_biomeOffset3_n; - - v_preshadedCoords0 = v_preshadedCoords + u_biomeOffset0_p; - v_preshadedCoords1 = v_preshadedCoords + u_biomeOffset1_p; - v_preshadedCoords2 = v_preshadedCoords + u_biomeOffset2_p; - v_preshadedCoords3 = v_preshadedCoords + u_biomeOffset3_p; - #endif -} \ No newline at end of file diff --git a/painter/res/shaders/roast.fs.glsl b/painter/res/shaders/roast.fs.glsl deleted file mode 100644 index 0d882cb..0000000 --- a/painter/res/shaders/roast.fs.glsl +++ /dev/null @@ -1,24 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; -uniform vec3 u_fireColor; -uniform vec3 u_roastedColor; - -uniform float u_fireLevel; - -void main() -{ - // reproduced gradient can be see here - // http://colorzilla.com/gradient-editor/#000000+0,ff7700+17,ff9232+72,ff7700+72,ff6e00+100&0.8+0,0+100 - - float alpha = u_fireLevel * 0.75; - vec3 roast_color = u_roastedColor * max(0.0, (u_fireLevel - 0.6) / 0.4) - + u_fireColor * min(1.0, (1.0 - u_fireLevel) / 0.4); - - vec4 color = texture2D(u_texture, v_texCoords) * v_color; - - gl_FragColor = color * (1.0 - alpha) + vec4(roast_color, 1.0) * color.a * alpha; -} \ No newline at end of file diff --git a/painter/res/shaders/waterfall.fs.glsl b/painter/res/shaders/waterfall.fs.glsl deleted file mode 100644 index b29856d..0000000 --- a/painter/res/shaders/waterfall.fs.glsl +++ /dev/null @@ -1,25 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; //mask - -uniform sampler2D u_waterTexture; -uniform vec2 u_texPos; -uniform vec4 u_uv; - -uniform float u_offset; - -#include lighting.h.glsl - -void main() -{ - vec2 texPos = (v_texCoords - u_uv.xy) / u_uv.zw / 8.0 - + vec2(u_texPos.x, 1.0 - u_texPos.y - u_offset); - - vec4 color = texture2D(u_waterTexture, texPos); - color.a *= texture2D(u_texture, v_texCoords).a; - - gl_FragColor = vec4(getLightLevel() * color.rgb, color.a) * v_color; -} \ No newline at end of file diff --git a/painter/res/shaders/wrap.fs.glsl b/painter/res/shaders/wrap.fs.glsl deleted file mode 100644 index 4e99167..0000000 --- a/painter/res/shaders/wrap.fs.glsl +++ /dev/null @@ -1,30 +0,0 @@ -#include glsl3_fs.h.glsl - -varying vec4 v_color; -varying vec2 v_texCoords; - -uniform sampler2D u_texture; -uniform vec2 u_offset; -uniform vec4 u_uv; - -#include lighting.h.glsl - -vec2 wrap(vec2 coords) -{ - vec2 size = u_uv.zw - u_uv.xy; - return (1.0 - abs(mod((coords - u_uv.xy) / size - u_offset, 2.0) - 1.0)) * size + u_uv.xy; -} - -void main() -{ - vec2 coords = wrap(v_texCoords); - - #ifdef LIGHTING_ENABLED - vec2 normalCoords = wrap(v_normalCoords); - vec2 preshadedCoords = wrap(v_preshadedCoords); - #else - vec2 normalCoords = vec2(0.0); - vec2 preshadedCoords = vec2(0.0); - #endif - gl_FragColor = sunPreshaded(u_texture, coords, normalCoords, preshadedCoords) * v_color; -} \ No newline at end of file diff --git a/painter/src/main/java/com/normalpainter/app/ColorComponent.java b/painter/src/main/java/com/normalpainter/app/ColorComponent.java index 3463eaa..b75d416 100644 --- a/painter/src/main/java/com/normalpainter/app/ColorComponent.java +++ b/painter/src/main/java/com/normalpainter/app/ColorComponent.java @@ -128,7 +128,7 @@ public boolean updateAxis(int screenX, int screenY, boolean evenIfOut) float rangeSize = 2000f * screen.camera.zoom; float rangeStartX = screen.camera.position.x + WORLD_WIDTH / 2f * screen.camera.zoom - rangeSize * 1.05f; - float rangeStartY = screen.camera.position.y + WORLD_HEIGHT / 2f * screen.camera.zoom - rangeSize * 1.05f; + float rangeStartY = screen.camera.position.y + (WORLD_HEIGHT / 2f - 500f) * screen.camera.zoom - rangeSize * 1.05f; boolean inBox = MathUtil.inBox(mousePos.x, mousePos.y, rangeStartX, rangeStartY, rangeSize); @@ -208,7 +208,7 @@ public void draw() { float rangeSize = 2000f * screen.camera.zoom; float rangeStartX = screen.camera.position.x + WORLD_WIDTH / 2f * screen.camera.zoom - rangeSize * 1.05f; - float rangeStartY = screen.camera.position.y + WORLD_HEIGHT / 2f * screen.camera.zoom - rangeSize * 1.05f; + float rangeStartY = screen.camera.position.y + (WORLD_HEIGHT / 2f - 500f) * screen.camera.zoom - rangeSize * 1.05f; screen.batch.draw(screen.normalRange, rangeStartX, rangeStartY, rangeSize / 2f, rangeSize / 2f, rangeSize, rangeSize, invert ? -1f : 1f, invert ? -1f : 1f, 0f); @@ -225,7 +225,7 @@ public void draw() screen.batch.setColor(screen.painter.color); screen.batch.draw(screen.whitePixel, screen.camera.position.x + WORLD_WIDTH / 2f * screen.camera.zoom - rangeSize * 1.05f, - screen.camera.position.y + WORLD_HEIGHT / 2f * screen.camera.zoom - rangeSize * 1.05f - colorSize * 1.05f, + screen.camera.position.y + (WORLD_HEIGHT / 2f - 500f) * screen.camera.zoom - rangeSize * 1.05f - colorSize * 1.05f, rangeSize, colorSize); } diff --git a/painter/src/main/java/com/normalpainter/app/NormalPainterConfig.java b/painter/src/main/java/com/normalpainter/app/NormalPainterConfig.java index bc905a6..6157400 100644 --- a/painter/src/main/java/com/normalpainter/app/NormalPainterConfig.java +++ b/painter/src/main/java/com/normalpainter/app/NormalPainterConfig.java @@ -1,15 +1,7 @@ package com.normalpainter.app; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.io.*; /** * Configuration for {@link NormalPainterApp} @@ -28,6 +20,10 @@ public void load(File file) { conf = (Conf)ois.readObject(); } + catch(FileNotFoundException ex) + { + conf = new Conf(); + } catch(IOException | ClassCastException | ClassNotFoundException ex) { System.out.println("Failed to read config"); @@ -92,10 +88,10 @@ public void apply(NormalPainterScreen screen) screen.fillBlue = conf.fillBlue; - screen.stage.flatFile.setText(conf.flatFile); - screen.stage.maskFile.setText(conf.refFile); - screen.stage.inputFile.setText(conf.inputFile); - screen.stage.outputFile.setText(conf.outputFile); + screen.stage.flatFile = conf.flatFile; + screen.stage.maskFile = conf.refFile; + screen.stage.inputFile = conf.inputFile; + screen.stage.outputFile = conf.outputFile; screen.stage.update(); } @@ -143,10 +139,10 @@ public void setFrom(NormalPainterScreen screen) conf.fillBlue = screen.fillBlue; - conf.flatFile = screen.stage.flatFile.getText(); - conf.refFile = screen.stage.maskFile.getText(); - conf.inputFile = screen.stage.inputFile.getText(); - conf.outputFile = screen.stage.outputFile.getText(); + conf.flatFile = screen.stage.flatFile; + conf.refFile = screen.stage.maskFile; + conf.inputFile = screen.stage.inputFile; + conf.outputFile = screen.stage.outputFile; } private static class Conf implements Serializable diff --git a/painter/src/main/java/com/normalpainter/app/NormalPainterLauncher.java b/painter/src/main/java/com/normalpainter/app/NormalPainterLauncher.java index 4862cb1..03c3440 100644 --- a/painter/src/main/java/com/normalpainter/app/NormalPainterLauncher.java +++ b/painter/src/main/java/com/normalpainter/app/NormalPainterLauncher.java @@ -1,5 +1,6 @@ package com.normalpainter.app; +import com.badlogic.gdx.Files; import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; @@ -18,6 +19,10 @@ public static void main(String[] args) throws Throwable config.useGL30 = true; config.width = 1280; config.height = 720; + config.addIcon("gfx/icon/icon_128.png", Files.FileType.Classpath); + config.addIcon("gfx/icon/icon_64.png", Files.FileType.Classpath); + config.addIcon("gfx/icon/icon_32.png", Files.FileType.Classpath); + config.addIcon("gfx/icon/icon_16.png", Files.FileType.Classpath); new LwjglApplication(new NormalPainterApp(), config) { @Override diff --git a/painter/src/main/java/com/normalpainter/app/NormalPainterScreen.java b/painter/src/main/java/com/normalpainter/app/NormalPainterScreen.java index 932ca38..9e34c92 100644 --- a/painter/src/main/java/com/normalpainter/app/NormalPainterScreen.java +++ b/painter/src/main/java/com/normalpainter/app/NormalPainterScreen.java @@ -30,6 +30,7 @@ import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.I18NBundle; import com.badlogic.gdx.utils.ObjectMap; +import com.kotcrab.vis.ui.widget.Menu; import com.normalpainter.app.dialog.OkayDialog; import com.normalpainter.render.ui.SkinTinter; import com.normalpainter.app.buffer.GdxPixmap; @@ -250,6 +251,10 @@ public void resize(int width, int height) font.setFixedWidthGlyphs("0123456789"); invalidateRecursively(stage.getRoot()); + + for(Menu menu : ReflectionUtil.>get(stage.menuBar, "menus")) { + invalidateRecursively(menu); + } } } @@ -525,50 +530,55 @@ private void drawAt(int screenX, int screenY, boolean dragged) } @Override - public boolean scrolled(int amount) + public boolean scrolled(float amountX, float amountY) { if(app.getScreen() != this) return false; if(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT)) { - colorPicker.axisPos.rotate(amount * colorPicker.normalRotateSpeed); + colorPicker.axisPos.rotate(amountY * colorPicker.normalRotateSpeed); colorPicker.updateNormalDir(); return true; } if(Gdx.input.isKeyPressed(Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Keys.CONTROL_RIGHT)) { - painter.brushSize += amount; + painter.brushSize += amountY; painter.brushSize = max(0, painter.brushSize); return true; } - camera.zoom *= Math.pow(1.1f, amount); + camera.zoom *= Math.pow(1.1f, amountY); return true; } + public void testLighting() + { + if(flat == null) + { + new OkayDialog(assets, "A flat asset is needed for light testing").show(stage); + return; + } + + lightScreen.texture = flat; + buffer.setColor(0.5f, 0.5f, 1f, 1f); + buffer.fillRectangle(0, 0, buffer.getWidth(), buffer.getHeight()); + buffer.setBlending(Blending.SourceOver); + buffer.drawPixmap(painter.preview, 0, 0); + + lightScreen.normal = new TextureRegion(new Texture(buffer)); + lightScreen.camera.setPosition(camera.position.x, camera.position.y); + lightScreen.camera.zoom = camera.zoom; + app.setScreen(lightScreen); + } + @Override public boolean keyDown(int keycode) { if(keycode == Keys.F5 && app.getScreen() == this) { - if(flat == null) - { - new OkayDialog(assets, "A flat asset is needed for light testing").show(stage); - return true; - } - - lightScreen.texture = flat; - buffer.setColor(0.5f, 0.5f, 1f, 1f); - buffer.fillRectangle(0, 0, buffer.getWidth(), buffer.getHeight()); - buffer.setBlending(Blending.SourceOver); - buffer.drawPixmap(painter.preview, 0, 0); - - lightScreen.normal = new TextureRegion(new Texture(buffer)); - lightScreen.camera.setPosition(camera.position.x, camera.position.y); - lightScreen.camera.zoom = camera.zoom; - app.setScreen(lightScreen); + testLighting(); return true; } @@ -599,22 +609,7 @@ else if(keycode == Keys.S && (Gdx.input.isKeyPressed(Keys.CONTROL_LEFT) || Gdx.i else if(keycode == Keys.Z && (Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) || Gdx.input.isKeyPressed(Keys.CONTROL_RIGHT)) { - if(painter.maskMultiply) - { - RangedGdxBuffer tmp = painter.maskBuffer; - painter.maskBuffer = painter.maskBufferBackup; - painter.updatePreview(true); - - painter.maskBufferBackup = tmp; - } - else - { - GdxPixmap tmp = painter.pixmap; - painter.pixmap = painter.backup; - painter.updatePreview(true); - - painter.backup = tmp; - } + painter.undo(); return true; } else if(keycode == Keys.SPACE) diff --git a/painter/src/main/java/com/normalpainter/app/NormalPainterStage.java b/painter/src/main/java/com/normalpainter/app/NormalPainterStage.java index 2181750..7180630 100644 --- a/painter/src/main/java/com/normalpainter/app/NormalPainterStage.java +++ b/painter/src/main/java/com/normalpainter/app/NormalPainterStage.java @@ -16,36 +16,21 @@ import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; -import com.badlogic.gdx.scenes.scene2d.ui.CheckBox; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.SelectBox; -import com.badlogic.gdx.scenes.scene2d.ui.Table; -import com.badlogic.gdx.scenes.scene2d.ui.TextButton; -import com.badlogic.gdx.scenes.scene2d.ui.TextField; +import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.utils.viewport.FitViewport; +import com.kotcrab.vis.ui.VisUI; +import com.kotcrab.vis.ui.widget.*; import com.normalpainter.app.buffer.GdxPixmap; -import com.normalpainter.app.dialog.AxisConfigurationDialog; -import com.normalpainter.app.dialog.DesktopFileSelector; -import com.normalpainter.app.dialog.NewPixmapDialog; -import com.normalpainter.app.dialog.OkayDialog; -import com.normalpainter.app.dialog.SaveDialog; -import com.normalpainter.util.ui.dynamic.DynamicLabel; -import com.normalpainter.util.ui.dynamic.DynamicStage; -import com.normalpainter.util.ui.dynamic.DynamicTable; -import com.normalpainter.util.ui.dynamic.DynamicTextButton; -import com.normalpainter.util.ui.dynamic.DynamicTextField; -import com.normalpainter.util.ui.listener.ChangeAdapter; -import com.normalpainter.util.ui.listener.ClickAdapter; -import com.normalpainter.util.ui.listener.HoverAdapter; -import com.normalpainter.util.ui.listener.TouchNothingListener; -import com.normalpainter.util.ui.listener.TouchOutOfActorListener; -import com.normalpainter.util.ui.scrollpane.BetterScrollPane; -import jpen.PenDevice; +import com.normalpainter.app.dialog.*; import com.normalpainter.render.AssetSupplier; import com.normalpainter.util.math.MathUtil; import com.normalpainter.util.ui.BetterSlider; +import com.normalpainter.util.ui.dynamic.*; +import com.normalpainter.util.ui.listener.*; +import com.normalpainter.util.ui.scrollpane.BetterScrollPane; +import jpen.PenDevice; -import javax.swing.JFileChooser; +import javax.swing.*; import java.io.File; import java.util.Locale; @@ -56,10 +41,11 @@ * * @author Alexander Winter */ -public class NormalPainterStage extends DynamicStage -{ +public class NormalPainterStage extends DynamicStage { private final NormalPainterScreen screen; + public MenuBar menuBar; + BetterSlider brushS, hardn, eqW, opacityS; SelectBox drawModeSelect; CheckBox multiply; @@ -74,21 +60,29 @@ public class NormalPainterStage extends DynamicStage CheckBox fillBlue; - TextField flatFile, maskFile, inputFile, outputFile; + String flatFile, maskFile, inputFile, outputFile; - DesktopFileSelector fileSelector = new DesktopFileSelector(); + AWTDesktopFileSelector fileSelector = new AWTDesktopFileSelector(); private final Vector3 tmpVec3 = new Vector3(); File baseDir = new File(System.getProperty("user.dir")); - public NormalPainterStage(NormalPainterScreen screen) - { - super(new FitViewport(NormalPainterScreen.STAGE_WIDTH, NormalPainterScreen.STAGE_HEIGHT), screen.batch); + public NormalPainterStage(NormalPainterScreen screen) { + super(new FitViewport(NormalPainterScreen.STAGE_WIDTH, NormalPainterScreen.STAGE_HEIGHT), + screen.batch); this.screen = screen; AssetSupplier assets = screen.assets; + VisUI.load(); + + VisUI.getSkin().get("default", VisTextButton.VisTextButtonStyle.class).font = assets.getSkin().getFont("baloo-semismall"); + VisUI.getSkin().get("default", Menu.MenuStyle.class).openButtonStyle.font = assets.getSkin().getFont("baloo-semismall"); + VisUI.getSkin().get("default", MenuItem.MenuItemStyle.class).font = assets.getSkin().getFont("baloo-semismall"); + VisUI.getSkin().get("menuitem-shortcut", Label.LabelStyle.class).font = assets.getSkin().getFont("baloo-small"); + + DynamicTable container = new DynamicTable(assets.getSkin()); container.setFillParent(true); @@ -96,7 +90,8 @@ public NormalPainterStage(NormalPainterScreen screen) container.left(); DynamicTable table = new DynamicTable(assets.getSkin()); - table.add("NormalPainter Alpha v1.6.0").left().row(); + table.add("").row(); + table.add("NormalPainter Alpha v1.6.1").left().row(); table.add("by Alexander Winter").left().row(); makeBrushSection(assets, screen.painter, table); @@ -153,27 +148,29 @@ public NormalPainterStage(NormalPainterScreen screen) pane.setCancelTouchFocus(false); pane.setScrollingDisabled(true, false); - pane.addListener(new HoverAdapter( - () -> setScrollFocus(pane), - () -> setScrollFocus(null))); + pane.addListener(new HoverAdapter(() -> setScrollFocus(pane), () -> setScrollFocus(null))); container.add(pane); addActor(container); DynamicTextField field = new DynamicTextField(() -> { - String value = Integer.toHexString(((int)(255 * screen.painter.color.r) << 16) | ((int)(255 * screen.painter.color.g) << 8) | ((int)(255 * screen.painter.color.b))); + String value = + Integer.toHexString(((int)(255 * screen.painter.color.r) << 16) | ((int)(255 * screen.painter.color.g) << 8) | ((int)(255 * screen.painter.color.b))); while(value.length() < 6) value = "0" + value; return "#" + value.toUpperCase(Locale.ROOT); }, assets.getSkin(), "default-small"); - field.setBounds(NormalPainterScreen.STAGE_WIDTH - field.getPrefWidth() - 10f, NormalPainterScreen.STAGE_HEIGHT - 275f, field.getPrefWidth(), field.getPrefHeight()); + field.setBounds(NormalPainterScreen.STAGE_WIDTH - field.getPrefWidth() - 10f, + NormalPainterScreen.STAGE_HEIGHT - 275f - 50f, + field.getPrefWidth(), + field.getPrefHeight()); addActor(field); DynamicTable savingTable = new DynamicTable(assets.getSkin()); - DynamicTextButton flush = new DynamicTextButton(() -> "Flush mask", assets.getSkin(), "default-small") { + DynamicTextButton flush = new DynamicTextButton(() -> "Flush mask", assets.getSkin(), + "default-small") { @Override - public void update() - { + public void update() { super.update(); setVisible(screen.painter.uncommittedMaskChanges); } @@ -183,206 +180,27 @@ public void update() savingTable.add(flush).colspan(3).center().row(); - savingTable.add(new DynamicLabel(() -> screen.app.unsavedChanges ? "Unsaved changes" : "", assets.getSkin())).colspan(3).center().row(); - - Table buttonWrapper = new Table(assets.getSkin()); - - TextButton newPixmap = new TextButton("New image", assets.getSkin(), "default-small"); - newPixmap.addListener(new ClickAdapter(() -> { - - Vector2 maskSize = screen.painter.mask != null ? new Vector2(screen.painter.mask.getWidth(), - screen.painter.mask.getHeight()) : null; - - Vector2 flatSize = screen.flat != null ? new Vector2(screen.flat.getRegionWidth(), - screen.flat.getRegionHeight()) : null; - - new NewPixmapDialog(assets, maskSize, flatSize, size -> newPixmap(Math.round(size.x), Math.round(size.y))).show(this); - })); - buttonWrapper.add(newPixmap).padRight(5f); - - TextButton loadTrio = new TextButton("Load Trio", assets.getSkin(), "default-small"); - loadTrio.addListener(new ClickAdapter(() -> { - fileSelector.selectFile(file -> { - Gdx.app.postRunnable(() -> { - String flatAsset = null, altFlatAsset = null; - String normalAsset = null; - String maskAsset = null; - - if(file.getName().endsWith("_f.png")) - { - flatAsset = file.getName(); - altFlatAsset = file.getName().substring(0, file.getName().length() - 6) + ".png"; - normalAsset = file.getName().substring(0, file.getName().length() - 6) + "_n.png"; - maskAsset = file.getName().substring(0, file.getName().length() - 6) + "_m.png"; - } - else if(file.getName().endsWith("_n.png")) - { - flatAsset = file.getName().substring(0, file.getName().length() - 6) + "_f.png"; - altFlatAsset = file.getName().substring(0, file.getName().length() - 6) + ".png"; - normalAsset = file.getName(); - maskAsset = file.getName().substring(0, file.getName().length() - 6) + "_m.png"; - } - else if(file.getName().endsWith("_m.png")) - { - flatAsset = file.getName().substring(0, file.getName().length() - 6) + "_f.png"; - altFlatAsset = file.getName().substring(0, file.getName().length() - 6) + ".png"; - normalAsset = file.getName().substring(0, file.getName().length() - 6) + "_n.png"; - maskAsset = file.getName(); - } - else if(file.getName().endsWith(".png")) - { - flatAsset = file.getName(); - altFlatAsset = file.getName().substring(0, file.getName().length() - 4) + "_f.png"; - normalAsset = file.getName().substring(0, file.getName().length() - 4) + "_n.png"; - maskAsset = file.getName().substring(0, file.getName().length() - 4) + "_m.png"; - } - - File flatAssetF = new File(file.getParentFile(), flatAsset); - File altAssetF = new File(file.getParentFile(), altFlatAsset); - File maskAssetF = new File(file.getParentFile(), maskAsset); - File normalAssetF = new File(file.getParentFile(), normalAsset); - - if(flatAssetF.exists()) - { - flatFile.setText(baseDir.toURI().relativize(flatAssetF.toURI()).getPath()); - loadFlat(false); - } - else if(altAssetF.exists()) - { - flatFile.setText(baseDir.toURI().relativize(altAssetF.toURI()).getPath()); - loadFlat(false); - } - else - { - flatFile.setText(""); - loadFlat(true); - } - - if(maskAssetF.exists()) - { - maskFile.setText(baseDir.toURI().relativize(maskAssetF.toURI()).getPath()); - loadMask(false); - } - else - { - maskFile.setText(""); - loadMask(true); - } - - if(normalAssetF.exists()) - { - inputFile.setText(baseDir.toURI().relativize(normalAssetF.toURI()).getPath()); - load(false); - } - else - { - inputFile.setText(""); - load(true); - } - }); - }, JFileChooser.OPEN_DIALOG); - })); - buttonWrapper.add(loadTrio); - - - savingTable.add(buttonWrapper).colspan(3).row(); - - savingTable.add("Flat", "default-small").colspan(3).left().row(); - - TextButton flatOpen = new TextButton("O", assets.getSkin(), "default-tiny"); - savingTable.add(flatOpen).width(30f); - - flatFile = new TextField("", assets.getSkin(), "default-tiny"); - savingTable.add(flatFile).growX(); - TextButton loadFlatButton = new TextButton("Load", assets.getSkin(), "default-small"); - loadFlatButton.addListener(new ClickAdapter(() -> loadFlat(false))); - savingTable.add(loadFlatButton).row(); - - flatOpen.addListener(new ClickAdapter(() -> { - fileSelector.selectFile(file -> { - Gdx.app.postRunnable(() -> { - flatFile.setText(baseDir.toURI().relativize(file.toURI()).getPath()); - loadFlat(false); - }); - }, JFileChooser.OPEN_DIALOG); - })); - - savingTable.add("Mask", "default-small").colspan(3).left().row(); - - TextButton refOpen = new TextButton("O", assets.getSkin(), "default-tiny"); - savingTable.add(refOpen).width(30f); - - maskFile = new TextField("", assets.getSkin(), "default-tiny"); - savingTable.add(maskFile).growX(); - TextButton loadRefButton = new TextButton("Load", assets.getSkin(), "default-small"); - loadRefButton.addListener(new ClickAdapter(() -> loadMask(false))); - savingTable.add(loadRefButton).row(); - - refOpen.addListener(new ClickAdapter(() -> { - fileSelector.selectFile(file -> { - Gdx.app.postRunnable(() -> { - maskFile.setText(baseDir.toURI().relativize(file.toURI()).getPath()); - loadMask(false); - }); - }, JFileChooser.OPEN_DIALOG); - })); - - savingTable.add("Input", "default-small").colspan(3).left().row(); - - TextButton inOpen = new TextButton("O", assets.getSkin(), "default-tiny"); - savingTable.add(inOpen).width(30f); - - inputFile = new TextField("", assets.getSkin(), "default-tiny"); - savingTable.add(inputFile).growX(); - TextButton loadInputButton = new TextButton("Load", assets.getSkin(), "default-small"); - loadInputButton.addListener(new ClickAdapter(() -> load(false))); - savingTable.add(loadInputButton).row(); - - inOpen.addListener(new ClickAdapter(() -> { - fileSelector.selectFile(file -> { - Gdx.app.postRunnable(() -> { - inputFile.setText(baseDir.toURI().relativize(file.toURI()).getPath()); - load(false); - }); - }, JFileChooser.OPEN_DIALOG); - })); - - savingTable.add("Output", "default-small").colspan(3).left().row(); - - TextButton outOpen = new TextButton("O", assets.getSkin(), "default-tiny"); - savingTable.add(outOpen).width(30f); - - outputFile = new TextField("", assets.getSkin(), "default-tiny"); - savingTable.add(outputFile).growX(); - TextButton saveOutputButton = new TextButton("Save", assets.getSkin(), "default-small"); - saveOutputButton.addListener(new ClickAdapter(this::save)); - savingTable.add(saveOutputButton).row(); - - outOpen.addListener(new ClickAdapter(() -> { - fileSelector.selectFile(file -> { - Gdx.app.postRunnable(() -> { - outputFile.setText(baseDir.toURI().relativize(file.toURI()).getPath()); - save(); - }); - }, JFileChooser.SAVE_DIALOG); - })); + screen.app.unsavedChanges = true; + savingTable.add(new DynamicLabel(() -> screen.app.unsavedChanges ? "Unsaved changes" : "", + assets.getSkin())).colspan(3).center().row(); savingTable.setBounds(NormalPainterScreen.STAGE_WIDTH - savingTable.getPrefWidth() - 10f, - 10f, - savingTable.getPrefWidth(), - savingTable.getPrefHeight()); + 10f, savingTable.getPrefWidth(), savingTable.getPrefHeight()); + + screen.app.unsavedChanges = false; + savingTable.update(); addListener(new TouchNothingListener(savingTable, () -> { setKeyboardFocus(null); })); addActor(savingTable); + + makeMenuBar(assets, screen); } - public void loadFlat(boolean silent) - { - if(flatFile.getText().length() <= 0) - { + public void loadFlat(boolean silent) { + if(flatFile.length() <= 0) { screen.flat = null; return; } @@ -392,49 +210,37 @@ public void loadFlat(boolean silent) mipMapParams.magFilter = TextureFilter.Nearest; mipMapParams.genMipMaps = true; - try - { - screen.flat = new TextureRegion(new Texture(new FileHandle(new File(flatFile.getText())))); - } - catch(Exception ex) - { + try { + screen.flat = new TextureRegion(new Texture(new FileHandle(new File(flatFile)))); + } catch(Exception ex) { screen.flat = null; if(!silent) - new OkayDialog(screen.assets, "Failed to load flat file", flatFile.getText()).show(this); + new OkayDialog(screen.assets, "Failed to load flat file", flatFile).show(this); } } - public void loadMask(boolean silent) - { - if(maskFile.getText().length() <= 0) - { + public void loadMask(boolean silent) { + if(maskFile.length() <= 0) { screen.painter.mask = null; return; } - try - { - screen.painter.mask = new GdxPixmap(new FileHandle(new File(maskFile.getText()))); - } - catch(Exception ex) - { + try { + screen.painter.mask = new GdxPixmap(new FileHandle(new File(maskFile))); + } catch(Exception ex) { screen.painter.mask = null; if(!silent) - new OkayDialog(screen.assets, "Failed to load mask file", maskFile.getText()).show(this); + new OkayDialog(screen.assets, "Failed to load mask file", maskFile).show(this); } } - public void load(boolean silent) - { - if(inputFile.getText().length() <= 0) - { - if(silent) - { + public void load(boolean silent) { + if(inputFile.length() <= 0) { + if(silent) { int width = 123; int height = 123; - if(screen.painter.mask != null) - { + if(screen.painter.mask != null) { width = screen.painter.mask.getWidth(); height = screen.painter.mask.getHeight(); } @@ -444,20 +250,15 @@ public void load(boolean silent) return; } - try - { - screen.painter.pixmap = new GdxPixmap(new FileHandle(new File(inputFile.getText()))); + try { + screen.painter.pixmap = new GdxPixmap(new FileHandle(new File(inputFile))); screen.painter.init(); - } - catch(Exception ex) - { - if(screen.painter.pixmap == null) - { + } catch(Exception ex) { + if(screen.painter.pixmap == null) { int width = 123; int height = 123; - if(screen.painter.mask != null) - { + if(screen.painter.mask != null) { width = screen.painter.mask.getWidth(); height = screen.painter.mask.getHeight(); } @@ -465,60 +266,247 @@ public void load(boolean silent) newPixmap(width, height); } - new OkayDialog(screen.assets, "Failed to load input file", inputFile.getText()).show(this); + new OkayDialog(screen.assets, "Failed to load input file", inputFile).show(this); } } - public void newPixmap(int width, int height) - { + public void newPixmap(int width, int height) { screen.painter.pixmap = new GdxPixmap(width, height); screen.painter.init(); } - public void save() - { - if(outputFile.getText().length() <= 0) - { + public void save() { + if(outputFile.length() <= 0) { new SaveDialog(screen.assets.getSkin(), "", fileName -> { - outputFile.setText(fileName); + outputFile = fileName; save(); }).show(this); return; } - if(!outputFile.getText().contains(".")) - outputFile.setText(outputFile.getText() + ".png"); + if(!outputFile.contains(".")) + outputFile = outputFile + ".png"; - try - { - if(screen.fillBlue) - { + try { + if(screen.fillBlue) { screen.buffer.setColor(0.5f, 0.5f, 1f, 1f); - screen.buffer.fillRectangle(0, 0, screen.buffer.getWidth(), screen.buffer.getHeight()); + screen.buffer.fillRectangle(0, 0, screen.buffer.getWidth(), + screen.buffer.getHeight()); screen.buffer.setBlending(Blending.SourceOver); screen.buffer.drawPixmap(screen.painter.preview, 0, 0); - PixmapIO.writePNG(new FileHandle(new File(outputFile.getText())), screen.buffer); - } - else - PixmapIO.writePNG(new FileHandle(new File(outputFile.getText())), screen.painter.preview); + PixmapIO.writePNG(new FileHandle(new File(outputFile)), screen.buffer); + } else + PixmapIO.writePNG(new FileHandle(new File(outputFile)), screen.painter.preview); screen.app.unsavedChanges = false; - } - catch(Exception ex) - { + } catch(Exception ex) { new OkayDialog(screen.assets, "Failed to save to output file").show(this); ex.printStackTrace(); } } - private void makeBrushSection(AssetSupplier assets, PaintComponent painter, Table table) - { + private void openNewImageDialog(AssetSupplier assets) { + Vector2 maskSize = screen.painter.mask != null ? + new Vector2(screen.painter.mask.getWidth(), screen.painter.mask.getHeight()) : + null; + + Vector2 flatSize = screen.flat != null ? new Vector2(screen.flat.getRegionWidth(), + screen.flat.getRegionHeight()) : null; + + new NewPixmapDialog(assets, maskSize, flatSize, size -> newPixmap(Math.round(size.x), + Math.round(size.y))).show(this); + } + + private void openLoadTrioDialog() { + fileSelector.selectFile(file -> { + Gdx.app.postRunnable(() -> { + String flatAsset = null, altFlatAsset = null; + String normalAsset = null; + String maskAsset = null; + + if(file.getName().endsWith("_f.png")) { + flatAsset = file.getName(); + altFlatAsset = file.getName().substring(0, file.getName().length() - 6) + + ".png"; + normalAsset = + file.getName().substring(0, file.getName().length() - 6) + "_n" + + ".png"; + maskAsset = file.getName().substring(0, file.getName().length() - 6) + "_m" + + ".png"; + } else if(file.getName().endsWith("_n.png")) { + flatAsset = file.getName().substring(0, file.getName().length() - 6) + "_f" + + ".png"; + altFlatAsset = file.getName().substring(0, file.getName().length() - 6) + + ".png"; + normalAsset = file.getName(); + maskAsset = file.getName().substring(0, file.getName().length() - 6) + "_m" + + ".png"; + } else if(file.getName().endsWith("_m.png")) { + flatAsset = file.getName().substring(0, file.getName().length() - 6) + "_f" + + ".png"; + altFlatAsset = file.getName().substring(0, file.getName().length() - 6) + + ".png"; + normalAsset = + file.getName().substring(0, file.getName().length() - 6) + "_n" + + ".png"; + maskAsset = file.getName(); + } else if(file.getName().endsWith(".png")) { + flatAsset = file.getName(); + altFlatAsset = + file.getName().substring(0, file.getName().length() - 4) + "_f" + + ".png"; + normalAsset = + file.getName().substring(0, file.getName().length() - 4) + "_n" + + ".png"; + maskAsset = file.getName().substring(0, file.getName().length() - 4) + "_m" + + ".png"; + } + + File flatAssetF = new File(file.getParentFile(), flatAsset); + File altAssetF = new File(file.getParentFile(), altFlatAsset); + File maskAssetF = new File(file.getParentFile(), maskAsset); + File normalAssetF = new File(file.getParentFile(), normalAsset); + + if(flatAssetF.exists()) { + flatFile = baseDir.toURI().relativize(flatAssetF.toURI()).getPath(); + loadFlat(false); + } else if(altAssetF.exists()) { + flatFile = baseDir.toURI().relativize(altAssetF.toURI()).getPath(); + loadFlat(false); + } else { + flatFile = ""; + loadFlat(true); + } + + if(maskAssetF.exists()) { + maskFile = baseDir.toURI().relativize(maskAssetF.toURI()).getPath(); + loadMask(false); + } else { + maskFile = ""; + loadMask(true); + } + + if(normalAssetF.exists()) { + inputFile = baseDir.toURI().relativize(normalAssetF.toURI()).getPath(); + load(false); + } else { + inputFile = ""; + load(true); + } + }); + }, JFileChooser.OPEN_DIALOG); + } + + private void openLoadFlatDialog() { + fileSelector.selectFile(file -> { + Gdx.app.postRunnable(() -> { + flatFile = baseDir.toURI().relativize(file.toURI()).getPath(); + loadFlat(false); + }); + }, JFileChooser.OPEN_DIALOG); + } + + private void openLoadMaskDialog() { + fileSelector.selectFile(file -> { + Gdx.app.postRunnable(() -> { + maskFile = baseDir.toURI().relativize(file.toURI()).getPath(); + loadMask(false); + }); + }, JFileChooser.OPEN_DIALOG); + } + + private void openLoadNormalDialog() { + fileSelector.selectFile(file -> { + Gdx.app.postRunnable(() -> { + inputFile = baseDir.toURI().relativize(file.toURI()).getPath(); + load(false); + }); + }, JFileChooser.OPEN_DIALOG); + } + + private void openSaveNormalDialog() { + fileSelector.selectFile(file -> { + Gdx.app.postRunnable(() -> { + outputFile = baseDir.toURI().relativize(file.toURI()).getPath(); + save(); + }); + }, JFileChooser.SAVE_DIALOG); + } + + private void makeMenuBar(AssetSupplier assets, NormalPainterScreen screen) { + Table wrapper = new Table(); + wrapper.setFillParent(true); + wrapper.top(); + + menuBar = new MenuBar(); + Menu fileMenu = new Menu("File"); + fileMenu.openButton.padLeft(10f).padRight(10f); + fileMenu.addItem(new MenuItem("New Normal Map", new ChangeAdapter(() -> { + openNewImageDialog(assets); + }))); + + fileMenu.addItem(new MenuItem("Load Normal Map", new ChangeAdapter(() -> { + openLoadNormalDialog(); + }))); + + fileMenu.addItem(new MenuItem("Load Flat Texture", new ChangeAdapter(() -> { + openLoadFlatDialog(); + }))); + + fileMenu.addItem(new MenuItem("Load Mask", new ChangeAdapter(() -> { + openLoadMaskDialog(); + }))); + + fileMenu.addItem(new MenuItem("Load Trio", new ChangeAdapter(() -> { + openLoadTrioDialog(); + }))); + + fileMenu.addItem(new MenuItem("Save Normal Map as", new ChangeAdapter(() -> { + openSaveNormalDialog(); + }))); + + fileMenu.addItem(new MenuItem("Save Normal Map", new ChangeAdapter(() -> { + save(); + })).setShortcut("CTRL + S")); + + menuBar.addMenu(fileMenu); + + Menu editMenu = new Menu("Edit"); + editMenu.openButton.padLeft(10f).padRight(10f); + editMenu.addItem(new MenuItem("Undo / Redo", new ChangeAdapter(() -> { + screen.painter.undo(); + })).setShortcut("CTRL + Z")); + editMenu.addItem(new MenuItem("Normalize all", new ChangeAdapter(() -> { + screen.painter.normalizeAll(); + })).setShortcut("CTRL + N")); + editMenu.addItem(new MenuItem("Blur", new ChangeAdapter(() -> { + screen.painter.blur(); + })).setShortcut("CTRL + B")); + + menuBar.addMenu(editMenu); + + Menu testMenu = new Menu("Test"); + testMenu.openButton.padLeft(10f).padRight(10f); + testMenu.setVisible(false); + testMenu.openButton.addListener(new ClickAdapter(() -> { + screen.testLighting(); + testMenu.openButton.getStyle().up = testMenu.buttonDefault; + })); + + menuBar.addMenu(testMenu); + + wrapper.add(menuBar.getTable()).growX(); + addActor(wrapper); + } + + private void makeBrushSection(AssetSupplier assets, PaintComponent painter, Table table) { DynamicTable header = new DynamicTable(assets.getSkin()); header.add("Brush", "default-small").expandX().left(); DynamicTable content = new DynamicTable(assets.getSkin()); - DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", assets.getSkin(), "default-small"); + DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", + assets.getSkin(), "default-small"); opener.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -540,34 +528,40 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu DynamicTable brushOpaContainer = new DynamicTable(); brushOpaContainer.add(opacityS); - brushOpaContainer.add(new DynamicLabel(() -> MathUtil.round(painter.brushOpacity, 2) + "", assets.getSkin(), "default-small")); + brushOpaContainer.add(new DynamicLabel(() -> MathUtil.round(painter.brushOpacity, 2) + "", + assets.getSkin(), "default-small")); content.add(brushOpaContainer).left().row(); content.add("Size: ", "default-small").left().row(); brushS = new BetterSlider(1f, 100f, 1f, false, assets.getSkin(), 150f); brushS.setValue(painter.brushSize); - brushS.addListener(new ChangeAdapter(() -> painter.brushSize = Math.round(brushS.getValue()))); + brushS.addListener(new ChangeAdapter(() -> painter.brushSize = + Math.round(brushS.getValue()))); DynamicTable brushSizeContainer = new DynamicTable(); brushSizeContainer.add(brushS); - brushSizeContainer.add(new DynamicLabel(() -> MathUtil.round(painter.brushSize, 2) + "", assets.getSkin(), "default-small")); + brushSizeContainer.add(new DynamicLabel(() -> MathUtil.round(painter.brushSize, 2) + "", + assets.getSkin(), "default-small")); content.add(brushSizeContainer).left().row(); enablePressure = new CheckBox(" Enable pressure", assets.getSkin(), "default-small"); enablePressure.setChecked(painter.enablePressure); - enablePressure.addListener(new ChangeAdapter(() -> painter.enablePressure = enablePressure.isChecked())); + enablePressure.addListener(new ChangeAdapter(() -> painter.enablePressure = + enablePressure.isChecked())); content.add(enablePressure).left().row(); content.add("Max pressure: ", "default-small").left().row(); maxPressure = new BetterSlider(0f, 1f, 0.0001f, false, assets.getSkin(), 150f); maxPressure.setValue(painter.maxPressure); - maxPressure.addListener(new ChangeAdapter(() -> painter.maxPressure = maxPressure.getValue())); + maxPressure.addListener(new ChangeAdapter(() -> painter.maxPressure = + maxPressure.getValue())); DynamicTable maxPresContainer = new DynamicTable(); maxPresContainer.add(maxPressure); - maxPresContainer.add(new DynamicLabel(() -> MathUtil.round(painter.maxPressure, 2) + "", assets.getSkin(), "default-small")); + maxPresContainer.add(new DynamicLabel(() -> MathUtil.round(painter.maxPressure, 2) + "", + assets.getSkin(), "default-small")); content.add(maxPresContainer).left().row(); @@ -578,7 +572,8 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu DynamicTable hardnContainer = new DynamicTable(); hardnContainer.add(hardn); - hardnContainer.add(new DynamicLabel(() -> MathUtil.round(painter.hardness, 2) + "", assets.getSkin(), "default-small")); + hardnContainer.add(new DynamicLabel(() -> MathUtil.round(painter.hardness, 2) + "", + assets.getSkin(), "default-small")); content.add(hardnContainer).left().row(); @@ -595,7 +590,8 @@ protected void onHide(Actor selectBoxList) { }; drawModeSelect.setItems(DrawMode.values()); drawModeSelect.setSelected(painter.drawMode); - drawModeSelect.addListener(new ChangeAdapter(() -> painter.drawMode = drawModeSelect.getSelected())); + drawModeSelect.addListener(new ChangeAdapter(() -> painter.drawMode = + drawModeSelect.getSelected())); content.add(drawModeSelect).left().row(); content.add("Blend weight: ", "default-small").left().row(); @@ -605,7 +601,8 @@ protected void onHide(Actor selectBoxList) { DynamicTable eqContainer = new DynamicTable(); eqContainer.add(eqW); - eqContainer.add(new DynamicLabel(() -> MathUtil.round(painter.equalizeWeight, 2) + "", assets.getSkin(), "default-small")); + eqContainer.add(new DynamicLabel(() -> MathUtil.round(painter.equalizeWeight, 2) + "", + assets.getSkin(), "default-small")); content.add(eqContainer).left().row(); @@ -617,15 +614,15 @@ protected void onHide(Actor selectBoxList) { table.add(content).expandX().left().row(); } - private void makeNormalSection(AssetSupplier assets, ColorComponent colorPicker, Table table) - { + private void makeNormalSection(AssetSupplier assets, ColorComponent colorPicker, Table table) { DynamicTable header = new DynamicTable(assets.getSkin()); header.add("Normal color", "default-small").expandX().left(); DynamicTable content = new DynamicTable(assets.getSkin()); - DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", assets.getSkin(), "default-small"); + DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", + assets.getSkin(), "default-small"); opener.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -642,7 +639,8 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu disaStick = new CheckBox(" Disable joystick/pen", assets.getSkin(), "default-small"); disaStick.setChecked(colorPicker.disableStickOrPen); - disaStick.addListener(new ChangeAdapter(() -> colorPicker.disableStickOrPen = disaStick.isChecked())); + disaStick.addListener(new ChangeAdapter(() -> colorPicker.disableStickOrPen = + disaStick.isChecked())); content.add(disaStick).left().row(); content.add("Max pen tilt (degrees): ", "default-small").left().row(); @@ -652,29 +650,34 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu DynamicTable maxTiltContainer = new DynamicTable(); maxTiltContainer.add(maxTiltS); - maxTiltContainer.add(new DynamicLabel(() -> colorPicker.maxTilt + "", assets.getSkin(), "default-small")); + maxTiltContainer.add(new DynamicLabel(() -> colorPicker.maxTilt + "", assets.getSkin(), + "default-small")); content.add(maxTiltContainer).left().row(); content.add("Min radius: ", "default-small").left().row(); - minRad = new BetterSlider(0f, 1f, Math.max(colorPicker.radiusStep, 1f / 255f), false, assets.getSkin(), 150f); + minRad = new BetterSlider(0f, 1f, Math.max(colorPicker.radiusStep, 1f / 255f), false, + assets.getSkin(), 150f); minRad.setValue(colorPicker.minRadius); minRad.addListener(new ChangeAdapter(() -> colorPicker.minRadius = minRad.getValue())); DynamicTable minRadContainer = new DynamicTable(); minRadContainer.add(minRad); - minRadContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.minRadius, 2) + "", assets.getSkin(), "default-small")); + minRadContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.minRadius, 2) + "", + assets.getSkin(), "default-small")); content.add(minRadContainer).left().row(); content.add("Max radius: ", "default-small").left().row(); - maxRad = new BetterSlider(0f, 1f, Math.max(colorPicker.radiusStep, 1f / 255f), false, assets.getSkin(), 150f); + maxRad = new BetterSlider(0f, 1f, Math.max(colorPicker.radiusStep, 1f / 255f), false, + assets.getSkin(), 150f); maxRad.setValue(colorPicker.maxRadius); maxRad.addListener(new ChangeAdapter(() -> colorPicker.maxRadius = maxRad.getValue())); DynamicTable maxRadContainer = new DynamicTable(); maxRadContainer.add(maxRad); - maxRadContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.maxRadius, 2) + "", assets.getSkin(), "default-small")); + maxRadContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.maxRadius, 2) + "", + assets.getSkin(), "default-small")); content.add(maxRadContainer).left().row(); @@ -689,14 +692,16 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu DynamicTable radStepContainer = new DynamicTable(); radStepContainer.add(radStep); - radStepContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.radiusStep, 2) + "", assets.getSkin(), "default-small")); + radStepContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.radiusStep, 2) + "" + , assets.getSkin(), "default-small")); content.add(radStepContainer).left().row(); content.add(new DynamicLabel(() -> { screen.painter.tmpColor.set(Color.rgba8888(screen.painter.color)); - tmpVec3.set(screen.painter.tmpColor.r, screen.painter.tmpColor.g, screen.painter.tmpColor.b).scl(2f).sub(1f); + tmpVec3.set(screen.painter.tmpColor.r, screen.painter.tmpColor.g, + screen.painter.tmpColor.b).scl(2f).sub(1f); float quality = tmpVec3.len(); return "Normal error: " + MathUtil.round(Math.abs(1f - quality), 5); @@ -705,20 +710,25 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.add("Normal rotation speed: ", "default-small").left().row(); rotSpeed = new BetterSlider(0f, 10f, 0.0001f, false, assets.getSkin(), 150f); rotSpeed.setValue(colorPicker.normalRotateSpeed); - rotSpeed.addListener(new ChangeAdapter(() -> colorPicker.normalRotateSpeed = rotSpeed.getValue())); + rotSpeed.addListener(new ChangeAdapter(() -> colorPicker.normalRotateSpeed = + rotSpeed.getValue())); DynamicTable rotSpeedContainer = new DynamicTable(); rotSpeedContainer.add(rotSpeed); - rotSpeedContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.normalRotateSpeed, 2) + "", assets.getSkin(), "default-small")); + rotSpeedContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.normalRotateSpeed, + 2) + "", assets.getSkin(), "default-small")); content.add(rotSpeedContainer).left().row(); - invertPinning = new CheckBox(" Invert normal pin direction (I)", assets.getSkin(), "default-small"); + invertPinning = new CheckBox(" Invert normal pin direction (I)", assets.getSkin(), + "default-small"); invertPinning.setChecked(colorPicker.invertPinning); - invertPinning.addListener(new ChangeAdapter(() -> colorPicker.invertPinning = invertPinning.isChecked())); + invertPinning.addListener(new ChangeAdapter(() -> colorPicker.invertPinning = + invertPinning.isChecked())); content.add(invertPinning).left().row(); - normalRelToPath = new CheckBox(" Normals from path (WIP)", assets.getSkin(), "default-small"); + normalRelToPath = new CheckBox(" Normals from path (WIP)", assets.getSkin(), "default" + + "-small"); normalRelToPath.setChecked(colorPicker.normalRelToPath); normalRelToPath.addListener(new ChangeAdapter(() -> { colorPicker.normalRelToPath = normalRelToPath.isChecked(); @@ -730,22 +740,26 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.add("Angle from path (CCW): ", "default-small").left().row(); angleFromPath = new BetterSlider(-180f, 180f, 0.1f, false, assets.getSkin(), 150f); angleFromPath.setValue(colorPicker.angle); - angleFromPath.addListener(new ChangeAdapter(() -> colorPicker.angle = angleFromPath.getValue())); + angleFromPath.addListener(new ChangeAdapter(() -> colorPicker.angle = + angleFromPath.getValue())); DynamicTable angleContainer = new DynamicTable(); angleContainer.add(angleFromPath); - angleContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.angle, 2) + "", assets.getSkin(), "default-small")); + angleContainer.add(new DynamicLabel(() -> MathUtil.round(colorPicker.angle, 2) + "", + assets.getSkin(), "default-small")); content.add(angleContainer).left().row(); content.add("Numpad color intensity: ", "default-small").left().row(); numpadIntensity = new BetterSlider(0f, 1f, 0.0001f, false, assets.getSkin(), 150f); numpadIntensity.setValue(colorPicker.numpadIntensity); - numpadIntensity.addListener(new ChangeAdapter(() -> colorPicker.numpadIntensity = numpadIntensity.getValue())); + numpadIntensity.addListener(new ChangeAdapter(() -> colorPicker.numpadIntensity = + numpadIntensity.getValue())); DynamicTable numContainer = new DynamicTable(); numContainer.add(numpadIntensity); - numContainer.add(new DynamicLabel(() -> colorPicker.numpadIntensity + "", assets.getSkin(), "default-small")); + numContainer.add(new DynamicLabel(() -> colorPicker.numpadIntensity + "", assets.getSkin() + , "default-small")); content.add(numContainer).left().row(); @@ -757,15 +771,15 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu table.add(content).expandX().left().row(); } - private void makeDisplaySection(AssetSupplier assets, Table table) - { + private void makeDisplaySection(AssetSupplier assets, Table table) { DynamicTable header = new DynamicTable(assets.getSkin()); header.add("Display", "default-small").expandX().left(); DynamicTable content = new DynamicTable(assets.getSkin()); - DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", assets.getSkin(), "default-small"); + DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", + assets.getSkin(), "default-small"); opener.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -788,7 +802,8 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.add("Mask opacity: ", "default-small").padLeft(20f).left().row(); maskOpacity = new BetterSlider(0f, 1f, 1f / 255f, false, assets.getSkin(), 150f); maskOpacity.setValue(screen.maskOpacity); - maskOpacity.addListener(new ChangeAdapter(() -> screen.maskOpacity = maskOpacity.getValue())); + maskOpacity.addListener(new ChangeAdapter(() -> screen.maskOpacity = + maskOpacity.getValue())); content.add(maskOpacity).left().row(); showFlat = new CheckBox(" Show flat texture (T)", assets.getSkin(), "default-small"); @@ -804,7 +819,8 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.add("Flat opacity: ", "default-small").padLeft(20f).left().row(); flatOpacity = new BetterSlider(0f, 1f, 1f / 255f, false, assets.getSkin(), 150f); flatOpacity.setValue(screen.flatOpacity); - flatOpacity.addListener(new ChangeAdapter(() -> screen.flatOpacity = flatOpacity.getValue())); + flatOpacity.addListener(new ChangeAdapter(() -> screen.flatOpacity = + flatOpacity.getValue())); content.add(flatOpacity).left().row(); normV = new CheckBox(" View normals", assets.getSkin(), "default-small"); @@ -815,7 +831,8 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.add("Normal view spacing: ", "default-small").padLeft(20f).left().row(); viewSpacing = new BetterSlider(1f, 50f, 1f, false, assets.getSkin(), 150f); viewSpacing.setValue(screen.normalSpacing); - viewSpacing.addListener(new ChangeAdapter(() -> screen.normalSpacing = Math.round(viewSpacing.getValue()))); + viewSpacing.addListener(new ChangeAdapter(() -> screen.normalSpacing = + Math.round(viewSpacing.getValue()))); content.add(viewSpacing).left().row(); livePrev = new CheckBox(" Live preview", assets.getSkin(), "default-small"); @@ -825,21 +842,22 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu drawLines = new CheckBox(" Draw lines on drag", assets.getSkin(), "default-small"); drawLines.setChecked(screen.drawLinesOnDrag); - drawLines.addListener(new ChangeAdapter(() -> screen.drawLinesOnDrag = drawLines.isChecked())); + drawLines.addListener(new ChangeAdapter(() -> screen.drawLinesOnDrag = + drawLines.isChecked())); content.add(drawLines).left().row(); table.add(content).expandX().left().row(); } - private void makeExportSection(AssetSupplier assets, Table table) - { + private void makeExportSection(AssetSupplier assets, Table table) { DynamicTable header = new DynamicTable(assets.getSkin()); header.add("Export", "default-small").expandX().left(); DynamicTable content = new DynamicTable(assets.getSkin()); - DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", assets.getSkin(), "default-small"); + DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", + assets.getSkin(), "default-small"); opener.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -862,15 +880,15 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu table.add(content).expandX().left().row(); } - private void makeDeviceSection(AssetSupplier assets, Table table) - { + private void makeDeviceSection(AssetSupplier assets, Table table) { DynamicTable header = new DynamicTable(assets.getSkin()); header.add("Devices", "default-small").expandX().left(); DynamicTable content = new DynamicTable(assets.getSkin()); - DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", assets.getSkin(), "default-small"); + DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", + assets.getSkin(), "default-small"); opener.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -885,19 +903,18 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.padLeft(20f); - for(Controller controller : Controllers.getControllers()) - { + for(Controller controller : Controllers.getControllers()) { Label label = new Label(controller.getName(), assets.getSkin(), "default-tiny"); content.add(label).left().row(); } - for(PenDevice pen : screen.colorPicker.jPenWrapper.getDevices()) - { + for(PenDevice pen : screen.colorPicker.jPenWrapper.getDevices()) { Label label = new Label(pen.getName(), assets.getSkin(), "default-tiny"); content.add(label).left().row(); } - TextButton configure = new TextButton("Configure Joystick", assets.getSkin(), "default-small"); + TextButton configure = new TextButton("Configure Joystick", assets.getSkin(), "default" + + "-small"); configure.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -911,15 +928,15 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu table.add(content).expandX().left().row(); } - private void makeHelpSection(AssetSupplier assets, Table table) - { + private void makeHelpSection(AssetSupplier assets, Table table) { DynamicTable header = new DynamicTable(assets.getSkin()); header.add("Shortcuts", "default-small").expandX().left(); DynamicTable content = new DynamicTable(assets.getSkin()); - DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", assets.getSkin(), "default-small"); + DynamicTextButton opener = new DynamicTextButton(() -> content.isVisible() ? "-" : "+", + assets.getSkin(), "default-small"); opener.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { @@ -934,8 +951,6 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.padLeft(20f); - content.add("CTRL + S saves", "default-tiny").left().row(); - content.add("CTRL + Z undos/redos", "default-tiny").left().row(); content.add("F5 tests lighting", "default-tiny").left().row(); content.add("Scroll zooms", "default-tiny").left().row(); content.add("Scroll + CTRL sizes brush", "default-tiny").left().row(); @@ -945,8 +960,6 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu content.add("SHIFT + Click copies hovered color", "default-tiny").left().row(); content.add("Numpad sets basic normal", "default-tiny").left().row(); content.add("L locks current radius", "default-tiny").left().row(); - content.add("CTRL + N normalizes all colors", "default-tiny").left().row(); - content.add("CTRL + B blurs all normals", "default-tiny").left().row(); content.add("Delete/Backspace/E sets eraser", "default-tiny").left().row(); content.add("B sets mode to behind", "default-tiny").left().row(); content.add("R sets mode to normal", "default-tiny").left().row(); @@ -958,5 +971,11 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu table.add(content).expandX().left().row(); } + + @Override + public void dispose() { + super.dispose(); + VisUI.dispose(); + } } diff --git a/painter/src/main/java/com/normalpainter/app/PaintComponent.java b/painter/src/main/java/com/normalpainter/app/PaintComponent.java index eed645c..7517a0c 100644 --- a/painter/src/main/java/com/normalpainter/app/PaintComponent.java +++ b/painter/src/main/java/com/normalpainter/app/PaintComponent.java @@ -12,11 +12,9 @@ import com.normalpainter.app.jpun.JPenListener; import static com.badlogic.gdx.math.MathUtils.clamp; -import static java.lang.Math.abs; -import static java.lang.Math.max; -import static java.lang.Math.min; import static com.normalpainter.app.NormalPainterScreen.WORLD_PERFECT_SCALE; import static com.normalpainter.util.Validation.ensureNotNull; +import static java.lang.Math.*; /** * Part of the software responsible for painting the pixmap @@ -25,13 +23,9 @@ * * @author Alexander Winter */ -public class PaintComponent implements JPenListener -{ - public static final float[][] GAUSSIAN_BLUR_3_3_KERNEL = new float[][] { - {1f / 16f, 1f / 8f, 1f / 16f}, - {1f / 8f, 1f / 4f, 1f / 8f}, - {1f / 16f, 1f / 8f, 1f / 16f}, - }; +public class PaintComponent implements JPenListener { + public static final float[][] GAUSSIAN_BLUR_3_3_KERNEL = new float[][] { { 1f / 16f, 1f / 8f, + 1f / 16f }, { 1f / 8f, 1f / 4f, 1f / 8f }, { 1f / 16f, 1f / 8f, 1f / 16f }, }; public NormalPainterScreen screen; @@ -61,21 +55,20 @@ public class PaintComponent implements JPenListener private int startX = -1, startY = -1, endX = -1, endY = -1; - public final Color color = new Color(), otherColor = new Color(), tmpColor = new Color(), tmpColor2 = new Color(), bufferColor = new Color(); + public final Color color = new Color(), otherColor = new Color(), tmpColor = new Color(), + tmpColor2 = new Color(), bufferColor = new Color(); private final Vector3 tmpVec3 = new Vector3(); private final Vector2 tmpLine = new Vector2(); private final Vector2 tmpLine2 = new Vector2(); - public PaintComponent(NormalPainterScreen screen) - { + public PaintComponent(NormalPainterScreen screen) { ensureNotNull(screen, "screen"); this.screen = screen; } - public boolean fillLine(int prevPixX, int prevPixY, int pixelX, int pixelY) - { + public boolean fillLine(int prevPixX, int prevPixY, int pixelX, int pixelY) { int radius = Math.round((enablePressure ? pressure : 1f) * brushSize); int minX = min(prevPixX, pixelX); @@ -83,10 +76,7 @@ public boolean fillLine(int prevPixX, int prevPixY, int pixelX, int pixelY) int minY = min(prevPixY, pixelY); int maxY = max(prevPixY, pixelY); - if(minX - radius + 1 >= pixmap.getWidth() - || minY - radius + 1 >= pixmap.getHeight() - || maxX + radius <= 0 - || maxY + radius <= 0) + if(minX - radius + 1 >= pixmap.getWidth() || minY - radius + 1 >= pixmap.getHeight() || maxX + radius <= 0 || maxY + radius <= 0) return false; tmpLine.set(pixelX - prevPixX, pixelY - prevPixY); @@ -99,8 +89,7 @@ public boolean fillLine(int prevPixX, int prevPixY, int pixelX, int pixelY) tmpLine.scl(1f / lineLen); for(int x = minX - radius + 1; x < maxX + radius; x++) - for(int y = minY - radius + 1; y < maxY + radius; y++) - { + for(int y = minY - radius + 1; y < maxY + radius; y++) { tmpLine2.set(x - prevPixX, y - prevPixY); float dot = tmpLine.dot(tmpLine2); @@ -111,8 +100,7 @@ public boolean fillLine(int prevPixX, int prevPixY, int pixelX, int pixelY) rad = tmpLine2.len(); else if(dot > lineLen) rad = tmpLine2.set(x - pixelX, y - pixelY).len(); - else - { + else { tmpLine2.mulAdd(tmpLine, -dot); rad = tmpLine2.len(); } @@ -128,15 +116,12 @@ else if(dot > lineLen) screen.colorPicker.mouseMoved(Gdx.input.getX(), Gdx.input.getY()); - if(startX < 0) - { + if(startX < 0) { startX = max(0, minX - radius + 1); startY = max(0, minY - radius + 1); endX = min(pixmap.getWidth(), maxX + radius); endY = min(pixmap.getHeight(), maxY + radius); - } - else - { + } else { if(minX - radius + 1 < startX) startX = max(0, minX - radius + 1); @@ -153,18 +138,12 @@ else if(dot > lineLen) return true; } - private boolean _fillCircle(int pixelX, int pixelY, int radius) - { - if(pixelX - radius + 1 >= pixmap.getWidth() - || pixelY - radius + 1 >= pixmap.getHeight() - || pixelX + radius <= 0 - || pixelY + radius <= 0) + private boolean _fillCircle(int pixelX, int pixelY, int radius) { + if(pixelX - radius + 1 >= pixmap.getWidth() || pixelY - radius + 1 >= pixmap.getHeight() || pixelX + radius <= 0 || pixelY + radius <= 0) return false; - for(int i = -radius + 1; i < radius; i++) - { - for(int j = -radius + 1; j < radius; j++) - { + for(int i = -radius + 1; i < radius; i++) { + for(int j = -radius + 1; j < radius; j++) { int r2 = i * i + j * j; if(r2 > radius * radius) continue; @@ -180,8 +159,7 @@ private boolean _fillCircle(int pixelX, int pixelY, int radius) return true; } - public boolean fillCircle(int pixelX, int pixelY) - { + public boolean fillCircle(int pixelX, int pixelY) { int radius = Math.round(pressure * brushSize); if(!_fillCircle(pixelX, pixelY, radius)) @@ -190,15 +168,12 @@ public boolean fillCircle(int pixelX, int pixelY) if(screen.colorPicker.pinned || screen.colorPicker.normalRelToPath) screen.colorPicker.mouseMoved(Gdx.input.getX(), Gdx.input.getY()); - if(startX < 0) - { + if(startX < 0) { startX = max(0, pixelX - radius + 1); startY = max(0, pixelY - radius + 1); endX = min(pixmap.getWidth(), pixelX + radius); endY = min(pixmap.getHeight(), pixelY + radius); - } - else - { + } else { if(pixelX - radius + 1 < startX) startX = max(0, pixelX - radius + 1); @@ -215,26 +190,24 @@ public boolean fillCircle(int pixelX, int pixelY) return true; } - private void drawPixel(int x, int y, float rad, int brushSize) - { + private void drawPixel(int x, int y, float rad, int brushSize) { if(maskMultiply) uncommittedMaskChanges = true; float hardRad = hardness * brushSize; - float opacity = brushSize == hardRad ? 1f : clamp((brushSize - rad) / (brushSize - hardRad), 0f, 1f); + float opacity = brushSize == hardRad ? 1f : + clamp((brushSize - rad) / (brushSize - hardRad), 0f, 1f); bufferColor.set(brushBuffer.getPixel(x, y)); - if(screen.colorPicker.pinned || screen.colorPicker.normalRelToPath) - { + if(screen.colorPicker.pinned || screen.colorPicker.normalRelToPath) { float worldX = (x - pixmap.getWidth() / 2f) * WORLD_PERFECT_SCALE; float worldY = -(y - pixmap.getHeight() / 2f) * WORLD_PERFECT_SCALE; tmpVec3.set(worldX, worldY, 0f); - screen.camera.project(tmpVec3, - screen.stage.getViewport().getScreenX(), + screen.camera.project(tmpVec3, screen.stage.getViewport().getScreenX(), screen.stage.getViewport().getScreenY(), screen.stage.getViewport().getScreenWidth(), screen.stage.getViewport().getScreenHeight()); @@ -258,42 +231,35 @@ private void drawPixel(int x, int y, float rad, int brushSize) brushBuffer.drawPixel(x, y); } - public void updatePreview(boolean clean) - { + public void updatePreview(boolean clean) { if(clean) preview.fullRange(); if(clean || startX == -1) - preview.drawPixmap(pixmap, - preview.getMinX(), preview.getMinY(), - preview.getMinX(), preview.getMinY(), - preview.getMaxX() - preview.getMinX(), preview.getMaxY() - preview.getMinY()); + preview.drawPixmap(pixmap, preview.getMinX(), preview.getMinY(), preview.getMinX(), + preview.getMinY(), preview.getMaxX() - preview.getMinX(), + preview.getMaxY() - preview.getMinY()); else - preview.drawPixmap(pixmap, startX, startY, startX, startY, endX - startX, endY - startY); + preview.drawPixmap(pixmap, startX, startY, startX, startY, endX - startX, + endY - startY); preview.initRange(); - if(maskMultiply) - { - if(clean || startX < 0) - { + if(maskMultiply) { + if(clean || startX < 0) { maskPreview.copy(maskBuffer); renderBuffer(maskPreview); - } - else - { - maskPreview.drawPixmap(maskBuffer, startX, startY, startX, startY, endX - startX, endY - startY); + } else { + maskPreview.drawPixmap(maskBuffer, startX, startY, startX, startY, endX - startX, + endY - startY); renderBuffer(maskPreview, startX, startY, endX, endY); } - } - else - { + } else { if(clean || startX < 0) renderBuffer(preview); else renderBuffer(preview, startX, startY, endX, endY); } - if(uncommittedMaskChanges) - { + if(uncommittedMaskChanges) { if(clean || startX < 0) flushMaskBuffer(maskPreview, preview, false); else @@ -306,11 +272,9 @@ public void updatePreview(boolean clean) endY = -1; } - public void normalizeAll() - { + public void normalizeAll() { for(int x = 0; x < pixmap.getWidth(); x++) - for(int y = 0; y < pixmap.getHeight(); y++) - { + for(int y = 0; y < pixmap.getHeight(); y++) { otherColor.set(pixmap.getPixel(x, y)); normalize(otherColor); @@ -322,14 +286,12 @@ public void normalizeAll() updatePreview(true); } - public void blur() - { + public void blur() { flushMask(); backup.copy(pixmap); for(int x = 0; x < pixmap.getWidth(); x++) - for(int y = 0; y < pixmap.getHeight(); y++) - { + for(int y = 0; y < pixmap.getHeight(); y++) { getBlurredColor(backup, x, y, GAUSSIAN_BLUR_3_3_KERNEL, otherColor); pixmap.setColor(otherColor); pixmap.drawPixel(x, y); @@ -338,7 +300,8 @@ public void blur() updatePreview(true); } - private void getBlurredColor(GdxPixmap pixmap, int centerX, int centerY, float[][] kernel, Color out) { + private void getBlurredColor(GdxPixmap pixmap, int centerX, int centerY, float[][] kernel, + Color out) { int kernelSize = kernel.length; @@ -374,23 +337,20 @@ private void getBlurredColor(GdxPixmap pixmap, int centerX, int centerY, float[] } - private void renderBuffer(PixmapBuffer pixmap) - { - renderBuffer(pixmap, brushBuffer.getMinX(), brushBuffer.getMinY(), brushBuffer.getMaxX(), brushBuffer.getMaxY()); + private void renderBuffer(PixmapBuffer pixmap) { + renderBuffer(pixmap, brushBuffer.getMinX(), brushBuffer.getMinY(), brushBuffer.getMaxX(), + brushBuffer.getMaxY()); } - private void renderBuffer(PixmapBuffer pixmap, int startX, int startY, int endX, int endY) - { + private void renderBuffer(PixmapBuffer pixmap, int startX, int startY, int endX, int endY) { for(int x = startX; x < endX; x++) - for(int y = startY; y < endY; y++) - { + for(int y = startY; y < endY; y++) { bufferColor.set(brushBuffer.getPixel(x, y)); if(bufferColor.a == 0f) continue; - if(drawMode == DrawMode.Normal) - { + if(drawMode == DrawMode.Normal) { otherColor.set(pixmap.getPixel(x, y)); float eqW = 1f - (1f - equalizeWeight) * otherColor.a; @@ -414,9 +374,7 @@ private void renderBuffer(PixmapBuffer pixmap, int startX, int startY, int endX, pixmap.setColor(otherColor); pixmap.drawPixel(x, y); - } - else if(drawMode == DrawMode.Erase) - { + } else if(drawMode == DrawMode.Erase) { otherColor.set(pixmap.getPixel(x, y)); float multAlpha = bufferColor.a * brushOpacity; @@ -424,9 +382,7 @@ else if(drawMode == DrawMode.Erase) otherColor.a *= 1f - multAlpha; pixmap.setColor(otherColor); pixmap.drawPixel(x, y); - } - else if(drawMode == DrawMode.Behind) - { + } else if(drawMode == DrawMode.Behind) { otherColor.set(pixmap.getPixel(x, y)); float r = bufferColor.r; @@ -453,8 +409,7 @@ else if(drawMode == DrawMode.Behind) } - public void endDraw() - { + public void endDraw() { if(!screen.livePreview) updatePreview(false); @@ -465,22 +420,20 @@ public void endDraw() brushBuffer.clear(); } - public void flushMask() - { + public void flushMask() { flushMaskBuffer(maskBuffer, pixmap, true); uncommittedMaskChanges = false; } - public void flushMaskBuffer(RangedGdxBuffer buffer, Pixmap destination, boolean clear) - { - flushMaskBuffer(buffer, destination, clear, buffer.getMinX(), buffer.getMinY(), buffer.getMaxX(), buffer.getMaxY()); + public void flushMaskBuffer(RangedGdxBuffer buffer, Pixmap destination, boolean clear) { + flushMaskBuffer(buffer, destination, clear, buffer.getMinX(), buffer.getMinY(), + buffer.getMaxX(), buffer.getMaxY()); } - public void flushMaskBuffer(RangedGdxBuffer buffer, Pixmap destination, boolean clear, int startX, int startY, int endX, int endY) - { + public void flushMaskBuffer(RangedGdxBuffer buffer, Pixmap destination, boolean clear, + int startX, int startY, int endX, int endY) { for(int x = startX; x < endX; x++) - for(int y = startY; y < endY; y++) - { + for(int y = startY; y < endY; y++) { bufferColor.set(buffer.getPixel(x, y)); if(bufferColor.a == 0f) @@ -495,9 +448,12 @@ public void flushMaskBuffer(RangedGdxBuffer buffer, Pixmap destination, boolean otherColor.set(destination.getPixel(x, y)); - bufferColor.r = bufferColor.r * bufferColor.a + otherColor.r * otherColor.a * (1f - bufferColor.a); - bufferColor.g = bufferColor.g * bufferColor.a + otherColor.g * otherColor.a * (1f - bufferColor.a); - bufferColor.b = bufferColor.b * bufferColor.a + otherColor.b * otherColor.a * (1f - bufferColor.a); + bufferColor.r = + bufferColor.r * bufferColor.a + otherColor.r * otherColor.a * (1f - bufferColor.a); + bufferColor.g = + bufferColor.g * bufferColor.a + otherColor.g * otherColor.a * (1f - bufferColor.a); + bufferColor.b = + bufferColor.b * bufferColor.a + otherColor.b * otherColor.a * (1f - bufferColor.a); bufferColor.a = bufferColor.a + otherColor.a * (1f - bufferColor.a); bufferColor.r /= bufferColor.a; @@ -514,8 +470,7 @@ public void flushMaskBuffer(RangedGdxBuffer buffer, Pixmap destination, boolean buffer.clear(); } - private void normalize(Color color) - { + private void normalize(Color color) { tmpVec3.set((int)(color.r * 255), (int)(color.g * 255), (int)(color.b * 255)); tmpVec3.scl(1f / 255f); tmpVec3.scl(2f).sub(1f); @@ -532,9 +487,9 @@ private void normalize(Color color) color.b = tmpVec3.z; } - public void init() - { - screen.buffer = new GdxPixmap(screen.painter.pixmap.getWidth(), screen.painter.pixmap.getHeight()); + public void init() { + screen.buffer = new GdxPixmap(screen.painter.pixmap.getWidth(), + screen.painter.pixmap.getHeight()); backup = new GdxPixmap(pixmap.getWidth(), pixmap.getHeight()); brushBuffer = new RangedGdxBuffer(pixmap.getWidth(), pixmap.getHeight()); preview = new RangedGdxBuffer(pixmap.getWidth(), pixmap.getHeight()); @@ -550,11 +505,26 @@ public void init() } @Override - public void pressureChanged(float pressure) - { + public void pressureChanged(float pressure) { this.pressure = pressure / maxPressure; } @Override public void tiltChanged(float x, float y) {} + + public void undo() { + if(maskMultiply) { + RangedGdxBuffer tmp = maskBuffer; + maskBuffer = maskBufferBackup; + updatePreview(true); + + maskBufferBackup = tmp; + } else { + GdxPixmap tmp = pixmap; + pixmap = backup; + updatePreview(true); + + backup = tmp; + } + } } diff --git a/painter/src/main/java/com/normalpainter/app/dialog/AWTDesktopFileSelector.java b/painter/src/main/java/com/normalpainter/app/dialog/AWTDesktopFileSelector.java new file mode 100644 index 0000000..0d68654 --- /dev/null +++ b/painter/src/main/java/com/normalpainter/app/dialog/AWTDesktopFileSelector.java @@ -0,0 +1,35 @@ +package com.normalpainter.app.dialog; + +import java.awt.*; +import java.io.File; +import java.util.function.Consumer; + +/** + * AWT implementation of FileSelector + *

+ * Created on 2023-10-11. + * + * @author Alexander Winter + */ +public class AWTDesktopFileSelector +{ + private File prevDir = null; + + public void selectFile(Consumer callback, int dialogType) + { + FileDialog dialog = new FileDialog((Frame)null); + if(prevDir != null) + dialog.setDirectory(prevDir.getAbsolutePath()); + dialog.setMode(dialogType); + dialog.setMultipleMode(false); + dialog.setVisible(true); + + if(dialog.getFiles().length != 1) + return; + + File file = dialog.getFiles()[0]; + + prevDir = file.getParentFile().getAbsoluteFile(); + callback.accept(file); + } +} diff --git a/painter/src/main/java/com/normalpainter/app/dialog/DesktopFileSelector.java b/painter/src/main/java/com/normalpainter/app/dialog/SwingDesktopFileSelector.java similarity index 97% rename from painter/src/main/java/com/normalpainter/app/dialog/DesktopFileSelector.java rename to painter/src/main/java/com/normalpainter/app/dialog/SwingDesktopFileSelector.java index 59e9c4b..53d681d 100644 --- a/painter/src/main/java/com/normalpainter/app/dialog/DesktopFileSelector.java +++ b/painter/src/main/java/com/normalpainter/app/dialog/SwingDesktopFileSelector.java @@ -16,7 +16,7 @@ * * @author Alexander Winter */ -public class DesktopFileSelector +public class SwingDesktopFileSelector { private final Array currentChoosers = new Array<>(); diff --git a/painter/src/main/java/com/normalpainter/app/lighttester/LightTesterScreen.java b/painter/src/main/java/com/normalpainter/app/lighttester/LightTesterScreen.java index 051fc98..0729158 100644 --- a/painter/src/main/java/com/normalpainter/app/lighttester/LightTesterScreen.java +++ b/painter/src/main/java/com/normalpainter/app/lighttester/LightTesterScreen.java @@ -399,7 +399,7 @@ public void resume() {} public void dispose() {} @Override - public boolean scrolled(int amount) + public boolean scrolled(float amountX, float amount) { float zoomGain; if(!(Gdx.input.isKeyPressed(Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Keys.CONTROL_RIGHT))) diff --git a/painter/src/main/java/com/normalpainter/util/gdx/Scene2dUtil.java b/painter/src/main/java/com/normalpainter/util/gdx/Scene2dUtil.java index 9c0dadf..f8b0c91 100644 --- a/painter/src/main/java/com/normalpainter/util/gdx/Scene2dUtil.java +++ b/painter/src/main/java/com/normalpainter/util/gdx/Scene2dUtil.java @@ -24,6 +24,9 @@ import com.badlogic.gdx.utils.Pools; import com.badlogic.gdx.utils.Predicate; import com.badlogic.gdx.utils.SnapshotArray; +import com.kotcrab.vis.ui.widget.Menu; +import com.kotcrab.vis.ui.widget.MenuBar; +import com.kotcrab.vis.ui.widget.MenuItem; import com.normalpainter.util.ui.drawable.TintedDrawable; import com.normalpainter.util.ReflectionUtil;