From 5385876a8a5cb212bcc708a90349fe5ce1fcd220 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sat, 20 Jun 2026 15:03:40 +0800 Subject: [PATCH] feat: pbr (#20) * feat: add pbr texture * feat(rendering): add normal mapping support for blocks * fix: normal map load * feat(scripts): add batch nearest neighbor upscale script --- .gitignore | 3 +- assets/normal/block/dirt/back_n.png | Bin 0 -> 3393 bytes assets/normal/block/dirt/base_n.png | Bin 0 -> 3393 bytes assets/normal/block/dirt/front_n.png | Bin 0 -> 3393 bytes assets/normal/block/dirt/left_n.png | Bin 0 -> 3393 bytes assets/normal/block/dirt/right_n.png | Bin 0 -> 3393 bytes assets/normal/block/dirt/top_n.png | Bin 0 -> 3393 bytes assets/normal/block/grass_block/back_n.png | Bin 0 -> 4098 bytes assets/normal/block/grass_block/base_n.png | Bin 0 -> 3393 bytes assets/normal/block/grass_block/front_n.png | Bin 0 -> 4098 bytes assets/normal/block/grass_block/left_n.png | Bin 0 -> 4098 bytes assets/normal/block/grass_block/right_n.png | Bin 0 -> 4098 bytes assets/normal/block/grass_block/top_n.png | Bin 0 -> 2780 bytes assets/normal/block/log/back_n.png | Bin 0 -> 1492 bytes assets/normal/block/log/base_n.png | Bin 0 -> 955 bytes assets/normal/block/log/front_n.png | Bin 0 -> 1492 bytes assets/normal/block/log/left_n.png | Bin 0 -> 1492 bytes assets/normal/block/log/right_n.png | Bin 0 -> 1492 bytes assets/normal/block/log/top_n.png | Bin 0 -> 955 bytes assets/normal/block/sand/back_n.png | Bin 0 -> 2755 bytes assets/normal/block/sand/base_n.png | Bin 0 -> 2755 bytes assets/normal/block/sand/front_n.png | Bin 0 -> 2755 bytes assets/normal/block/sand/left_n.png | Bin 0 -> 2755 bytes assets/normal/block/sand/right_n.png | Bin 0 -> 2755 bytes assets/normal/block/sand/top_n.png | Bin 0 -> 2755 bytes .../normal/block/snowy_grass_block/back_n.png | Bin 0 -> 4113 bytes .../normal/block/snowy_grass_block/base_n.png | Bin 0 -> 3393 bytes .../block/snowy_grass_block/front_n.png | Bin 0 -> 4113 bytes .../normal/block/snowy_grass_block/left_n.png | Bin 0 -> 4113 bytes .../block/snowy_grass_block/right_n.png | Bin 0 -> 4113 bytes .../normal/block/snowy_grass_block/top_n.png | Bin 0 -> 2978 bytes assets/normal/block/stone/back_n.png | Bin 0 -> 3166 bytes assets/normal/block/stone/base_n.png | Bin 0 -> 3166 bytes assets/normal/block/stone/front_n.png | Bin 0 -> 3166 bytes assets/normal/block/stone/left_n.png | Bin 0 -> 3166 bytes assets/normal/block/stone/right_n.png | Bin 0 -> 3166 bytes assets/normal/block/stone/top_n.png | Bin 0 -> 3166 bytes assets/shaders/block_f_shader.glsl | 33 +++- assets/shaders/block_v_shader.glsl | 9 +- include/Cubed/primitive_data.hpp | 61 ++++++++ include/Cubed/renderer.hpp | 4 + include/Cubed/texture_manager.hpp | 4 + include/Cubed/tools/shader_tools.hpp | 3 +- pyproject.toml | 1 + scripts/upscale_nearest.py | 141 ++++++++++++++++++ src/dev_panel.cpp | 4 + src/gameplay/chunk.cpp | 10 +- src/gameplay/vertex_data.cpp | 4 +- src/renderer.cpp | 65 ++++---- src/texture_manager.cpp | 123 ++++++++++++--- src/tools/shader_tools.cpp | 27 +++- uv.lock | 35 +++++ 52 files changed, 462 insertions(+), 65 deletions(-) create mode 100644 assets/normal/block/dirt/back_n.png create mode 100644 assets/normal/block/dirt/base_n.png create mode 100644 assets/normal/block/dirt/front_n.png create mode 100644 assets/normal/block/dirt/left_n.png create mode 100644 assets/normal/block/dirt/right_n.png create mode 100644 assets/normal/block/dirt/top_n.png create mode 100644 assets/normal/block/grass_block/back_n.png create mode 100644 assets/normal/block/grass_block/base_n.png create mode 100644 assets/normal/block/grass_block/front_n.png create mode 100644 assets/normal/block/grass_block/left_n.png create mode 100644 assets/normal/block/grass_block/right_n.png create mode 100644 assets/normal/block/grass_block/top_n.png create mode 100644 assets/normal/block/log/back_n.png create mode 100644 assets/normal/block/log/base_n.png create mode 100644 assets/normal/block/log/front_n.png create mode 100644 assets/normal/block/log/left_n.png create mode 100644 assets/normal/block/log/right_n.png create mode 100644 assets/normal/block/log/top_n.png create mode 100644 assets/normal/block/sand/back_n.png create mode 100644 assets/normal/block/sand/base_n.png create mode 100644 assets/normal/block/sand/front_n.png create mode 100644 assets/normal/block/sand/left_n.png create mode 100644 assets/normal/block/sand/right_n.png create mode 100644 assets/normal/block/sand/top_n.png create mode 100644 assets/normal/block/snowy_grass_block/back_n.png create mode 100644 assets/normal/block/snowy_grass_block/base_n.png create mode 100644 assets/normal/block/snowy_grass_block/front_n.png create mode 100644 assets/normal/block/snowy_grass_block/left_n.png create mode 100644 assets/normal/block/snowy_grass_block/right_n.png create mode 100644 assets/normal/block/snowy_grass_block/top_n.png create mode 100644 assets/normal/block/stone/back_n.png create mode 100644 assets/normal/block/stone/base_n.png create mode 100644 assets/normal/block/stone/front_n.png create mode 100644 assets/normal/block/stone/left_n.png create mode 100644 assets/normal/block/stone/right_n.png create mode 100644 assets/normal/block/stone/top_n.png create mode 100644 scripts/upscale_nearest.py diff --git a/.gitignore b/.gitignore index 875022c..5584e1a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,5 @@ CMakeError.log *~ .DS_Store assets/config.toml -.venv/ \ No newline at end of file +.venv/ +pyout/ \ No newline at end of file diff --git a/assets/normal/block/dirt/back_n.png b/assets/normal/block/dirt/back_n.png new file mode 100644 index 0000000000000000000000000000000000000000..1510cc9aa69783215e16302eb4d5278013b78007 GIT binary patch literal 3393 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 STCK3FfX}`|p0)1b>AwS=Yia`k literal 0 HcmV?d00001 diff --git a/assets/normal/block/log/back_n.png b/assets/normal/block/log/back_n.png new file mode 100644 index 0000000000000000000000000000000000000000..31ae88292f06dd9dce2ebe666171cb31f237feb1 GIT binary patch literal 1492 zcmZ8hc~H`M6#pTnSdyAHnV6(*$6Bsyo0+7G=csF;ri-n*c(md{VQP7yL3tme<`Gt) zuE&mwhFg)97pX*rn(d}}p?M&tpbO?fBreuA+5YjqZ{|DmKJ(t^eLgS44dn>ksRyOrUM@VC|_kBP}&a%-jBwHP-N_vb2`3ahX5A0i8l{j}JSNM*x z`t?+bK;;%de&BSS78ZxIa6Ozso0S@Yx!7Gz_9gLAlCBzm8<*hX4YI)6}Np*^`Mla2?o=^kxgS41RgxxCa&v> zC&Y~1goWJXmKjj(EUlU-E$1_GIh@{z?*uzFL*?J5?e{zixjpY2rQF5fs03q~i_vW& z%xO}fv_+&Fn6vb!6bXg3spW3wvynx7l6AKLDSBtSnnXtC+w!}^(St{tvASc^h zS7*t~c*Y0^?mTHWN*u$a6Mtfvv|N$>fYAzX<VXp-I%)xl zKmL#K38xDqjN4HKePgt@XV?4tY`Afm*-s727?j4nu_nJxOhj7HufzA}<{II8l}^W$ zltZ0FsF~w)h?uPE6Ry{aioqLN`Z$eTyxFB*$veUiPY*xT$D>oU*kwEoac0G`hCj)< zq-Zjs<&<|T6*hr&xVP%VZniS;*3)Mn4{OxOTS`6_Dv8FY-|8#lf$A-C5MOuMk)@V5 zf!(tfRToF_%;qbjo30o#am`4a$!Jf%w4H+tPLiXc*Q=lcr6EY~1lFuhcC5eIN@scz zT)|b3`r20_lz7`;ixK!!GSn8@+IVW9>r;-KF`R#YYT;GG5F8V-tVq&wEceSBx;32N z=Ft&GjMX2~Ot&DRK3h}1!b)uw=`^%!*rRD}{Lr=v- zVc98I~2aCCX_3Q>Wa9J5;6N%;s>GVsi5lVbv$d+{KA`L!J;$Ou7WxH%X znyD>_>-qQb2hWN^%kOGW-uj&0WG;G4KI1jdi}S__HnVn|(pk*Ema&A(VHraTnD|!Q z;Jfzo!Pu#{uU*V#3)!J#JePmXxtS~9TAvF%XRHwYswUy8b)Vf9whNjJGnpoUiJkMA zbIj)7i__w4H`mvHoX#fmq4JeliCsi#8nZqBob8eq=B72wZ&#|@x%^`LH3lnIgUP?-tQ|8`5-xAy^x)JNu#@b6!Qy&-wpw- zD|5fK)cx)CDW{9l4#*u}CaW=BE_B;rzB%koX3V#qu|BXV`LFGP=CwV=4QK5>w55oj zzV_NX#yFupIg-Uj-sI?p=Wi|CUCo)hj2`@4(3|r`@HWp31-KhGy(n`GyM62JbNf{W z-ycRw89!hyE#7PU@AK2I+gg(iCZ(hu*jmXdSG(eQXOCUbOh)?|&`?2y4>Bv#=zZI{ z+0yI!_Z_dCd;i(f>|+hPE=j>-h08(guHFh= zc9Dhu_=_*{<$e}=adf}>I)>^shkNZBHg&w%yfOX}7sCa+jpr7b!V`f=L(evDtyzq@ zI`boQx)*9#K4TX+i&MDJS6pD>mE|J~pIKiGOh_S-zL+`xR`=f;MT_P8re8}P%Qy?Q?H#Vo{`aQLu7EordEv$V z$urM3v|hF^v}eDPCqCgfil)eN$D217T<^QwZ)be7=ueG-+=4?+i{D?|xqTl0o97K) kzyGFRKn^ruqW;g2sWap5<2#BSz}(2->FVdQ&MBb@093c3BLDyZ literal 0 HcmV?d00001 diff --git a/assets/normal/block/log/front_n.png b/assets/normal/block/log/front_n.png new file mode 100644 index 0000000000000000000000000000000000000000..31ae88292f06dd9dce2ebe666171cb31f237feb1 GIT binary patch literal 1492 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 literal 0 HcmV?d00001 diff --git a/assets/normal/block/sand/back_n.png b/assets/normal/block/sand/back_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6e07cf5f277b0c2cdd4aa757c2701fae013f8a3e GIT binary patch literal 2755 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 CkN2|x literal 0 HcmV?d00001 diff --git a/assets/normal/block/snowy_grass_block/base_n.png b/assets/normal/block/snowy_grass_block/base_n.png new file mode 100644 index 0000000000000000000000000000000000000000..1510cc9aa69783215e16302eb4d5278013b78007 GIT binary patch literal 3393 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 CkN2|x literal 0 HcmV?d00001 diff --git a/assets/normal/block/snowy_grass_block/left_n.png b/assets/normal/block/snowy_grass_block/left_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ec92e5e8b33d1faf3156c61c563b2cb171945cec GIT binary patch literal 4113 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 CkN2|x literal 0 HcmV?d00001 diff --git a/assets/normal/block/snowy_grass_block/right_n.png b/assets/normal/block/snowy_grass_block/right_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ec92e5e8b33d1faf3156c61c563b2cb171945cec GIT binary patch literal 4113 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 CkN2|x literal 0 HcmV?d00001 diff --git a/assets/normal/block/snowy_grass_block/top_n.png b/assets/normal/block/snowy_grass_block/top_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c08004dd2d90d04794abb39ac4d1eb67e20d8b72 GIT binary patch literal 2978 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 EFZ@5((*OVf literal 0 HcmV?d00001 diff --git a/assets/normal/block/stone/back_n.png b/assets/normal/block/stone/back_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c268e19334aecc1433ad6099ab60ab52a8cd2497 GIT binary patch literal 3166 zcmZXXc{mhm8^&ksl!>fm&|*nKbh4YN$dYA*;>ea!LzbB^Loy3a941R7Bg+&eWldx( zXG9ofk3z;y!(fQ)Gv?FxUElf6)%VBqKG*yHci-=Q{hs%Yx3{wp6*(*d002a-EX^GD zI^kyt@$9WOZtK~5O&DeQdk6pkYW`Wkbajv%0I*-o%Iv&Tc=lqByDxY^ipCv1koenS zKcy{G|CS0RRlh)Rp)LC|pN7;4l1gmhv)QL&so7s+g#CruLE)1T7Gb3< ze5?UuG=w!0y!}Ifue4FHTqbriP14bY|8#V23Kkp!=z_yKFkNuaI|_=!Ck?#+6zZe+ z{un{HQL|m>D$qul10@y#>?XgH7QwWGgnff*yAQ*kfNm+sY7o3D0SUc4C(QRf`MGhB zB}aX}Q%1@^ z?oQ}d7_ka@iQ5yZi_>ee!aI+97jO(+mWK*1d?sspEnQuq9>s0^3s@)dA?}7b7aKNX z+AXuVP>j{!RKtIgI==an)iuP}x_;5US@CQ>lT`Os= z?QAlr{I+m`Qb7i%>(WeB0tIs2=nli%nT^Q{(q2gY9TEyYJtJMG-Wc^Iv~4%0`O*1j zs(;MWv>Ah2L_12EGv_K_=(`Ims;gIP#f(QXg@+8FIYevoa;6-s5?0zKR?oa0)5jv`Y=T!JdlI149aZ!~Q zi%snxoYqV3p3z$S{#|sRJTwrpGZA$&^*ewgYM->H>~ER-=lNgXfm8=IBe74<&W={` z2I)`HStlyBWSZHEN$J3_;j|9=N-|U|(9{FHWv;bQj)d3PG2+jLUx%9bZ(~&j=GJS_ z{2wBCK&}AIZidxe^iXkExQ6yMu7$Fw9+(dhUfDnDP}mRw?!`MP7 zh?JRvb+36z@P?L_U!6S#3!l&*FLBk0TNj^*2y(yv6@`7ZTLawGCv>d}7e7eK6wKY& z7{5E16YnlG+Jacd?*_>%myi}oE;_XR&OZ^gRKlu>Ox!vB+-d)e1*9K znYT1HzCmhpw2!r+I2Cmv7XwrixIJj8VB|a5Z43AKNUl|TX3B?63gP+7R0-~OnjXRv ze8l)05wmMNW7QBnsTf`B%f;D4WAc2-?*L0 zneZFyG8HA>8~CLs2kn$yeMJRepSCu65r_%=;WwNBPwLX4TC&dwVdSz4Oxa$NdB89` zNmYsN4L%2l+pgcU1eo2(bfBq|0|lc=rMMLbU_zE zn$S*Utbd*er4WX%{GR)(`w!P>#VsT#@K)m={K{9Ai#t29{`>pTH91Q&)9Xt?b@TGRG^Xp)K(1+VkMk@Ts>zN+rbu!OdR`!?X65xC$)3ea*V*dE;5_FA=)pYmy zucmj|@+6-D1AQMF@Z2R`@(7z1)Z36`=uC*IMIOc-emh7#l^dyybX+$H^aB5%A!_$6 zB|!pft`q)bul7eNIm2YyTD#wivsB0TRa)&|5)CA+cw}KrZJ2sWmGwwq`8)Dnpr5|aJh_HrFQ<<@(z56eo6N5~ef+X& zBz}BqX-aDVLIXe^awJ}EBZ8p)0zG5@Wc41=dgg%yHkIT=jpW@r)A`D@lvVVAA+Xa| z3F{cm@FN>r0KLL8GK?&2FrGG=o*ugR*!K4mq)KquY8llMWP~N27+27pMT2y?aeibY zi|hU9;!e*7%V`7e{*u4DtA7%(_I0d6|s>yO>=`XtnC2M&_z~7DUbd`feGllw9(a>Q72>Fq{5Bzq4!TTKFRk7H4*?*%;@@Ovqok z<1-}%Qb~HsPom{_VIs)ZY82y4$K^9?Yo^g0`=OfxHrs%8r;WLb+Gun z`huL7=b$v6+a=KUmx;JMuS;Y3WNw|m0j|DDT%6W}BsjzF zqT`h~P!?qu6Xa0|&C0^#Xy{%D6RmhsqN)B0EIaR@Z>x4#hl&UVsm#<%t=i;d>(bSj zO6n|nw0g-m)&5z}tN49BO7cZ}|R7ywom?aWFqct!sU Dme&*T literal 0 HcmV?d00001 diff --git a/assets/normal/block/stone/base_n.png b/assets/normal/block/stone/base_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c268e19334aecc1433ad6099ab60ab52a8cd2497 GIT binary patch literal 3166 zcmZXXc{mhm8^&ksl!>fm&|*nKbh4YN$dYA*;>ea!LzbB^Loy3a941R7Bg+&eWldx( zXG9ofk3z;y!(fQ)Gv?FxUElf6)%VBqKG*yHci-=Q{hs%Yx3{wp6*(*d002a-EX^GD zI^kyt@$9WOZtK~5O&DeQdk6pkYW`Wkbajv%0I*-o%Iv&Tc=lqByDxY^ipCv1koenS zKcy{G|CS0RRlh)Rp)LC|pN7;4l1gmhv)QL&so7s+g#CruLE)1T7Gb3< ze5?UuG=w!0y!}Ifue4FHTqbriP14bY|8#V23Kkp!=z_yKFkNuaI|_=!Ck?#+6zZe+ z{un{HQL|m>D$qul10@y#>?XgH7QwWGgnff*yAQ*kfNm+sY7o3D0SUc4C(QRf`MGhB zB}aX}Q%1@^ z?oQ}d7_ka@iQ5yZi_>ee!aI+97jO(+mWK*1d?sspEnQuq9>s0^3s@)dA?}7b7aKNX z+AXuVP>j{!RKtIgI==an)iuP}x_;5US@CQ>lT`Os= z?QAlr{I+m`Qb7i%>(WeB0tIs2=nli%nT^Q{(q2gY9TEyYJtJMG-Wc^Iv~4%0`O*1j zs(;MWv>Ah2L_12EGv_K_=(`Ims;gIP#f(QXg@+8FIYevoa;6-s5?0zKR?oa0)5jv`Y=T!JdlI149aZ!~Q zi%snxoYqV3p3z$S{#|sRJTwrpGZA$&^*ewgYM->H>~ER-=lNgXfm8=IBe74<&W={` z2I)`HStlyBWSZHEN$J3_;j|9=N-|U|(9{FHWv;bQj)d3PG2+jLUx%9bZ(~&j=GJS_ z{2wBCK&}AIZidxe^iXkExQ6yMu7$Fw9+(dhUfDnDP}mRw?!`MP7 zh?JRvb+36z@P?L_U!6S#3!l&*FLBk0TNj^*2y(yv6@`7ZTLawGCv>d}7e7eK6wKY& z7{5E16YnlG+Jacd?*_>%myi}oE;_XR&OZ^gRKlu>Ox!vB+-d)e1*9K znYT1HzCmhpw2!r+I2Cmv7XwrixIJj8VB|a5Z43AKNUl|TX3B?63gP+7R0-~OnjXRv ze8l)05wmMNW7QBnsTf`B%f;D4WAc2-?*L0 zneZFyG8HA>8~CLs2kn$yeMJRepSCu65r_%=;WwNBPwLX4TC&dwVdSz4Oxa$NdB89` zNmYsN4L%2l+pgcU1eo2(bfBq|0|lc=rMMLbU_zE zn$S*Utbd*er4WX%{GR)(`w!P>#VsT#@K)m={K{9Ai#t29{`>pTH91Q&)9Xt?b@TGRG^Xp)K(1+VkMk@Ts>zN+rbu!OdR`!?X65xC$)3ea*V*dE;5_FA=)pYmy zucmj|@+6-D1AQMF@Z2R`@(7z1)Z36`=uC*IMIOc-emh7#l^dyybX+$H^aB5%A!_$6 zB|!pft`q)bul7eNIm2YyTD#wivsB0TRa)&|5)CA+cw}KrZJ2sWmGwwq`8)Dnpr5|aJh_HrFQ<<@(z56eo6N5~ef+X& zBz}BqX-aDVLIXe^awJ}EBZ8p)0zG5@Wc41=dgg%yHkIT=jpW@r)A`D@lvVVAA+Xa| z3F{cm@FN>r0KLL8GK?&2FrGG=o*ugR*!K4mq)KquY8llMWP~N27+27pMT2y?aeibY zi|hU9;!e*7%V`7e{*u4DtA7%(_I0d6|s>yO>=`XtnC2M&_z~7DUbd`feGllw9(a>Q72>Fq{5Bzq4!TTKFRk7H4*?*%;@@Ovqok z<1-}%Qb~HsPom{_VIs)ZY82y4$K^9?Yo^g0`=OfxHrs%8r;WLb+Gun z`huL7=b$v6+a=KUmx;JMuS;Y3WNw|m0j|DDT%6W}BsjzF zqT`h~P!?qu6Xa0|&C0^#Xy{%D6RmhsqN)B0EIaR@Z>x4#hl&UVsm#<%t=i;d>(bSj zO6n|nw0g-m)&5z}tN49BO7cZ}|R7ywom?aWFqct!sU Dme&*T literal 0 HcmV?d00001 diff --git a/assets/normal/block/stone/front_n.png b/assets/normal/block/stone/front_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c268e19334aecc1433ad6099ab60ab52a8cd2497 GIT binary patch literal 3166 zcmZXXc{mhm8^&ksl!>fm&|*nKbh4YN$dYA*;>ea!LzbB^Loy3a941R7Bg+&eWldx( zXG9ofk3z;y!(fQ)Gv?FxUElf6)%VBqKG*yHci-=Q{hs%Yx3{wp6*(*d002a-EX^GD zI^kyt@$9WOZtK~5O&DeQdk6pkYW`Wkbajv%0I*-o%Iv&Tc=lqByDxY^ipCv1koenS zKcy{G|CS0RRlh)Rp)LC|pN7;4l1gmhv)QL&so7s+g#CruLE)1T7Gb3< ze5?UuG=w!0y!}Ifue4FHTqbriP14bY|8#V23Kkp!=z_yKFkNuaI|_=!Ck?#+6zZe+ z{un{HQL|m>D$qul10@y#>?XgH7QwWGgnff*yAQ*kfNm+sY7o3D0SUc4C(QRf`MGhB zB}aX}Q%1@^ z?oQ}d7_ka@iQ5yZi_>ee!aI+97jO(+mWK*1d?sspEnQuq9>s0^3s@)dA?}7b7aKNX z+AXuVP>j{!RKtIgI==an)iuP}x_;5US@CQ>lT`Os= z?QAlr{I+m`Qb7i%>(WeB0tIs2=nli%nT^Q{(q2gY9TEyYJtJMG-Wc^Iv~4%0`O*1j zs(;MWv>Ah2L_12EGv_K_=(`Ims;gIP#f(QXg@+8FIYevoa;6-s5?0zKR?oa0)5jv`Y=T!JdlI149aZ!~Q zi%snxoYqV3p3z$S{#|sRJTwrpGZA$&^*ewgYM->H>~ER-=lNgXfm8=IBe74<&W={` z2I)`HStlyBWSZHEN$J3_;j|9=N-|U|(9{FHWv;bQj)d3PG2+jLUx%9bZ(~&j=GJS_ z{2wBCK&}AIZidxe^iXkExQ6yMu7$Fw9+(dhUfDnDP}mRw?!`MP7 zh?JRvb+36z@P?L_U!6S#3!l&*FLBk0TNj^*2y(yv6@`7ZTLawGCv>d}7e7eK6wKY& z7{5E16YnlG+Jacd?*_>%myi}oE;_XR&OZ^gRKlu>Ox!vB+-d)e1*9K znYT1HzCmhpw2!r+I2Cmv7XwrixIJj8VB|a5Z43AKNUl|TX3B?63gP+7R0-~OnjXRv ze8l)05wmMNW7QBnsTf`B%f;D4WAc2-?*L0 zneZFyG8HA>8~CLs2kn$yeMJRepSCu65r_%=;WwNBPwLX4TC&dwVdSz4Oxa$NdB89` zNmYsN4L%2l+pgcU1eo2(bfBq|0|lc=rMMLbU_zE zn$S*Utbd*er4WX%{GR)(`w!P>#VsT#@K)m={K{9Ai#t29{`>pTH91Q&)9Xt?b@TGRG^Xp)K(1+VkMk@Ts>zN+rbu!OdR`!?X65xC$)3ea*V*dE;5_FA=)pYmy zucmj|@+6-D1AQMF@Z2R`@(7z1)Z36`=uC*IMIOc-emh7#l^dyybX+$H^aB5%A!_$6 zB|!pft`q)bul7eNIm2YyTD#wivsB0TRa)&|5)CA+cw}KrZJ2sWmGwwq`8)Dnpr5|aJh_HrFQ<<@(z56eo6N5~ef+X& zBz}BqX-aDVLIXe^awJ}EBZ8p)0zG5@Wc41=dgg%yHkIT=jpW@r)A`D@lvVVAA+Xa| z3F{cm@FN>r0KLL8GK?&2FrGG=o*ugR*!K4mq)KquY8llMWP~N27+27pMT2y?aeibY zi|hU9;!e*7%V`7e{*u4DtA7%(_I0d6|s>yO>=`XtnC2M&_z~7DUbd`feGllw9(a>Q72>Fq{5Bzq4!TTKFRk7H4*?*%;@@Ovqok z<1-}%Qb~HsPom{_VIs)ZY82y4$K^9?Yo^g0`=OfxHrs%8r;WLb+Gun z`huL7=b$v6+a=KUmx;JMuS;Y3WNw|m0j|DDT%6W}BsjzF zqT`h~P!?qu6Xa0|&C0^#Xy{%D6RmhsqN)B0EIaR@Z>x4#hl&UVsm#<%t=i;d>(bSj zO6n|nw0g-m)&5z}tN49BO7cZ}|R7ywom?aWFqct!sU Dme&*T literal 0 HcmV?d00001 diff --git a/assets/normal/block/stone/left_n.png b/assets/normal/block/stone/left_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c268e19334aecc1433ad6099ab60ab52a8cd2497 GIT binary patch literal 3166 zcmZXXc{mhm8^&ksl!>fm&|*nKbh4YN$dYA*;>ea!LzbB^Loy3a941R7Bg+&eWldx( zXG9ofk3z;y!(fQ)Gv?FxUElf6)%VBqKG*yHci-=Q{hs%Yx3{wp6*(*d002a-EX^GD zI^kyt@$9WOZtK~5O&DeQdk6pkYW`Wkbajv%0I*-o%Iv&Tc=lqByDxY^ipCv1koenS zKcy{G|CS0RRlh)Rp)LC|pN7;4l1gmhv)QL&so7s+g#CruLE)1T7Gb3< ze5?UuG=w!0y!}Ifue4FHTqbriP14bY|8#V23Kkp!=z_yKFkNuaI|_=!Ck?#+6zZe+ z{un{HQL|m>D$qul10@y#>?XgH7QwWGgnff*yAQ*kfNm+sY7o3D0SUc4C(QRf`MGhB zB}aX}Q%1@^ z?oQ}d7_ka@iQ5yZi_>ee!aI+97jO(+mWK*1d?sspEnQuq9>s0^3s@)dA?}7b7aKNX z+AXuVP>j{!RKtIgI==an)iuP}x_;5US@CQ>lT`Os= z?QAlr{I+m`Qb7i%>(WeB0tIs2=nli%nT^Q{(q2gY9TEyYJtJMG-Wc^Iv~4%0`O*1j zs(;MWv>Ah2L_12EGv_K_=(`Ims;gIP#f(QXg@+8FIYevoa;6-s5?0zKR?oa0)5jv`Y=T!JdlI149aZ!~Q zi%snxoYqV3p3z$S{#|sRJTwrpGZA$&^*ewgYM->H>~ER-=lNgXfm8=IBe74<&W={` z2I)`HStlyBWSZHEN$J3_;j|9=N-|U|(9{FHWv;bQj)d3PG2+jLUx%9bZ(~&j=GJS_ z{2wBCK&}AIZidxe^iXkExQ6yMu7$Fw9+(dhUfDnDP}mRw?!`MP7 zh?JRvb+36z@P?L_U!6S#3!l&*FLBk0TNj^*2y(yv6@`7ZTLawGCv>d}7e7eK6wKY& z7{5E16YnlG+Jacd?*_>%myi}oE;_XR&OZ^gRKlu>Ox!vB+-d)e1*9K znYT1HzCmhpw2!r+I2Cmv7XwrixIJj8VB|a5Z43AKNUl|TX3B?63gP+7R0-~OnjXRv ze8l)05wmMNW7QBnsTf`B%f;D4WAc2-?*L0 zneZFyG8HA>8~CLs2kn$yeMJRepSCu65r_%=;WwNBPwLX4TC&dwVdSz4Oxa$NdB89` zNmYsN4L%2l+pgcU1eo2(bfBq|0|lc=rMMLbU_zE zn$S*Utbd*er4WX%{GR)(`w!P>#VsT#@K)m={K{9Ai#t29{`>pTH91Q&)9Xt?b@TGRG^Xp)K(1+VkMk@Ts>zN+rbu!OdR`!?X65xC$)3ea*V*dE;5_FA=)pYmy zucmj|@+6-D1AQMF@Z2R`@(7z1)Z36`=uC*IMIOc-emh7#l^dyybX+$H^aB5%A!_$6 zB|!pft`q)bul7eNIm2YyTD#wivsB0TRa)&|5)CA+cw}KrZJ2sWmGwwq`8)Dnpr5|aJh_HrFQ<<@(z56eo6N5~ef+X& zBz}BqX-aDVLIXe^awJ}EBZ8p)0zG5@Wc41=dgg%yHkIT=jpW@r)A`D@lvVVAA+Xa| z3F{cm@FN>r0KLL8GK?&2FrGG=o*ugR*!K4mq)KquY8llMWP~N27+27pMT2y?aeibY zi|hU9;!e*7%V`7e{*u4DtA7%(_I0d6|s>yO>=`XtnC2M&_z~7DUbd`feGllw9(a>Q72>Fq{5Bzq4!TTKFRk7H4*?*%;@@Ovqok z<1-}%Qb~HsPom{_VIs)ZY82y4$K^9?Yo^g0`=OfxHrs%8r;WLb+Gun z`huL7=b$v6+a=KUmx;JMuS;Y3WNw|m0j|DDT%6W}BsjzF zqT`h~P!?qu6Xa0|&C0^#Xy{%D6RmhsqN)B0EIaR@Z>x4#hl&UVsm#<%t=i;d>(bSj zO6n|nw0g-m)&5z}tN49BO7cZ}|R7ywom?aWFqct!sU Dme&*T literal 0 HcmV?d00001 diff --git a/assets/normal/block/stone/right_n.png b/assets/normal/block/stone/right_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c268e19334aecc1433ad6099ab60ab52a8cd2497 GIT binary patch literal 3166 zcmZXXc{mhm8^&ksl!>fm&|*nKbh4YN$dYA*;>ea!LzbB^Loy3a941R7Bg+&eWldx( zXG9ofk3z;y!(fQ)Gv?FxUElf6)%VBqKG*yHci-=Q{hs%Yx3{wp6*(*d002a-EX^GD zI^kyt@$9WOZtK~5O&DeQdk6pkYW`Wkbajv%0I*-o%Iv&Tc=lqByDxY^ipCv1koenS zKcy{G|CS0RRlh)Rp)LC|pN7;4l1gmhv)QL&so7s+g#CruLE)1T7Gb3< ze5?UuG=w!0y!}Ifue4FHTqbriP14bY|8#V23Kkp!=z_yKFkNuaI|_=!Ck?#+6zZe+ z{un{HQL|m>D$qul10@y#>?XgH7QwWGgnff*yAQ*kfNm+sY7o3D0SUc4C(QRf`MGhB zB}aX}Q%1@^ z?oQ}d7_ka@iQ5yZi_>ee!aI+97jO(+mWK*1d?sspEnQuq9>s0^3s@)dA?}7b7aKNX z+AXuVP>j{!RKtIgI==an)iuP}x_;5US@CQ>lT`Os= z?QAlr{I+m`Qb7i%>(WeB0tIs2=nli%nT^Q{(q2gY9TEyYJtJMG-Wc^Iv~4%0`O*1j zs(;MWv>Ah2L_12EGv_K_=(`Ims;gIP#f(QXg@+8FIYevoa;6-s5?0zKR?oa0)5jv`Y=T!JdlI149aZ!~Q zi%snxoYqV3p3z$S{#|sRJTwrpGZA$&^*ewgYM->H>~ER-=lNgXfm8=IBe74<&W={` z2I)`HStlyBWSZHEN$J3_;j|9=N-|U|(9{FHWv;bQj)d3PG2+jLUx%9bZ(~&j=GJS_ z{2wBCK&}AIZidxe^iXkExQ6yMu7$Fw9+(dhUfDnDP}mRw?!`MP7 zh?JRvb+36z@P?L_U!6S#3!l&*FLBk0TNj^*2y(yv6@`7ZTLawGCv>d}7e7eK6wKY& z7{5E16YnlG+Jacd?*_>%myi}oE;_XR&OZ^gRKlu>Ox!vB+-d)e1*9K znYT1HzCmhpw2!r+I2Cmv7XwrixIJj8VB|a5Z43AKNUl|TX3B?63gP+7R0-~OnjXRv ze8l)05wmMNW7QBnsTf`B%f;D4WAc2-?*L0 zneZFyG8HA>8~CLs2kn$yeMJRepSCu65r_%=;WwNBPwLX4TC&dwVdSz4Oxa$NdB89` zNmYsN4L%2l+pgcU1eo2(bfBq|0|lc=rMMLbU_zE zn$S*Utbd*er4WX%{GR)(`w!P>#VsT#@K)m={K{9Ai#t29{`>pTH91Q&)9Xt?b@TGRG^Xp)K(1+VkMk@Ts>zN+rbu!OdR`!?X65xC$)3ea*V*dE;5_FA=)pYmy zucmj|@+6-D1AQMF@Z2R`@(7z1)Z36`=uC*IMIOc-emh7#l^dyybX+$H^aB5%A!_$6 zB|!pft`q)bul7eNIm2YyTD#wivsB0TRa)&|5)CA+cw}KrZJ2sWmGwwq`8)Dnpr5|aJh_HrFQ<<@(z56eo6N5~ef+X& zBz}BqX-aDVLIXe^awJ}EBZ8p)0zG5@Wc41=dgg%yHkIT=jpW@r)A`D@lvVVAA+Xa| z3F{cm@FN>r0KLL8GK?&2FrGG=o*ugR*!K4mq)KquY8llMWP~N27+27pMT2y?aeibY zi|hU9;!e*7%V`7e{*u4DtA7%(_I0d6|s>yO>=`XtnC2M&_z~7DUbd`feGllw9(a>Q72>Fq{5Bzq4!TTKFRk7H4*?*%;@@Ovqok z<1-}%Qb~HsPom{_VIs)ZY82y4$K^9?Yo^g0`=OfxHrs%8r;WLb+Gun z`huL7=b$v6+a=KUmx;JMuS;Y3WNw|m0j|DDT%6W}BsjzF zqT`h~P!?qu6Xa0|&C0^#Xy{%D6RmhsqN)B0EIaR@Z>x4#hl&UVsm#<%t=i;d>(bSj zO6n|nw0g-m)&5z}tN49BO7cZ}|R7ywom?aWFqct!sU Dme&*T literal 0 HcmV?d00001 diff --git a/assets/normal/block/stone/top_n.png b/assets/normal/block/stone/top_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c268e19334aecc1433ad6099ab60ab52a8cd2497 GIT binary patch literal 3166 zcmZXXc{mhm8^&ksl!>fm&|*nKbh4YN$dYA*;>ea!LzbB^Loy3a941R7Bg+&eWldx( zXG9ofk3z;y!(fQ)Gv?FxUElf6)%VBqKG*yHci-=Q{hs%Yx3{wp6*(*d002a-EX^GD zI^kyt@$9WOZtK~5O&DeQdk6pkYW`Wkbajv%0I*-o%Iv&Tc=lqByDxY^ipCv1koenS zKcy{G|CS0RRlh)Rp)LC|pN7;4l1gmhv)QL&so7s+g#CruLE)1T7Gb3< ze5?UuG=w!0y!}Ifue4FHTqbriP14bY|8#V23Kkp!=z_yKFkNuaI|_=!Ck?#+6zZe+ z{un{HQL|m>D$qul10@y#>?XgH7QwWGgnff*yAQ*kfNm+sY7o3D0SUc4C(QRf`MGhB zB}aX}Q%1@^ z?oQ}d7_ka@iQ5yZi_>ee!aI+97jO(+mWK*1d?sspEnQuq9>s0^3s@)dA?}7b7aKNX z+AXuVP>j{!RKtIgI==an)iuP}x_;5US@CQ>lT`Os= z?QAlr{I+m`Qb7i%>(WeB0tIs2=nli%nT^Q{(q2gY9TEyYJtJMG-Wc^Iv~4%0`O*1j zs(;MWv>Ah2L_12EGv_K_=(`Ims;gIP#f(QXg@+8FIYevoa;6-s5?0zKR?oa0)5jv`Y=T!JdlI149aZ!~Q zi%snxoYqV3p3z$S{#|sRJTwrpGZA$&^*ewgYM->H>~ER-=lNgXfm8=IBe74<&W={` z2I)`HStlyBWSZHEN$J3_;j|9=N-|U|(9{FHWv;bQj)d3PG2+jLUx%9bZ(~&j=GJS_ z{2wBCK&}AIZidxe^iXkExQ6yMu7$Fw9+(dhUfDnDP}mRw?!`MP7 zh?JRvb+36z@P?L_U!6S#3!l&*FLBk0TNj^*2y(yv6@`7ZTLawGCv>d}7e7eK6wKY& z7{5E16YnlG+Jacd?*_>%myi}oE;_XR&OZ^gRKlu>Ox!vB+-d)e1*9K znYT1HzCmhpw2!r+I2Cmv7XwrixIJj8VB|a5Z43AKNUl|TX3B?63gP+7R0-~OnjXRv ze8l)05wmMNW7QBnsTf`B%f;D4WAc2-?*L0 zneZFyG8HA>8~CLs2kn$yeMJRepSCu65r_%=;WwNBPwLX4TC&dwVdSz4Oxa$NdB89` zNmYsN4L%2l+pgcU1eo2(bfBq|0|lc=rMMLbU_zE zn$S*Utbd*er4WX%{GR)(`w!P>#VsT#@K)m={K{9Ai#t29{`>pTH91Q&)9Xt?b@TGRG^Xp)K(1+VkMk@Ts>zN+rbu!OdR`!?X65xC$)3ea*V*dE;5_FA=)pYmy zucmj|@+6-D1AQMF@Z2R`@(7z1)Z36`=uC*IMIOc-emh7#l^dyybX+$H^aB5%A!_$6 zB|!pft`q)bul7eNIm2YyTD#wivsB0TRa)&|5)CA+cw}KrZJ2sWmGwwq`8)Dnpr5|aJh_HrFQ<<@(z56eo6N5~ef+X& zBz}BqX-aDVLIXe^awJ}EBZ8p)0zG5@Wc41=dgg%yHkIT=jpW@r)A`D@lvVVAA+Xa| z3F{cm@FN>r0KLL8GK?&2FrGG=o*ugR*!K4mq)KquY8llMWP~N27+27pMT2y?aeibY zi|hU9;!e*7%V`7e{*u4DtA7%(_I0d6|s>yO>=`XtnC2M&_z~7DUbd`feGllw9(a>Q72>Fq{5Bzq4!TTKFRk7H4*?*%;@@Ovqok z<1-}%Qb~HsPom{_VIs)ZY82y4$K^9?Yo^g0`=OfxHrs%8r;WLb+Gun z`huL7=b$v6+a=KUmx;JMuS;Y3WNw|m0j|DDT%6W}BsjzF zqT`h~P!?qu6Xa0|&C0^#Xy{%D6RmhsqN)B0EIaR@Z>x4#hl&UVsm#<%t=i;d>(bSj zO6n|nw0g-m)&5z}tN49BO7cZ}|R7ywom?aWFqct!sU Dme&*T literal 0 HcmV?d00001 diff --git a/assets/shaders/block_f_shader.glsl b/assets/shaders/block_f_shader.glsl index e9544aa..eef3983 100644 --- a/assets/shaders/block_f_shader.glsl +++ b/assets/shaders/block_f_shader.glsl @@ -3,13 +3,15 @@ in vec2 tc; in vec3 normal; in vec3 vert_pos; +in vec3 tangent; +in vec3 bitangent; in vec4 FragPosLightSpace; in float roughness; flat in int tex_layer; out vec4 color; layout (binding = 0) uniform sampler2D shadowMap; layout (binding = 1) uniform sampler2DArray samp; - +layout (binding = 2) uniform sampler2DArray normMap; uniform float ambientStrength; uniform vec3 sunlightColor; uniform vec3 ambientColor; @@ -21,6 +23,8 @@ uniform float specularStrength; uniform float lightSizeUV; uniform float minRadius; uniform float maxRadius; +uniform bool enablePBR; +uniform bool flipY; const vec2 poissonDisk32[32] = vec2[]( vec2(-0.975402, -0.071138), vec2(-0.920347, -0.411420), @@ -259,6 +263,16 @@ float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir) return shadow; } +vec3 calcNewNormal() { + mat3 TBN = mat3(normalize(tangent), normalize(bitangent), normalize(normal)); + vec3 retrievedNormal = texture(normMap, vec3(tc, tex_layer)).xyz; + retrievedNormal = retrievedNormal * 2.0 - 1.0; + if (flipY) { + retrievedNormal.y = -retrievedNormal.y; + } + vec3 newNormal = TBN * retrievedNormal; + return normalize(newNormal); +} void main(void) { vec4 objectColor = texture(samp, vec3(tc, tex_layer)); @@ -273,8 +287,13 @@ void main(void) { vec3 lightDir = normalize(-sunlightDir); - - vec3 norm = normalize(normal); + vec3 norm; + if (enablePBR) { + norm = calcNewNormal(); + } else { + norm = normalize(normal); + } + vec3 V = normalize(cameraPos - vert_pos); @@ -323,7 +342,9 @@ void main(void) { float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); - color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular, objectColor.a); - - //color = varyingColor; + color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular * objectColor.rgb, objectColor.a); + //color = vec4(normal * 0.5 + 0.5, 1.0); + //color = vec4(tangent * 0.5 + 0.5, 1.0);; + //color = vec4(norm * 0.5 + 0.5, 1.0); + //color = vec4(calcNewNormal(), 1.0); } diff --git a/assets/shaders/block_v_shader.glsl b/assets/shaders/block_v_shader.glsl index d22fcad..6927a2e 100644 --- a/assets/shaders/block_v_shader.glsl +++ b/assets/shaders/block_v_shader.glsl @@ -5,9 +5,11 @@ layout (location = 1) in vec2 texCoord; layout (location = 2) in float layer; layout (location = 3) in vec3 aNormal; layout (location = 4) in float Roughness; - +layout (location = 5) in vec3 aTangent; out vec2 tc; out vec3 normal; +out vec3 tangent; +out vec3 bitangent; out vec3 vert_pos; flat out int tex_layer; out vec4 FragPosLightSpace; @@ -20,6 +22,7 @@ mat4 buildTranslate(float x, float y, float z); uniform mat4 mv_matrix; uniform mat4 proj_matrix; +uniform mat4 model_matrix; uniform mat4 norm_matrix; uniform mat4 lightSpaceMatrix; @@ -32,7 +35,9 @@ void main(void) { tex_layer = int(layer); roughness = Roughness; - normal = mat3(norm_matrix) * aNormal; + normal = normalize(mat3(norm_matrix) * aNormal); + tangent = normalize(mat3(model_matrix) * aTangent); + bitangent = cross(normal, tangent); FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0); gl_Position = proj_matrix * viewPos; } diff --git a/include/Cubed/primitive_data.hpp b/include/Cubed/primitive_data.hpp index 39e5a61..713b49b 100644 --- a/include/Cubed/primitive_data.hpp +++ b/include/Cubed/primitive_data.hpp @@ -136,6 +136,50 @@ constexpr float NORMALS[6][6][3] = { {0.0f, -1.0f, 0.0f}, {0.0f, -1.0f, 0.0f}}}; +constexpr float TANGENTS[6][6][3] = { + // ===== front (z = +1) ===== + {{1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}}, + // ===== right (x = +1) ===== + {{0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}}, + // ===== back (z = -1) ===== + {{-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}}, + // ===== left (x = -1) ===== + {{0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f}}, + // ===== top (y = +1) ===== + {{1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}}, + // ===== bottom (y = -1) ===== + {{1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}}}; + #pragma endregion constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, @@ -211,6 +255,22 @@ constexpr float CROSS_NORMALS[2][6][3] = { {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}}}; +constexpr float CROSS_TANGENTS[2][6][3] = { + // ===== Plane 1 ===== + {{0.7071f, 0.0f, 0.7071f}, + {0.7071f, 0.0f, 0.7071f}, + {0.7071f, 0.0f, 0.7071f}, + {0.7071f, 0.0f, 0.7071f}, + {0.7071f, 0.0f, 0.7071f}, + {0.7071f, 0.0f, 0.7071f}}, + // ===== Plane 2 ===== + {{-0.7071f, 0.0f, 0.7071f}, + {-0.7071f, 0.0f, 0.7071f}, + {-0.7071f, 0.0f, 0.7071f}, + {-0.7071f, 0.0f, 0.7071f}, + {-0.7071f, 0.0f, 0.7071f}, + {-0.7071f, 0.0f, 0.7071f}}}; + #pragma endregion // [-1, 1] constexpr float QUAD_VERTICES[] = { @@ -225,6 +285,7 @@ struct Vertex3D { float layer = 0.0f; float nx = 0.0f, ny = 0.0f, nz = 0.0f; float roughness = 1.0f; + float tx = 0.0f, ty = 0.0f, tz = 0.0f; }; struct Vertex2D { diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index 275e8f0..74ef14c 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -34,6 +34,8 @@ public: bool& shader_on(); bool& water_perturb(); bool& water_depth_fade(); + bool& pbr(); + bool& flip_y(); int& shadow_mode(); int& light_cull_face(); int& light_size_uv(); @@ -95,6 +97,8 @@ private: bool m_shader_on = true; bool m_water_perturb = true; bool m_water_depth_fade = true; + bool m_pbr = true; + bool m_flip_y = false; int m_shadow_mode = 0; int m_light_cull_face = 0; float m_aspect = 0.0f; diff --git a/include/Cubed/texture_manager.hpp b/include/Cubed/texture_manager.hpp index 2579885..eca2aba 100644 --- a/include/Cubed/texture_manager.hpp +++ b/include/Cubed/texture_manager.hpp @@ -12,7 +12,9 @@ private: GLuint m_texture_array = 0; GLuint m_cross_plane_array = 0; GLuint m_ui_array = 0; + GLuint m_pbr_texture_array = 0; GLfloat m_max_aniso = 0.0f; + int m_aniso = 1; std::vector m_item_textures; @@ -22,6 +24,7 @@ private: void load_block_item_texture(unsigned id); void load_cross_plane_texture(unsigned id); void load_ui_texture(unsigned id); + void load_pbr_texture(unsigned id); void init_item(); void init_block(); void init_ui(); @@ -36,6 +39,7 @@ public: GLuint get_texture_array() const; GLuint get_cross_plane_array() const; GLuint get_ui_array() const; + GLuint get_pbr_texture() const; const std::vector& item_textures() const; // Must call after MapTable::init_map() and glfwMakeContextCurrent(window); void init_texture(); diff --git a/include/Cubed/tools/shader_tools.hpp b/include/Cubed/tools/shader_tools.hpp index 0c6c151..aedb4e3 100644 --- a/include/Cubed/tools/shader_tools.hpp +++ b/include/Cubed/tools/shader_tools.hpp @@ -14,7 +14,8 @@ void print_program_info(int prog); bool check_opengl_error(); std::string read_shader_source(const std::string& file_path); void delete_image_data(unsigned char* data); -unsigned char* load_image_data(const std::string& tex_image_path); +unsigned char* load_image_data(const std::string& tex_image_path, + bool check_exist = true); } // namespace Tools diff --git a/pyproject.toml b/pyproject.toml index b11e9b9..dfaf528 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.14" dependencies = [ "loguru>=0.7.3", + "pillow>=12.2.0", "pytomlpp>=1.1.0", ] diff --git a/scripts/upscale_nearest.py b/scripts/upscale_nearest.py new file mode 100644 index 0000000..4656e1e --- /dev/null +++ b/scripts/upscale_nearest.py @@ -0,0 +1,141 @@ +""" +Batch upscale images using nearest neighbor interpolation (preserve hard pixel edges, no blur). + +Usage: + python upscale_nearest.py --scale 8 + python upscale_nearest.py --size 128 + + --scale N Upscale by factor N, e.g., 16x16 image --scale 8 -> 128x128 + --size N Directly specify the target side length (square), e.g., --size 128 + (--scale and --size are mutually exclusive; --size takes precedence) + +Dependencies: + pip install pillow +""" + +import argparse +import os +import sys +from pathlib import Path + +from loguru import logger +from PIL import Image + +TEXTURE_PATH = "assets/texture/block" +TEMP_PATH = "pyout" + +work_path = Path(__file__).parent.parent + +input_path = work_path / TEXTURE_PATH +output_path = work_path / TEMP_PATH +skip_already_output = True + + +def upscale_image( + in_path: str, out_path: str, scale: int | None, target_size: int | None +) -> bool: + img = Image.open(in_path) + w, h = img.size + + if target_size is not None: + new_w, new_h = target_size, target_size + else: + if scale is None: + raise ValueError("Either target_size or scale must be provided") + new_w, new_h = w * scale, h * scale + + if skip_already_output: + out = Path(out_path) + if out.is_file(): + return False + if w > new_w: + logger.warning(f"w: {w} is greater than target: {new_w}") + if h > new_h: + logger.warning(f"h: {h} is greater than target: {new_h}") + # NEAREST ensures crisp pixel boundaries without any blurring/interpolation artifacts + + out = img.resize((new_w, new_h), Image.NEAREST) # type: ignore + out.save(out_path) + return True + + +def process_folder( + scale: int | None, + target_size: int | None, + exts: tuple[str, ...], +) -> None: + + os.makedirs(output_path, exist_ok=True) + + processed = 0 + + for root, _, files in os.walk(input_path): + for fname in files: + if not fname.lower().endswith(exts): + continue + + in_file = os.path.join(root, fname) + + rel_path = os.path.relpath(root, input_path) + out_dir = os.path.join(output_path, rel_path) + os.makedirs(out_dir, exist_ok=True) + + out_file = os.path.join(out_dir, fname) + + ans = upscale_image(in_file, out_file, scale, target_size) + processed += 1 + if ans: + print(f"{os.path.relpath(in_file, input_path)} -> upscaled") + + if processed == 0: + print( + f"No images with extensions {exts} found in {input_path}.", + file=sys.stderr, + ) + else: + print(f"\nDone, processed {processed} images, output to {output_path}") + + +def main(): + parser = argparse.ArgumentParser( + description="Batch nearest neighbor upscale (keep hard pixel edges)" + ) + parser.add_argument("input_dir", nargs="?", default=None, help="Input image folder") + parser.add_argument( + "output_dir", nargs="?", default=None, help="Output image folder" + ) + parser.add_argument( + "--scale", type=int, default=None, help="Upscale factor, e.g., 8" + ) + parser.add_argument( + "--size", type=int, default=None, help="Target side length (square), e.g., 128" + ) + parser.add_argument( + "--ext", + default=".png,.jpg,.jpeg,.bmp", + help="File extensions to process, comma-separated (default: .png,.jpg,.jpeg,.bmp)", + ) + parser.add_argument( + "--noskip", action="store_true", help="Not skip the file if it is esisted" + ) + args = parser.parse_args() + + if args.scale is None and args.size is None: + print("Either --scale or --size must be specified.", file=sys.stderr) + sys.exit(1) + + exts = tuple(e.strip().lower() for e in args.ext.split(",")) + global input_path + global output_path + global skip_already_output + if args.input_dir: + input_path = Path(args.input_dir) + if args.output_dir: + output_path = Path(args.output_dir) + if args.noskip: + skip_already_output = False + process_folder(args.scale, args.size, exts) + + +if __name__ == "__main__": + main() diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index be84f9b..f765419 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -617,6 +617,10 @@ void DevPanel::show_shader_tab_item() { static const char* samples[] = {"8", "16", "32"}; if (ImGui::BeginTabItem("shader")) { ImGui::Checkbox("Shader", &m_app.renderer().shader_on()); + ImGui::SameLine(); + ImGui::Checkbox("PBR", &m_app.renderer().pbr()); + ImGui::SameLine(); + ImGui::Checkbox("Flip Y", &m_app.renderer().flip_y()); if (ImGui::SliderFloat("AmbientStrength", &m_app.renderer().ambient_strength(), 0.0f, 0.35f)) diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 2d31253..691cd91 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -383,7 +383,10 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) { NORMALS[face][i][0], NORMALS[face][i][1], NORMALS[face][i][2], - BlockManager::roughness(cur_id) + BlockManager::roughness(cur_id), + TANGENTS[face][i][0], + TANGENTS[face][i][1], + TANGENTS[face][i][2] }; if (BlockManager::is_transparent(cur_id)) { @@ -441,7 +444,10 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z, CROSS_NORMALS[face][i][0], CROSS_NORMALS[face][i][1], CROSS_NORMALS[face][i][2], - BlockManager::roughness(id) + BlockManager::roughness(id), + CROSS_TANGENTS[face][i][0], + CROSS_TANGENTS[face][i][1], + CROSS_TANGENTS[face][i][2] }; m_vertex_data[1].m_vertices.emplace_back(vex); diff --git a/src/gameplay/vertex_data.cpp b/src/gameplay/vertex_data.cpp index 87ae597..03df63a 100644 --- a/src/gameplay/vertex_data.cpp +++ b/src/gameplay/vertex_data.cpp @@ -53,12 +53,14 @@ void VertexData::upload() { (void*)offsetof(Vertex3D, nx)); glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), (void*)offsetof(Vertex3D, roughness)); + glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, tx)); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); glEnableVertexAttribArray(4); - + glEnableVertexAttribArray(5); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); } diff --git a/src/renderer.cpp b/src/renderer.cpp index dea33b7..b10d611 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -677,7 +677,7 @@ void Renderer::render_world() { float dist2d = glm::distance(camera_pos_xz, center_xz); if (dist2d <= CROSS_PLANE_DISTANCE * 16) { glBindTexture(GL_TEXTURE_2D_ARRAY, - m_texture_manager.get_texture_array()); + m_texture_manager.get_cross_plane_array()); glBindVertexArray(snapshot.cross_vao); glDrawArrays(GL_TRIANGLES, 0, @@ -694,7 +694,6 @@ void Renderer::render_world() { } } } - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glCullFace(GL_BACK); @@ -702,16 +701,12 @@ void Renderer::render_world() { const auto& normal_block_shader = get_shader("normal_block"); normal_block_shader.use(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); - glActiveTexture(GL_TEXTURE1); - m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); m_v_mat = m_camera.get_camera_lookat(); m_mv_mat = m_v_mat * m_m_mat; m_norm_mat = glm::transpose(glm::inverse(m_mv_mat)); glm::vec3 light_dir_view = glm::normalize(glm::mat3(m_v_mat) * lightdir); - + normal_block_shader.set_loc("model_matrix", m_m_mat); normal_block_shader.set_loc("mv_matrix", m_mv_mat); normal_block_shader.set_loc("proj_matrix", m_p_mat); normal_block_shader.set_loc("norm_matrix", m_norm_mat); @@ -732,7 +727,7 @@ void Renderer::render_world() { normal_block_shader.set_loc("samples", m_samples); normal_block_shader.set_loc("specularStrength", m_specular_strength); normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos()); - + normal_block_shader.set_loc("flipY", m_flip_y); m_mvp_mat = m_p_mat * m_mv_mat; auto& m_planes = m_world.planes(); @@ -741,12 +736,18 @@ void Renderer::render_world() { int rendered_sum = 0; glEnable(GL_DEPTH_TEST); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array()); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_pbr_texture()); + normal_block_shader.set_loc("enablePBR", m_pbr); for (const auto& snapshot : m_render_snapshots) { if (Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, m_planes)) { - glBindTexture(GL_TEXTURE_2D_ARRAY, - m_texture_manager.get_texture_array()); glBindVertexArray(snapshot.normal_vao); @@ -755,8 +756,24 @@ void Renderer::render_world() { rendered_sum++; } } - // cross_plane and discard + // discard + for (const auto& snapshot : m_render_snapshots) { + if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, + m_planes)) { + continue; + } + if (snapshot.normal_discard_vertices_count != 0) { + glBindVertexArray(snapshot.normal_discard_vao); + glDrawArrays(GL_TRIANGLES, 0, + snapshot.normal_discard_vertices_count); + } + } + // cross_plane + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_cross_plane_array()); + normal_block_shader.set_loc("enablePBR", false); for (const auto& snapshot : m_render_snapshots) { if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, m_planes)) { @@ -767,22 +784,11 @@ void Renderer::render_world() { glm::vec2 center_xz{snapshot.center.x, snapshot.center.z}; float dist2d = glm::distance(camera_pos_xz, center_xz); if (dist2d <= CROSS_PLANE_DISTANCE * 16) { - glBindTexture(GL_TEXTURE_2D_ARRAY, - m_texture_manager.get_cross_plane_array()); - glBindVertexArray(snapshot.cross_vao); glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count); } } - if (snapshot.normal_discard_vertices_count != 0) { - glBindTexture(GL_TEXTURE_2D_ARRAY, - m_texture_manager.get_texture_array()); - glBindVertexArray(snapshot.normal_discard_vao); - - glDrawArrays(GL_TRIANGLES, 0, - snapshot.normal_discard_vertices_count); - } } // copy depth buffer @@ -830,6 +836,7 @@ void Renderer::render_world() { set_accum_loc(accum_shader); glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array()); for (const auto& snapshot : m_render_snapshots) { if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, m_planes)) { @@ -837,8 +844,7 @@ void Renderer::render_world() { } if (snapshot.normal_blend_vertices_count != 0) { - glBindTexture(GL_TEXTURE_2D_ARRAY, - m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.normal_blend_vao); glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count); @@ -879,6 +885,7 @@ void Renderer::render_world() { glBindTexture(GL_TEXTURE_2D, m_screen_depth_texture); glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array()); for (const auto& snapshot : m_render_snapshots) { if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, m_planes)) { @@ -886,8 +893,7 @@ void Renderer::render_world() { } if (snapshot.water_vertices_count != 0) { - glBindTexture(GL_TEXTURE_2D_ARRAY, - m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.water_vao); glDrawArrays(GL_TRIANGLES, 0, snapshot.water_vertices_count); @@ -896,9 +902,10 @@ void Renderer::render_world() { auto& composite_shader = get_shader("composite"); glDisable(GL_BLEND); + composite_shader.use(); - glUniform1i(composite_shader.loc("u_accumTex"), 0); - glUniform1i(composite_shader.loc("u_revealTex"), 1); + composite_shader.set_loc("u_accumTex", 0); + composite_shader.set_loc("u_revealTex", 1); glDisable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glEnable(GL_BLEND); @@ -977,6 +984,8 @@ bool& Renderer::discard_transparent() { return m_discard_tranparent; } bool& Renderer::shader_on() { return m_shader_on; } bool& Renderer::water_perturb() { return m_water_perturb; } bool& Renderer::water_depth_fade() { return m_water_depth_fade; } +bool& Renderer::pbr() { return m_pbr; } +bool& Renderer::flip_y() { return m_flip_y; } int& Renderer::shadow_mode() { return m_shadow_mode; } int& Renderer::light_cull_face() { return m_light_cull_face; } int& Renderer::light_size_uv() { return m_light_size_uv; } diff --git a/src/texture_manager.cpp b/src/texture_manager.cpp index 915d946..8dffe03 100644 --- a/src/texture_manager.cpp +++ b/src/texture_manager.cpp @@ -7,6 +7,28 @@ #include "Cubed/tools/log.hpp" #include "Cubed/tools/shader_tools.hpp" +namespace { +constexpr int BLOCK_SIZE = 16; +constexpr int BLOCK_NORMAL_SIZE = 128; +constexpr int CROSS_PLANE_SIZE = 16; +constexpr int BLOCK_ITEM_SIZE = 16; +constexpr int UI_SIZE = 16; +constexpr int BLOCK_STATUS_SIZE = 16; + +unsigned char* generate_flat_normal_map(int width = BLOCK_NORMAL_SIZE, + int height = BLOCK_NORMAL_SIZE) { + unsigned char* data = new unsigned char[width * height * 4]; + for (int i = 0; i < width * height; i++) { + data[i * 4 + 0] = 128; // R -> X = 0 + data[i * 4 + 1] = 128; // G -> Y = 0 + data[i * 4 + 2] = 255; // B -> Z = 1 + data[i * 4 + 3] = 255; // A + } + return data; +} + +} // namespace + namespace Cubed { TextureManager::TextureManager() {} @@ -17,6 +39,7 @@ void TextureManager::delet_texture() { glDeleteTextures(1, &m_texture_array); glDeleteTextures(1, &m_block_status_array); glDeleteTextures(1, &m_cross_plane_array); + glDeleteBuffers(1, &m_pbr_texture_array); for (auto& id : m_item_textures) { glDeleteTextures(1, &id); } @@ -34,6 +57,8 @@ GLuint TextureManager::get_cross_plane_array() const { } GLuint TextureManager::get_ui_array() const { return m_ui_array; } +GLuint TextureManager::get_pbr_texture() const { return m_pbr_texture_array; } + const std::vector& TextureManager::item_textures() const { return m_item_textures; } @@ -45,8 +70,9 @@ void TextureManager::load_block_status(unsigned id) { unsigned char* image_data = nullptr; image_data = (Tools::load_image_data(path)); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, 16, 16, 1, GL_RGBA, - GL_UNSIGNED_BYTE, image_data); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, BLOCK_STATUS_SIZE, + BLOCK_STATUS_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, + image_data); Tools::delete_image_data(image_data); } @@ -76,8 +102,9 @@ void TextureManager::load_block_texture(unsigned id) { glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); Tools::check_opengl_error(); for (int i = 0; i < 6; i++) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, 16, 16, 1, - GL_RGBA, GL_UNSIGNED_BYTE, image_data[i]); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, BLOCK_SIZE, + BLOCK_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, + image_data[i]); Tools::check_opengl_error(); Tools::delete_image_data(image_data[i]); } @@ -94,8 +121,8 @@ void TextureManager::load_block_item_texture(unsigned id) { GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA, - GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BLOCK_ITEM_SIZE, BLOCK_ITEM_SIZE, + 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, @@ -115,8 +142,8 @@ void TextureManager::load_cross_plane_texture(unsigned id) { unsigned char* image_data = Tools::load_image_data(path); glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, - BlockManager::cross_plane_index(id), 16, 16, 1, GL_RGBA, - GL_UNSIGNED_BYTE, image_data); + BlockManager::cross_plane_index(id), CROSS_PLANE_SIZE, + CROSS_PLANE_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data); Tools::delete_image_data(image_data); } @@ -127,27 +154,74 @@ void TextureManager::load_ui_texture(unsigned id) { unsigned char* image_data = nullptr; image_data = (Tools::load_image_data(path)); glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, 16, 16, 1, GL_RGBA, - GL_UNSIGNED_BYTE, image_data); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, UI_SIZE, UI_SIZE, 1, + GL_RGBA, GL_UNSIGNED_BYTE, image_data); Tools::delete_image_data(image_data); } +void TextureManager::load_pbr_texture(unsigned id) { + + if (id == 0) { + return; + } + + if (BlockManager::is_cross_plane(id)) { + + return; + } + + std::string path = "normal/block/" + BlockManager::name_form_id(id); + + unsigned char* image_data[6]; + + image_data[0] = (Tools::load_image_data(path + "/front_n.png", false)); + image_data[1] = (Tools::load_image_data(path + "/right_n.png", false)); + image_data[2] = (Tools::load_image_data(path + "/back_n.png", false)); + image_data[3] = (Tools::load_image_data(path + "/left_n.png", false)); + image_data[4] = (Tools::load_image_data(path + "/top_n.png", false)); + image_data[5] = (Tools::load_image_data(path + "/base_n.png", false)); + + glBindTexture(GL_TEXTURE_2D_ARRAY, m_pbr_texture_array); + for (int i = 0; i < 6; i++) { + unsigned char* data = image_data[i]; + bool is_fallback = false; + if (!data) { + is_fallback = true; + data = generate_flat_normal_map(); + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, + BLOCK_NORMAL_SIZE, BLOCK_NORMAL_SIZE, 1, GL_RGBA, + GL_UNSIGNED_BYTE, data); + if (is_fallback) { + delete[] data; + } else { + Tools::delete_image_data(image_data[i]); + } + } +} + void TextureManager::init_block() { glGenTextures(1, &m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, BLOCK_SIZE, BLOCK_SIZE, BlockManager::sums() * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glGenTextures(1, &m_cross_plane_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, - BlockManager::cross_plane_sum(), 0, GL_RGBA, GL_UNSIGNED_BYTE, - nullptr); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, CROSS_PLANE_SIZE, + CROSS_PLANE_SIZE, BlockManager::cross_plane_sum(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + glGenTextures(1, &m_pbr_texture_array); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_pbr_texture_array); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, BLOCK_NORMAL_SIZE, + BLOCK_NORMAL_SIZE, BlockManager::sums() * 6, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); for (unsigned i = 0; i < BlockManager::sums(); i++) { load_block_texture(i); load_block_item_texture(i); + load_pbr_texture(i); } glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); @@ -172,13 +246,25 @@ void TextureManager::init_block() { static_cast(m_aniso)); } + glBindTexture(GL_TEXTURE_2D_ARRAY, m_pbr_texture_array); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + if (m_aniso >= 1) { + glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, + static_cast(m_aniso)); + } + Logger::info("Block Texture Load Success"); } void TextureManager::init_ui() { glGenTextures(1, &m_ui_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, MAX_UI_NUM, 0, - GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, UI_SIZE, UI_SIZE, MAX_UI_NUM, + 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); for (int i = 0; i < MAX_UI_NUM; i++) { load_ui_texture(i); } @@ -190,8 +276,9 @@ void TextureManager::init_ui() { void TextureManager::init_block_status() { glGenTextures(1, &m_block_status_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, MAX_BLOCK_STATUS, 0, - GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, BLOCK_STATUS_SIZE, + BLOCK_STATUS_SIZE, MAX_BLOCK_STATUS, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); for (int i = 0; i < MAX_BLOCK_STATUS; i++) { load_block_status(i); } diff --git a/src/tools/shader_tools.cpp b/src/tools/shader_tools.cpp index 0fdf0fc..d5873d7 100644 --- a/src/tools/shader_tools.cpp +++ b/src/tools/shader_tools.cpp @@ -118,16 +118,31 @@ std::string read_shader_source(const std::string& file_path) { return content; } -void delete_image_data(unsigned char* data) { SOIL_free_image_data(data); } +void delete_image_data(unsigned char* data) { + if (data == nullptr) { + return; + } + SOIL_free_image_data(data); +} -unsigned char* load_image_data(const std::string& tex_image_path) { +unsigned char* load_image_data(const std::string& tex_image_path, + bool check_exist) { fs::path path = ASSETS_PATH + tex_image_path; - ASSERT_MSG(fs::is_regular_file(path), path.c_str()); + if (check_exist) { + ASSERT_MSG(fs::is_regular_file(path), path.c_str()); + } unsigned char* data = nullptr; int width, height, channels; - data = SOIL_load_image(path.string().c_str(), &width, &height, &channels, - SOIL_LOAD_AUTO); - ASSERT_MSG(data, "Could not load texture" + path.string()); + data = + SOIL_load_image(path.string().c_str(), &width, &height, &channels, + SOIL_LOAD_RGBA); // Materials are all RGBA; must force + // RGBA, otherwise sampling will fail. + if (check_exist) { + if (!data) { + ASSERT_MSG(data, "Could not load texture" + path.string()); + std::abort(); + } + } return data; } diff --git a/uv.lock b/uv.lock index e82cdf7..683467c 100644 --- a/uv.lock +++ b/uv.lock @@ -17,6 +17,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "loguru" }, + { name = "pillow" }, { name = "pytomlpp" }, ] @@ -28,6 +29,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "loguru", specifier = ">=0.7.3" }, + { name = "pillow", specifier = ">=12.2.0" }, { name = "pytomlpp", specifier = ">=1.1.0" }, ] @@ -47,6 +49,39 @@ wheels = [ { url = "https://mirrors.ustc.edu.cn/pypi/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, ] +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://mirrors.ustc.edu.cn/pypi/simple" } +sdist = { url = "https://mirrors.ustc.edu.cn/pypi/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://mirrors.ustc.edu.cn/pypi/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://mirrors.ustc.edu.cn/pypi/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + [[package]] name = "pytomlpp" version = "1.1.0"