From 0d522685b787c53a48ae6810d56b3a1df3ca39d8 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 26 Apr 2021 16:06:33 +0200 Subject: [PATCH 01/53] Test --- .gitignore | 5 ++ .idea/jeu-de-combat.iml | 9 ++++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 +++ src/HelloWorld.java | 115 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/jeu-de-combat.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 src/HelloWorld.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a49887 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/.idea/ +/.gitignore +/jeu-de-combat.iml +/out/ +/lwjgl-release-3.2.3-custom/ diff --git a/.idea/jeu-de-combat.iml b/.idea/jeu-de-combat.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/jeu-de-combat.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..90e6b95 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/HelloWorld.java b/src/HelloWorld.java new file mode 100644 index 0000000..284f611 --- /dev/null +++ b/src/HelloWorld.java @@ -0,0 +1,115 @@ +import org.lwjgl.*; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; +import org.lwjgl.system.*; + +import java.nio.*; + +import static org.lwjgl.glfw.Callbacks.*; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryStack.*; +import static org.lwjgl.system.MemoryUtil.*; + +public class HelloWorld { + + // The window handle + private long window; + + public void run() { + System.out.println("Hello LWJGL " + Version.getVersion() + "!"); + + init(); + loop(); + + // Free the window callbacks and destroy the window + glfwFreeCallbacks(window); + glfwDestroyWindow(window); + + // Terminate GLFW and free the error callback + glfwTerminate(); + glfwSetErrorCallback(null).free(); + } + + private void init() { + // Setup an error callback. The default implementation + // will print the error message in System.err. + GLFWErrorCallback.createPrint(System.err).set(); + + // Initialize GLFW. Most GLFW functions will not work before doing this. + if ( !glfwInit() ) + throw new IllegalStateException("Unable to initialize GLFW"); + + // Configure GLFW + glfwDefaultWindowHints(); // optional, the current window hints are already the default + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable + + // Create the window + window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL); + if ( window == NULL ) + throw new RuntimeException("Failed to create the GLFW window"); + + // Setup a key callback. It will be called every time a key is pressed, repeated or released. + glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { + if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) + glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop + }); + + // Get the thread stack and push a new frame + try ( MemoryStack stack = stackPush() ) { + IntBuffer pWidth = stack.mallocInt(1); // int* + IntBuffer pHeight = stack.mallocInt(1); // int* + + // Get the window size passed to glfwCreateWindow + glfwGetWindowSize(window, pWidth, pHeight); + + // Get the resolution of the primary monitor + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + // Center the window + glfwSetWindowPos( + window, + (vidmode.width() - pWidth.get(0)) / 2, + (vidmode.height() - pHeight.get(0)) / 2 + ); + } // the stack frame is popped automatically + + // Make the OpenGL context current + glfwMakeContextCurrent(window); + // Enable v-sync + glfwSwapInterval(1); + + // Make the window visible + glfwShowWindow(window); + } + + private void loop() { + // This line is critical for LWJGL's interoperation with GLFW's + // OpenGL context, or any context that is managed externally. + // LWJGL detects the context that is current in the current thread, + // creates the GLCapabilities instance and makes the OpenGL + // bindings available for use. + GL.createCapabilities(); + + // Set the clear color + glClearColor(1.0f, 0.0f, 0.0f, 0.0f); + + // Run the rendering loop until the user has attempted to close + // the window or has pressed the ESCAPE key. + while ( !glfwWindowShouldClose(window) ) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer + + glfwSwapBuffers(window); // swap the color buffers + + // Poll for window events. The key callback above will only be + // invoked during this call. + glfwPollEvents(); + } + } + + public static void main(String[] args) { + new HelloWorld().run(); + } + +} \ No newline at end of file From 4e88d1d1a4eabe4d057db8ced48283ea748142b4 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 13 May 2021 16:30:18 +0200 Subject: [PATCH 02/53] Stock --- res/bg.jpeg | Bin 0 -> 24744 bytes res/bird.png | Bin 0 -> 65913 bytes res/pipe.png | Bin 0 -> 69674 bytes shaders/bg.frag | 14 ++++++++ shaders/bg.vert | 16 +++++++++ src/BufferUtils.java | 32 +++++++++++++++++ src/FileUtils.java | 27 +++++++++++++++ src/HelloWorld.java | 2 +- src/Input.java | 13 +++++++ src/Level.java | 40 ++++++++++++++++++++++ src/Matrix4f.java | 80 +++++++++++++++++++++++++++++++++++++++++++ src/Shader.java | 73 +++++++++++++++++++++++++++++++++++++++ src/ShaderUtils.java | 57 ++++++++++++++++++++++++++++++ src/Texture.java | 57 ++++++++++++++++++++++++++++++ src/Vector3f.java | 17 +++++++++ src/VertexArray.java | 72 ++++++++++++++++++++++++++++++++++++++ 16 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 res/bg.jpeg create mode 100644 res/bird.png create mode 100644 res/pipe.png create mode 100644 shaders/bg.frag create mode 100644 shaders/bg.vert create mode 100644 src/BufferUtils.java create mode 100644 src/FileUtils.java create mode 100644 src/Input.java create mode 100644 src/Level.java create mode 100644 src/Matrix4f.java create mode 100644 src/Shader.java create mode 100644 src/ShaderUtils.java create mode 100644 src/Texture.java create mode 100644 src/Vector3f.java create mode 100644 src/VertexArray.java diff --git a/res/bg.jpeg b/res/bg.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..af5a79d2cfa54d871f634ab8244899d0827ffbbb GIT binary patch literal 24744 zcmeFY2T)V(*Do5oV!;9^Aov%h3kcGiuc#m(K?ofJ(o5*Q^S(+^>MI105_l;ggpfpq z5FjKdRk{$8NC}|`NJ8kH0GI!F=DTO^opbKonRDjO+?g+XCi|KF?7h~r*IxTszx8|8 zKKgky3Ak#YtFH?ngUoo5BGEf0E~wr_otrN!^AjuPO z`ajwLfY|XK066u}iT~dF&&mJnJ#qV=Q~%jM`5z}9jRBtiThIT}@83fHmjwL#;-jyC zYv)e_PTW3q;u_%OwG*eVoj7U(2p`|x$y5I!{6EHf{CD=;=`-gqoH}um>$uV|Kx^RK({FPJ3wg8+ybK)$|scScQPoCqudtdRosQmeRN>84dm^lWBh&^)o>qE)s zm75C6PoJ9yCwv>c1+uV=&#A$w=(+}mBxaSv>xL=RDJM5*+p%<^;|Fl!%(3?W)P1b| z#HrI~&Ye7e;kf(B@jy?UJbmiS+2f-p&i`Bc@nEits*#6nE?~`HTMU%#KI)^Hub<)i6VN!Z7a~Qrtst8J**Lnlv45Csz3ij@R|szNH=r>} z<4;po)oy8B%$=?`cw_g#jDL&1h^T3>Rq6V~^>1azh``AAgssY#*F2Ga2+W^=2=seI z0;lTX7D9&SpeJHvkG*C&!G0hs$46k|?n$8SYBRDIakE(4Q^LF}KclD}PL3DpmB23X zFP5QWWh~z$oB=$H?)Y#sAk#?FUl)?drcd;5F@VKa!x06J0j7{qq>~hVrF=%%clftIB7-r5pp&_!vxr=(QhgJ?I-w-n&HFM^B_1lAgL?(}eF^S+Xv zdhV)Dz$6~*IvX4NXZ5TFQ(q%+ZyBK-cWM-2eB-9+p=hHeqnLUGkWoB`mX_8M*yF@D z#;C~XmSqJGH5x^*W25$1tALGT%gXsgLe@@dQH*$>tx9dpQfagJjM;8mKg!l^cKrpZ9jxpks=C{6CwWNz2^kOyaO(>#1-~zJGhd{Lct2ds=t`Vh* z49LU)27egBhn$wwuW!kEYlP||QVWj&eS2vB>2mppwN-ukey%>Np5@eZ#w zOD1P;F?YaN5394`tj-)B+Fi!%jrrw#Er*4xT3Chbrjxw_Vg<`z+jsep=-1#2o1?pl2`iI#6A6|FFbNL2Osa8)j+1u zh){|9)jNeZWj4R;FW~lGwHU|%3t2N^$!`iawtdll)04OdAp%BWA5*m~F_Z;aKih!V z18vJnS1aZh-*9guHNpx)2Q@v>V>O}`>^J|~q2&GB6g1c0XaT%Mie0S@VjEs3kD98Q zs!~_2G`?t2(DZtGww*B1O`G?RioeURexpf)7BjZ~V^#fU)#`H96P+|IjXot(?w55- z+V+{K-uw5N`YKAnOmI1V!!}|!BHIqAsVE)Legv3W2``9T2ihG9=RUB$XtsV>GtIsr z;4q+Iqd7Ehq1zqZ`mkr__QD|VbJvq;3o8C&GfgS(VC?rVNoq7K7lr5k&b|9M!{=p? zoctTNGd|HP+2=0#f4tC=k{O5+%S~h@>l^g5O2g<8LI@5(u^ETa zzN12?ji#v`n3bOEsZvHA{XNmN3rphOIgMIrvIrR;NC=N9I+T`1-^XHd=Y5jLthCzI zzvislQl(#P}AQo#mT#8WewD-wUPrf zQ$bL8gg%_NCnW?cjj^}~F69Wb&zkeCr9SP{kuvpbOm*NQ92ha4hdHNSK_|6MvP={2 zT~rUbYb`F(;l+`q4zU+E*J$YarF&;m5f%X*#n9=_w7iIRXfdycKKm za0~}aNP;R0@=bl|YINzn-Cb5x?=!)Fm|^C3>W9OZu*9JW>iWqX@r$P1tx`7DJMJubI{`TJA(0lgIumv-O+bX9OnS`mQ7u8Xxj|gNBMa z0)(D}srmGfKP;F>(~A*1XHld zT~UAr*(`Qs^OoZ7A=6#1^>{8GGASuCs6KYAZWFYTX|+Ub2+k`Ne(Fen`QLYT7W zgVDe=OXNoElb?l2_v1ZYKUAEzpHQ!%XgHX6#&|QBezEWjA)(6>U?HJft(2M7*!3sI zT73(~DVI}Gy5&BtVUk{r^!0VJ!~45MPoVvUW$(AbL7uB1tMIz*#_3ew#<*-OAL~?o z(UXt01@}Z$^(<@i(ml^tL=`*=uT18;Sh}nDRHp2!iQoqY_?*ycya`ODhLOzQ0j87t zZ`PS)rScT$v*3%EZR%cxjN&gX6zAUepgc1a^toddT+N`|+{h-4G6T;=h(k0KBi^eR zkWXiFrlkAM2jb_tRptDUIwZ;U(~{#4)@il^ zq}f-9iRMUKjMt&sor@m&+hV>^XOSg36#ucB)=Kb0P`Up zA&$5N$~pq<5|@H4ko)<#ztEZqbA1o2UEHgC7&(sw;kRvBi<&2jMM6PVzZuYuha}Bs zcA9sh@1ADg^xap2>Kb#Ks;1f)xAH56=Fp5VbvwSnnC`<{TL`U8D6&b`q#AA!kim=J zcJ*9p3?g;v9svMCa`Vo5dud@coPb?Ur)tKGk?eM2(Nn2SGs@hmuQ1QH{sLYm{e$I- z{%}&jQw=l8G0Vkdpiv+xld>>S6%#RF3+}V|M)8eY)h~Lnm=~(M2m*8bxn zbg0p3UtfMR4K)ur(&cTTQ{o)m#jRi(?~6%hc${b*IVac7uVr!1p{q8ih#Y%!D@$a0 z%WB|Sf=xpuMqJQq$DF>q5Q;A8Px>Rl&kG>{HS?IF7sp)a@bc7Hi?ruxTMo}DAv-Th zf(-m_R=ab3DD>Xl@5$Qvc?oPy%Qw4w>xsu~c=^y^v#c*C!^&0nY5+=7J#$2HENtI1 z`2X!Bo<1*xTI|<5U1Rr5m25fXYxMYq*F-_kFMS2y*3cU5tRj=7O5{ja0OpLSyG!9Q zr15bmmKM?K7C21IFnq1JFUOI}n%OkE$(Sb0Dk;WY*ARNw7IrI|mO(Oj2+V#_&W4Xy zXA&6my&udt581P~@3lE8V6IFr`B=ckWo(IMzc9KX@<_ELJM!f+XSX7o@01c}{!Ewj zWYHfky;qwg+=(TISqk`DXwx^6cTYzR>SYA1(qAsu-HPpTbbW`gDH5gFndKL{EThsW z3?ZtBeeBVEJ!pJSA(d&!eGWc36zXRj z+P^QO?m+&1*mZa-H0WPbNOgJaN8N#BFXIkeb23XW1l}A}wv?2ANAXIG@xwOLN~Rh1 z9a?Pp2w-1oIA9&4E_AZOAu)3@JX%Zbel>;5xaWXWJg9F(?9czFsF>CVdWov|%~{a5 zZ`RI#W%f!0_?M>S*qvA9+mawe{JrOQw6j+k7ji6jk_sjT;7Ns59oYrbSr&9cfJPk~ z#R`2F>$ejy!l466k1qqwSy4DAco_v;i6dp$6GA50ueW(vC+$*zZBqNCPkh`Uw?tjC zMFvm*Fy^_I9`Xp&>%}x%3z$UuHK%ECyEXGZ02ma@hCc$2-b#0$?b;^z~na}qN1s7{dLMH zKWhVv>99x?qKrm%W?nkWXI39a9)9>!;<+y88ey=4PUFLD`=QRt$f{p$Z1?v)=$F}? zD|T3OKut(uB*O4>`L+&Vtvinwg(dqFZ15PEr}&-gO-h7gOj<;Wj2IIOY%_qBSSa3e z5AVJPqbY^fF09VSL^L=0nSG|lzbO;TT}QGr&MGAI=h>fi+WJ=fy8}5WXprC0u^%H1 z+cQZ}RM>ga8XbAlkzo9;n_vPga{UOj2K(M>c{J@M%+sK4REybTMcu})%6;L9dOBk8 zxqFQn*!QqIDEozyfvIn_5z1Oc0`Y4X0*TNU@hXj_9&>Ll)^VC$BI#F%UL7>Ok56J9 zrVxq&pfex-DBaWgQK9{zkKizZ8EE!wo0+lYwkgO~MRgCn*_S5Nv9}RDu%bmd&ziP{ zp`Q7Kc1JINV2D7Z+6sQ=OLkcdUsEIO{{q``2UaUE0-!BTl4_%!DV`@=rG-vqRrEx` zJY!GpKTWC&O=-;1uYVvq8rpn$dT}%EX4P+t#;;2f)YA>w-Z}$~_xUW`K3BLv*CNBk zlP^wgK0+Tm9?#RazmTNu)P()mSjKG*YeJ0X7|#WL-D?bS5ykK2$M9k6X_TUjen z8eP8`VwLMJ%Ev;}2XEfGB>xI=0jyBwdk}GVdddCym8$^m$Oy zFK^U-ePfwDwyrn-;=w;p&Pcp6bNy|mS~-0?Tg0#%@d}$_yQnzQ>G{dp$?sZfyroij zGdMrWVcIXSsCS-!vGmg|b-XVvESV97a~&9rTnLRj%{~TDDtiu8-)Klx!?O#89~VAbu?n$ z#Q}}SY2Q&&N|NR&Szh)RY?k$^^p&DU`$s*Ulr74g=0*#Buu7E84@rg+X)h$ZATnS& zDZlvl*Bu>GD1C06VXY#**|7KMmX7c*XYPZ~CI^`(m#751;li4hPu1Y&nNzfwn2
e?;4nC)^^?(IfZVws#> z$;ot~uHEbXuIllHxh{^)pOWXa@Qa7W$y;_nbzaC4)O-Usv_e!2BW>FtkSg7AmkH*u z$;ZQ=!qp@o<>Pk;k~T-ivN=Q>BiKOX%NJ+K#E#D2f-;TpYTc$`gg`$_($W}gsPHaON^$0v?x?QhbKAjFUCf@+ z-G6~wR_IgE*;`SA0npNA(gI>RuzArM%ax{SHYETpPBbtvCA*gy$H;KXlJ_g3@_OUh)Y0r%}`U<;2LL#56>9z9uZ~ z0@y$w?=oiFV@c~n*(lZr_ZFV8T@B~F z# zRL7f5PEgRU^{Jk)ut*Ztay_yrC%NbOpa0AGRU!aryF*KNn_fqU4rB(Vl9nmDt8 z^JFPJIh(i2AP6k3rXLLtwoQ;S8w*|ly^X5Zk@uq|NWbYD9jT#b(f?&`piftZEk~^r zj{q@|XR`*&X}TCw8|HA<*~VMJS5<8m6nkZ?e&mV?zoG0t52R*u3jy=`^6o+TL_OZv zQUf2d&az6*GgKC7z5y`>?LXR_+)asG%ZXuGxtGk#DEn0lo<8*cn9(CZE9acIf5`74 zIG0tSrmMPXyje7mOe$1~<%4b616e5{;^QIm4?g;gE42)i|7@WMN?7#_+P@EWOZ|4S zCGL`8)^lgsS5-31{kvy=q+Pmq%Ys3PGTJG5GP&Rw$gj&Aa1~fHO{|@c-K=JZKu{X! z!Ong09h2llWwiZ}>|UY(yt4g%mQgJ(@sLT}vtAm|E#K7ve-Pc;wKww{@^R|wtukV%^j zlvC~+`nC79(W*dj-OW*|_dsUUdXyq4?wef0{_Yw0foEad z@-JNMeRoe$HVv<#w=bf=xwDuRmLq71J+Q`>ei%$rj?~b)qgST>lpLcKe8zk4aTeg8 zlUtO);Zq_-_paGIEHHqs45UBOS0OG}5O0p&h#_aIsl00)PKjS2bTCT;Aj|`~Fv^fA zKBxlmD=&fMl|B0aw_AnzEBa1F_QviT(eph1OMX43l0=!tcLDFpZaKvH?ik9`o7lqc z(AJBZ-6kbT2G>?(Wep9rWsX@=tq1RbH&^yQ=V=FMy4{gX4al}dN*cfo-Zvm*KuzWt z_GY8(kYML~bh1^GafAcURv=RB7u%OWWFv+@MoA-{kW1p@{TdkVfc(8YIo2E4E3fE7oraKb-242Tx5`h6$SNUZM3Ja=3%;zOUQ3D+r(1XIbNLVA0hE9=RljpQx4Z*jAF&@<~e|0unU!R-Y~tj-7>A5qt?@u45!8 zX?v?=oVY!1=3V6uLUf*ldcM-2)Ix3q*DEG!of_-Hme@UZH>HOxbCU(T#FoQxrZehn=u>1%B=0*()SoNOn#v4(xoIN)3ndN!yC0(RMru| z7loi9r+J2|s5fnl!@E4^U8UJ^gtxr`*Wg|zMmiP*I%{D6uxDIJ0>KrEqT2e!H51Qt z*GKw;**r`m=JtH^_f@0D>w$jla%2?Ee4F&osyk$43=Jc>C*3bXi|wkoW3z^NY9>ZQ zq&9FHB-;G%V?zI!*;b)=HU25*)3GPx@P}{?DR*?wfsFlnqJMlZX!jy|8R5{pcu|Xu zFny1xF&-R0SFsoQ+kAmk)OhmWiv@qaIzh^CuRrs>o9F%lZi0NEZNy?Ve`7WBN*QyX z;R;Jdl+V^>_|Od58$bDGjk-6TW(9IrmcQ7*yH+}Z`61H4!iVdiH|9`sU|Kb18Ap?* zza4iq0^_C)VavxZ7dnhrr3B)6p+zZ%z8so3Hv6yXrQ@d%#mK@@_Z@QHuhk{NTc3dU zX*+29W5C5_kXoKO85u#s(1Y=hC(+i^C^4jki2-c|Er;7h{bLQid(#~_kU#AvS2LYU zd{wO<3-l?6z&JZ88y7XR*6U?Qp4dV(NZ@@)nlTE@j5vL{~2(R5+)` zi5TgoSIZI(`q4hIP1^_EEHVS>GYVg1~IYHpU zVl5h7`)P6G-qOb2<0kI3roD)d{Of-3&Vy|Hfuxrymfzoh#~HFVap=HpgqH^1@H8;j zLNrnI-oAYS9`PW`;%RxfM;h;1xQwv9voXRXQnU9;RqHh|_!Ce7`F;2DGxmTP8j)wi z(vHv*m)}Rc#4Ys8cg^VeRw?gXELgwN)^k0uD!OK1E@5s=rt33*syuJybEQOmq*}Y! zY?SZjPQLJqY2e`0Xq!UWf4g3UlyOSy8K_QXluT&ip z*1Q#CiZQ6Uo-=l%ggb0WX7RwmXTrB<+b)qWtF`J;?t3Qgc!Jnnpe`e)725ET!K&G# z&AeD2;o@__qY5e45H_msbP9b7rU*FL7G?8VjtCU~z}CNnS5gax=G(Ga|2eyz7JS!q zXCcSfI!ZP{%=U6F-qM7NXLEbF?zw{!ujbscou9b$m%L0k(C*=s@Adp_1IRbA`h1}N zKK=-hRWwbow5f2k{JCms6&_%_X4_O+l>5aeKrUzQi(TGk<{@=x22%a*uIc(m^8NVI z?}%^RJmUE2e-*@TtTu-p4!Dkos@&~)bS~?`7uApEOJ!0_Ofpcie{7x9KT6C?NdlMj zeZoER+mr+$wbgVB%6Q=#Sea=O2|iPW@6^wH>Kqwm&YN!dNFZBPa%dXVSdTD@4DRw& zGg~-FEUv9rb(B|Vo&9?%;rWGQ%*ERRU)I7SYZ==xLB!r9yc#cy7*SP!0& zd}=S~KyOC(oKN+4yB=*CDWjYrqq4ty-D(KIa|B?^hY@oMlPXYL*W(w!54+QW=QnUMqS=FmrrX4a@LgLR*W65u&S?91BmkGHqjGW8&s zhY8kt2)9^oUuSPLPKHw>gbrRsT?kRn-_^*3W*wghLuZ2??13wK*u zu&j*KtXWtQM7_I}1C8!F~DG&r{DF_7`>&49*MJK8!x}KTP@*v)*wJ(s< zYCHm*b-B%=ppxK;8A4@l%=;X93X@jM9u_tXh3GX7?xB0l4Ts8Ft8lFy``=r2CTQ#} zwt$TN_<`EN$!3{ths(xkDK(KOu%MvQt^CM%$sS!L%+ z|D5Wc;jNPMkyK@^<-QFirrwR zGO%9$way9eN7c==u!t@`eSC>@?`C4(#7%Ca=Vqy&R`3Yg6w2F;AH=V{a($UU_`uwwrq5y&Dg7Yl}Wl*`jK5NYW|`*XQr@K3uARdLp0 zQiDI6@w4W3X1Zgx8oB4whP?91VT|^D^5AhY6sa8el297~d$5#I=e8+5C6HA~%x+gK z-DtFA4OnjcKI;Q{QFAZf!a`-VQ}pAHmjyCWhVPA>N_5^0OG3)iy1aSxI`1f|wCsSR zQ)+KLs0)GU3bjNdfT2Je@zyA?PgLM=k>ffiSEch+Nv^Kn4RKjD71OL}$>!nALHjWl z932?iU@l{V-@u2Ya~=`TwIrJrHOadn8E0n;dQd{ z^2VNEvwO3v*cs0z^LB^anBA645Xx!P4z>vGn)9*@f`J!hacF;)vT9UawW;%=2TO`=PVo3RBqKyGR%&%ws`4UYpTk{k1q8# zx9T08t(~C1pkAULgaF%xFf4M#YPTB2m!cQn*gI%d{B&7sPQIdTdDVD6gBISi3M*=N z_79gjC`Ov}QQqdpSV(Fp!b=VHFcad=`l)ShKfO5HrJ%;;I!4YeJ;#CP_3X{F|a zLTLH8v&pJ0>Psj+D7x2MSv?}bE{Wf!s$K8uXA=W|eHGv;m4(6kb&pb{TBdj2Mu(=3o})MaMs!2LoTmWmMg_<+en!|7@d zOa+=z@qQB!7#5y)g)RGtKcW3s3Mp9JWLlPjSZQR%`au{ves@_gE=&G2O}3^7;qG9H z_JG_-`*|<9&-k}(o@4owRX3#)-dKHecabARzf2|9gg~Yi1}?rS(+v@9B32X@rLm$` zA8W{r5^M|XQfiIQBqKFjLgZh)F|se5#-$tqynOcqCuos&MArzWWg)9freRLa_pd_tRN=e)NU%nCCQ#c*ahsUWh_&^^z3;#H_?vs+WJ`OC&2O@U zMmhTkAjDnRGV23}crZ$qf`he%HFna$dR;S&fO7aE*~gq}GZwSR-aO9YevNl0ieG|a z+lqAyXjA(~fM<3C-zI16D=u44m)&wUUjw}${xwuFo07enGkjeNvMVu-@!Mw0>&qFR^4)m?Zxe*# z5t?t{5VB9d60>~vD{LbTPsYmFV?WNSCB~$~#SPWdG%TvC7oHHLY79e3v^=p~v96_7 z>r{o-#gQUqObXQ8_ub*m5x6?8xlsm?252TkpSs_=RVR@+31edvKSwtqiwwH)@~X=aTHaH;2DOa;Li$AVIM;5^D_UvyJ4u{~HRw zV9=la`0`Mu5yL3ca_6^Bc3+lR7m5uj%M*TNSFEF7M*c_VE%(FdN|VW7%{Psw(| zyk4od6Ba-5adyYSg{Wyb6&M)6X^?@V>+G8Pd|t8y)4n>d@YmfV!F4fr5-O#YCdBt5 zeAelU))(vEVk-CLyt|f58kf6onjI(=)#OjF-=cPyCrb00^4c*MZfb?(suF?^FL>WI zQgycA%Wtg&nay}Ccy!v@?6dIS-?IcCj#xLAeP~FL+2QW5l!{gBi*%cTjBvZYEyv-4-gFGRgEay6jzpQF%tbMDSFAoV@bV zVx;#@R)_g#!`#WcCbPBJoEK^~Crwnfro&NfDt`E=Hzz$iQR63MinR)-E}%f47xZ&u^2+s)EF3NLlVrI8;E5VZ~WnINx$ z{A3@$UqPuB0eR-V>(QdO`&F%`%$2w=iN>np%lsTP9D3Ue#n2uG9^O=+Ek|<4wxGPM z7$-9LFd2W$ttEB4%m#En^6nt2x<2|^<)5_*gg+aNFIv+N+@X6wq~_ExMt`CCaZB3T3+wB{y{hy;xFTV>0tG-H*3UQk?9eP7mAoW;c zm%3ps-oZH`Z$w zoEC6)%&wx~o6p}*yI%*c{1&ilt@&B)D_e2zvTmwGfA=+W&)=D!|J5C3+xZ3|g`1aL zH3I@uOX(F<>Rx_BNDGVve)?AMWsmAI{qb7_{#6-!g7@MZmeTK2}YAE)Mu%v0?yRgsbiC%+b`Uqvq7=)mxs_*>yPiz9&d)0rhb zs>8RzVHr~+w0FZMaqnPFD#j}oKsrPpa{kcYm&ody4bC?ySiMh$GBrl%zBvcyn<32Z zB4dxbVjd8+Z>O4CRBZq3`R!tn>aG~J<2--vEFNML-MnYcjPn};w}lo9CA3pgU5z>( zLG7IW$)b!X)<*gqEc3#%rUJa6%-7-<+x`1P=EZ6Wp52y~8?@(WjGn!ywf$ePKt3A* zPPa1EI+OBhicLnWWjkm(xy`EeyQ~hca-S}*()aHIrQzsU?haN|Al-A_4keQ+^>h@h z&3=6DcK*nS4O=UA^O2~E3v&^tOmpv5jQLR&E3AFB(!`Oq9x=6eKd<)l-|2BFg{~47 zh{VuF~<5WW7e#4tPaqXC3$tg+Xpz&3Xfi0d4KQ3e*uXcCipv@AK z1j^2N5w@Rl@n!Q}7>+v-2%4WT-)xy~^0MZAKBV<)Kf;)`jmeibZ0{bs6bLfCCrc^qASw#1O+_ z`{3u?=F3ID{J#9FM5VcFZWKJQ$oU|XI%-pKx+1T+UBNOSX0yR^5K=j`}Idrt= z+7V#E5_!<9UxgZ#{b82K4_aD%WH}0x)BrnVMa7AyY8&S2Wn2T6l?e8Ybca&#eQ4YH zX4v=gvI?@)z?ZTjVXm0ldsV7ATg@M2$UpsNoM(mO#tm0KR>%55=SRMPsqeYIu49%a_}doS+n*p1vqE3d$8SB;JuuS3%z8Vv!o&rYu%S7@;Q z7J5bb^3BMFsp;oB^T!OA-{bUyU-(+eD(dEyOV1_=^$TqjTG)UYwJ4b#?F1_>5-7UK z1J_X?+NykokqdDn6O2I->Z62bD3!Zkf6q2>$1NNjJViwoKki!F;?t0{DPMorVJ@z? zU@-*BN~4I!2tvSz6$7ef)azwws?S${)TBD)ua1TEMRHOGGg^?3`|1wAJt=K3Usq)u z0hEb*Hb;Qjti{{6KZRS%$N={|_hNU~)|AAnA9(ce^S?4OGE3YWS&i6TW)6hslf$`^ z%k4i^7TBmvL2SK(E){i2VY6lI3gLUR-ya5`Yb;%GxGPmR5L0BSG$nr3{+jA}nT{up zRb)i?^MJ76EE9kGV}D&&69XrHo0c$3^HOqy+2pI41;nZa5{#G)vA?r4^%Z=Q!+I2} z6`2Bo6K@JfsI(go@y5S4?HTUXR1-E|h-Qq0Zt!aGG$&e75-m<@(qIh0_VVfrQR0aB zcIA1lArpTP(v)wCE-OZ2^k2yfih71c^J!h#E?@X;7Ad3=We=v7==GV5&9OIwFFo^; zcrEY8HC|?Ttpi%!^eQ^1*eJsLS!F-Z!nA*XK&sR*yyakj)N7Q1u!UohaSt4;`?1zP z`U4ql)8yH0`mCLi`=Y9w7*YIV*S5eaD;uZhRc}kdCEB1R@E)bx0?2Pa>fTAu`oSF#)0UY;oEwBGZQYV$v6egnRHNgwqSso z0WE9nJ+aQUcILtUy`?FEXwx>z7#sjo9?EScX%}b~X7JXE;ToCtjBQ=?0$VsR=g^t3;B+U)ac*mDWbe@GcgtDGhc7 z+U!)-pt+K(l6|AiEimd1S!>O0*!eEkv>XcKIqtId8!t^^(GZN#b1gjuRG9o$;~IFx zF4QNzbH+I}bFxqtG2U_$cs;tbI41+x_@R0R@F_SqB-vcD@lyaIYGt%EN_ljaJP=6z zQfH*nH`-gL=ko>@+qNOo$hXxDpseQ0O>4%+(&tieUbpP7oN1I@3WtHBe;omeOGLWw z>hXVc&c|d;wu)o$lZH`i4EFVeRHZx$OP#;pcBjxLnzY}9dS87Ie=<=j+aLf;wjuo9 zYi>^ZAlv5X3Cc1cQA2^_SQ>RXPT^z{=fN>jI0EFn?^h>UZP`jKjh7zdJgd59TY3k> zLdU60n><}#T8=h?U=IapGfsWp_!Q>prH6xWyk9~zZ+q;go>8W%gvPDhvx>IA*Jz0X z6|+95CGuq^8>A`XM&<4LP7}aSWXWq<0eR8qR+*BAYBA=V;_B6u5)&tB_ytC5NUMyJ zA6StP3EoiRj|&@r&A4rm9TI2}`S|e|)016u8BHnjj0tP@_WP6F9o4?U5!xX6Y+tvl zg3kQyww(eT_6zZi^rPI;7cX}4(N~1OC+3Hejf<$Pz`4)nZ?c{2w6oo~2@4z{E#mX- zjw8TLO2pOF($XwX#aTFIH*oy-_4Yw?FP#viuD*<@#kHIPm(e(2n6%LU38D-%#N7+J zv_SF46nB`35=TLPbY37&5q_b4C-=R+(lSZ|L8JZl{=hzGN)6r23HW}9nI+mMdVK!e z5@qiwbxR$})Tvg2RvB7~ql{5wMsnY&jyYntUUJ7|q?pEYH02u&WP6MpMu#iCM+WP) zj14R1ng)X``ZgXwc>cALED*kI8yo1JMMD}s z=$o;*R! lg~zh1l}+RTN{dF3|H75Cs%f}#^32Oa}Fms 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 literal 0 HcmV?d00001 diff --git a/res/bird.png b/res/bird.png new file mode 100644 index 0000000000000000000000000000000000000000..d80cc86b3dced295a4607caf9e875dfc35c1fd37 GIT binary patch literal 65913 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 literal 0 HcmV?d00001 diff --git a/res/pipe.png b/res/pipe.png new file mode 100644 index 0000000000000000000000000000000000000000..04cd9e774828174bc45d7c0fd4f1b81bccdb7036 GIT binary patch literal 69674 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 literal 0 HcmV?d00001 diff --git a/shaders/bg.frag b/shaders/bg.frag new file mode 100644 index 0000000..57415c7 --- /dev/null +++ b/shaders/bg.frag @@ -0,0 +1,14 @@ +# version 330 core + +layout (location = 0) out vec4 color; + +in DATA { + vec2 tc; +} fs_in; + +uniform sampler2D tex; + +void main() +{ + color = texture(tex, fs_in.tc); +} \ No newline at end of file diff --git a/shaders/bg.vert b/shaders/bg.vert new file mode 100644 index 0000000..eb0ea51 --- /dev/null +++ b/shaders/bg.vert @@ -0,0 +1,16 @@ +# version 330 core + +layout (location = 0) in vec4 position; +layout (location = 2) in vec2 tc; + +uniform mat4 pr_matrix; + +out DATA { + vec2 tc; +} vs_out; + +void main() +{ + gl_Position = pr_matrix * position; + vs_out.tc = tc; +} \ No newline at end of file diff --git a/src/BufferUtils.java b/src/BufferUtils.java new file mode 100644 index 0000000..b1e8d1f --- /dev/null +++ b/src/BufferUtils.java @@ -0,0 +1,32 @@ +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public class BufferUtils { + + private BufferUtils() { + + } + + public static ByteBuffer createByteBuffer(byte[] array){ + ByteBuffer result = ByteBuffer.allocateDirect(array.length).order(ByteOrder.nativeOrder()); + result.put(array).flip(); + return result; + } + + public static FloatBuffer createFloatBuffer(float[] array){ + FloatBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); + result.put(array).flip(); + return result; + } + + public static IntBuffer createIntBuffer(int[] array){ + IntBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asIntBuffer(); + result.put(array).flip(); + return result; + } + + + +} diff --git a/src/FileUtils.java b/src/FileUtils.java new file mode 100644 index 0000000..56c672f --- /dev/null +++ b/src/FileUtils.java @@ -0,0 +1,27 @@ +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +public class FileUtils { + + private FileUtils() { + + } + + public static String loadAsString(String file){ + StringBuilder result = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String buffer = ""; + while ((buffer = reader.readLine()) != null) { + result.append(buffer +"\n"); + } + reader.close(); + } catch (IOException e){ + e.printStackTrace(); + } + return result.toString(); + } + +} diff --git a/src/HelloWorld.java b/src/HelloWorld.java index 284f611..046e316 100644 --- a/src/HelloWorld.java +++ b/src/HelloWorld.java @@ -93,7 +93,7 @@ public class HelloWorld { GL.createCapabilities(); // Set the clear color - glClearColor(1.0f, 0.0f, 0.0f, 0.0f); + glClearColor(1.0f, 0.0f, 1.0f, 0.0f); // Run the rendering loop until the user has attempted to close // the window or has pressed the ESCAPE key. diff --git a/src/Input.java b/src/Input.java new file mode 100644 index 0000000..26efef9 --- /dev/null +++ b/src/Input.java @@ -0,0 +1,13 @@ +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWKeyCallback; + +public class Input extends GLFWKeyCallback { + + public static boolean[] keys = new boolean[65536]; + + @Override + public void invoke(long window, int key, int scancode, int action, int mods) { + keys[key] = action != GLFW.GLFW_RELEASE; + + } +} diff --git a/src/Level.java b/src/Level.java new file mode 100644 index 0000000..f0a8e44 --- /dev/null +++ b/src/Level.java @@ -0,0 +1,40 @@ +import static org.lwjgl.glfw.GLFW.*; + +public class Level { + + private VertexArray background; + private Texture bgTexture; + + public Level(){ + float[] vertices = new float[] { + -10.0f, -10.0f * 9.0f / 16.0f, 0.0f, + -10.0f, 10.0f * 9.0f / 16.0f, 0.0f, + 0f, 10.0f * 9.0f / 16.0f, 0.0f, + 0f, -10.0f * 9.0f / 16.0f, 0.0f, + }; + + byte[] indices = new byte[] { + 0, 1, 2, + 2, 3, 0 + }; + + float[] tcs = new float[] { + 0, 1, + 0, 0, + 1, 0, + 1, 1 + }; + + background = new VertexArray(vertices, indices, tcs); + bgTexture = new Texture("res/bg.jpeg"); + } + + public void render() { + bgTexture.bind(); + Shader.BG.enable(); + background.render(); + Shader.BG.disable(); + bgTexture.unbind(); + } + +} diff --git a/src/Matrix4f.java b/src/Matrix4f.java new file mode 100644 index 0000000..bdc9985 --- /dev/null +++ b/src/Matrix4f.java @@ -0,0 +1,80 @@ +import java.nio.FloatBuffer; + +public class Matrix4f { + + public static final int SIZE = 4 * 4; + public float[] elements = new float[4 * 4]; + + public Matrix4f(){ + + } + + public static Matrix4f identity(){ + Matrix4f result = new Matrix4f(); + for (int i = 0; i < SIZE; i++){ + result.elements[i] = 0.0f; + } + result.elements[0] = 1.0f; + result.elements[1 + 4] = 1.0f; + result.elements[2 + 2 * 4] = 1.0f; + result.elements[3 + 3 * 4] = 1.0f; + return result; + } + + public static Matrix4f orthographic(float left, float right, float bottom, float top, float near, float far){ + Matrix4f result = identity(); + + result.elements[0] = 2.0f / (right - left); + result.elements[1 + 4] = 2.0f / (top - bottom); + result.elements[2 + 2 * 4] = 2.0f / (near - far); + + result.elements[3 * 4] = (left + right) / (left - right); + result.elements[1 + 3 * 4] = (bottom + top) / (bottom - top); + result.elements[2 + 3 * 4] = (far + near) / (far - near); + + return result; + } + + public static Matrix4f translate(Vector3f vector){ + Matrix4f result= identity(); + result.elements[0 + 3*4] = vector.x; + result.elements[1 + 3*4] = vector.y; + result.elements[2 + 3*4] = vector.z; + + return result; + } + + public static Matrix4f rotate(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(angle); + float sin = (float) Math.sin(angle); + + result.elements[0 + 0 * 4] = cos; + result.elements[1 + 0 * 4] = sin; + + result.elements[0 + 1 * 4] = -sin; + result.elements[1 + 1 * 4] = cos; + + return result; + } + + public Matrix4f multiply(Matrix4f matrix){ + Matrix4f result = new Matrix4f(); + for (int y = 0; y< 4; y++){ + for (int x = 0; x< 4; x++){ + float sum = 0.0f; + for (int e = 0; e< 4; e++){ + sum += this.elements[x + e * 4] * matrix.elements[e + y * 4]; + } + result.elements[x + y * 4] = sum; + } + } + return result; + } + + public FloatBuffer toFloatBuffer() { + return BufferUtils.createFloatBuffer(elements); + } + +} diff --git a/src/Shader.java b/src/Shader.java new file mode 100644 index 0000000..c7173f1 --- /dev/null +++ b/src/Shader.java @@ -0,0 +1,73 @@ +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.opengl.GL20.*; + +public class Shader { + + public static final int VERTEX_ATTRIB = 0; + public static final int TCOORD_ATTRIB = 1; + + public static Shader BG; + + private boolean enabled = false; + + //Identifiant du programme resultat de la compilation des shaders + private final int ID; + private Map locationCache = new HashMap(); + + /* + Crée le fragment et le vertex shader les lie dans un programme dont il renvoie l'identifiant. + */ + public Shader(String vertex, String fragment) { + ID = ShaderUtils.load(vertex, fragment); + } + + public static void loadAll(){ + BG = new Shader("shaders/bg.vert", "shaders/bg.frag"); + } + + public int getUniform(String name){ + if (locationCache.containsKey(name)) return locationCache.get(name); + int result = glGetUniformLocation(ID, name); + if (result == -1) System.err.println("Could not find uniform variable " + name); + else locationCache.put(name, result); + return result; + } + + public void setUniform1i(String name, int value) { + if (!enabled) enable(); + glUniform1i(getUniform(name), value); + } + + public void setUniform1f(String name, float value) { + if (!enabled) enable(); + glUniform1f(getUniform(name), value); + } + + public void setUniform2f(String name, float x, float y) { + if (!enabled) enable(); + glUniform2f(getUniform(name), x, y); + } + + public void setUniform3f(String name, Vector3f vector) { + if (!enabled) enable(); + glUniform3f(getUniform(name), vector.x, vector.y, vector.z); + } + + public void setUniformMat4f(String name, Matrix4f matrix){ + if (!enabled) enable(); + glUniformMatrix4fv(getUniform(name), false, matrix.toFloatBuffer()); + } + + public void enable() { + glUseProgram(ID); + enabled = true; + } + + public void disable() { + glUseProgram(0); + enabled = false; + } + +} diff --git a/src/ShaderUtils.java b/src/ShaderUtils.java new file mode 100644 index 0000000..625c84c --- /dev/null +++ b/src/ShaderUtils.java @@ -0,0 +1,57 @@ + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +public class ShaderUtils { + + private ShaderUtils(){ + + } + + public static int load(String vertPath, String fragPath){ + String vert = FileUtils.loadAsString(vertPath); + String frag = FileUtils.loadAsString(fragPath); + return create(vert, frag); + } + + public static int create(String vert, String frag){ + // On crée et compile le vertex et le fragment shader + int vertID = glCreateShader(GL_VERTEX_SHADER); + int fragID = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(vertID, vert); + glShaderSource(fragID, frag); + + glCompileShader(vertID); + if (glGetShaderi(vertID, GL_COMPILE_STATUS) == GL_FALSE){ + System.err.println("Failed to compile vertex shader"); + System.err.println(glGetShaderInfoLog(vertID)); + return -1; + } + + glCompileShader(fragID); + if (glGetShaderi(fragID, GL_COMPILE_STATUS) == GL_FALSE){ + System.err.println("Failed to compile fragment shader"); + System.err.println(glGetShaderInfoLog(fragID)); + return -1; + } + //on lie les shaders au programme + int program = glCreateProgram(); + glAttachShader(program, vertID); + glAttachShader(program, fragID); + glLinkProgram(program); + + if(glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) { + System.err.println("Failed to link vertex and fragment shader"); + System.err.println(glGetShaderInfoLog(program)); + return -1; + } + + glValidateProgram(program); + + glDeleteShader(vertID); + glDeleteShader(fragID); + + return program; + } + +} diff --git a/src/Texture.java b/src/Texture.java new file mode 100644 index 0000000..175210a --- /dev/null +++ b/src/Texture.java @@ -0,0 +1,57 @@ +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import static org.lwjgl.opengl.GL11.*; + +public class Texture { + + private int width, height; + private int texture; + + public Texture(String path) { + texture = load(path); + } + + private int load(String path) { + int[] pixels = null; + try { + BufferedImage image = ImageIO.read(new FileInputStream(path)); + width = image.getWidth(); + height = image.getHeight(); + pixels = new int[width * height]; + image.getRGB(0, 0, width, height, pixels, 0, width); + } catch (IOException e) { + e.printStackTrace(); + } + + int[] data = new int[width * height]; + for (int i = 0; i < width * height; i++) { + int a = (pixels[i] & 0xff000000) >> 24; + int r = (pixels[i] & 0xff0000) >> 16; + int g = (pixels[i] & 0xff00) >> 8; + int b = (pixels[i] & 0xff); + + data[i] = a << 24 | b << 16 | g << 8 | r; + } + + int result = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, result); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); + glBindTexture(GL_TEXTURE_2D, 0); + return result; + } + + public void bind() { + glBindTexture(GL_TEXTURE_2D, texture); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } + +} \ No newline at end of file diff --git a/src/Vector3f.java b/src/Vector3f.java new file mode 100644 index 0000000..3eb454e --- /dev/null +++ b/src/Vector3f.java @@ -0,0 +1,17 @@ +public class Vector3f { + + public float x, y, z; + + public Vector3f(){ + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + public Vector3f(float x, float y, float z){ + this.x = x; + this.y = y; + this.z = z; + } + +} diff --git a/src/VertexArray.java b/src/VertexArray.java new file mode 100644 index 0000000..e3594fb --- /dev/null +++ b/src/VertexArray.java @@ -0,0 +1,72 @@ +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL30.*; + + +public class VertexArray { + + private int vao, vbo, ibo, tbo; + private int count; + + /* + Donne la description de l'agencement des differents buffer + */ + public VertexArray(float[] vertices, byte[] indices, float[] textureCoordinates){ + count = indices.length; + + /* + VERTEX ARRAY OBJECT + */ + vao = glGenVertexArrays(); + glBindVertexArray(vao); + + /* + VERTEX BUFFER OBJECT + */ + vbo = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + glVertexAttribPointer(Shader.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(Shader.VERTEX_ATTRIB); + + /* + TEXTURE BUFFER OBJECT + */ + tbo = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, tbo); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW); + glVertexAttribPointer(Shader.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(Shader.TCOORD_ATTRIB); + + /* + INDICES BUFFER OBJECT OR ELEMENT BUFFER OBJECT + */ + ibo = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + public void bind(){ + glBindVertexArray(vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + } + + public void unbind(){ + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + public void draw(){ + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); + } + + public void render(){ + bind(); + draw(); + } + +} From b02119dca79de9ea5a3b1072ac68fbe2b6e606c5 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 13 May 2021 20:40:21 +0200 Subject: [PATCH 03/53] Ajout de la classe: Scene, VertexArray, Shader, ShaderUtils, BufferUtils, FileUtils, Matrix4f, Vector3f Ajout des shaders de test: vert.vert et frag.frag dans le dossier shaders --- .gitignore | 2 - res/bg.jpeg | Bin 24744 -> 0 bytes res/bird.png | Bin 65913 -> 0 bytes res/pipe.png | Bin 69674 -> 0 bytes shaders/bg.frag | 14 ---- shaders/bg.vert | 16 ----- shaders/frag.frag | 7 ++ shaders/vert.vert | 7 ++ src/HelloWorld.java | 115 ------------------------------ src/Level.java | 40 ----------- src/Texture.java | 57 --------------- src/VertexArray.java | 72 ------------------- src/{ => engine}/BufferUtils.java | 4 +- src/engine/Engine.java | 92 +++++++++++++++++++++++- src/{ => engine}/FileUtils.java | 2 + src/{ => engine}/Input.java | 3 +- src/{ => engine}/Matrix4f.java | 6 +- src/engine/Scene.java | 25 +++++++ src/{ => engine}/Shader.java | 11 +-- src/{ => engine}/ShaderUtils.java | 1 + src/{ => engine}/Vector3f.java | 2 + src/engine/VertexArray.java | 47 ++++++++++++ 22 files changed, 192 insertions(+), 331 deletions(-) delete mode 100644 res/bg.jpeg delete mode 100644 res/bird.png delete mode 100644 res/pipe.png delete mode 100644 shaders/bg.frag delete mode 100644 shaders/bg.vert create mode 100644 shaders/frag.frag create mode 100644 shaders/vert.vert delete mode 100644 src/HelloWorld.java delete mode 100644 src/Level.java delete mode 100644 src/Texture.java delete mode 100644 src/VertexArray.java rename src/{ => engine}/BufferUtils.java (98%) rename src/{ => engine}/FileUtils.java (97%) rename src/{ => engine}/Input.java (95%) rename src/{ => engine}/Matrix4f.java (95%) create mode 100644 src/engine/Scene.java rename src/{ => engine}/Shader.java (88%) rename src/{ => engine}/ShaderUtils.java (99%) rename src/{ => engine}/Vector3f.java (93%) create mode 100644 src/engine/VertexArray.java diff --git a/.gitignore b/.gitignore index 8a49887..2880f64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ /.idea/ /.gitignore /jeu-de-combat.iml -/out/ -/lwjgl-release-3.2.3-custom/ diff --git a/res/bg.jpeg b/res/bg.jpeg deleted file mode 100644 index af5a79d2cfa54d871f634ab8244899d0827ffbbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24744 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
e?;4nC)^^?(IfZVws#> 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 lg~zh1l}+RTN{dF3|H75Cs%f}#^32Oa}Fms zg(gO;{g5Bq*HdZN-W&m*cdu+RD~;E4@@vO2b&-rvIm4Id00X@UFQvDYDR^R8`-6SrL6f!vPXW(#;iw zx|o2_w{_BN6lcWXV(@lTAwd#;fA}YJt4|wBxHoDe)oSis3+?(808!oMN*B3}yMv!s z0;Ae!))U8tE@n(M-5jJ2WZaHI%}0ms3KsPamL+wstd&_auChc4KDx#;-G?sO|5KMz z+>u(_s$4Zq1r;haj^KK*tv|*n|I8%9y}lIILkQqy_wAa0sn_%>o5x!AuY^B7vCz+Z zd*EERS?FFL10pOmV|jZt_w=3P52G${8V-yeyUm-bKG2y=Jpw!mo#_r7W6kL+rG50S zrki{pZ_nYJn>XWqe0z@gC?I9z!V!RBHqk>sqXy&1UfUacc_YJdi`gNTrvHrEG{-I- zTrARhXm}KL@D)h`U@{CA#-qqg?)}Mu>&f{ub7)((5cQ=&o#-LC0Z_^S0A+%&S`>A~7@4Q1%?VMPZ~9Jyrf6ZskG3@SGHIpgT`T*EYkOErEFHJ63h z%!3s#J$azWr8M$nBCre zX%Tx|#`NplwfBXeH*ZUJ=y!)^;_{o&hjCFic*mP$&IoX9^sMl18_k3UekyOi4}WNQ zZH;+)>6jZ6^jfDoeYS6yqnG{M?FdlN89`dC6AS2H0s6NfGCFj;^$WE;@YbVAI^cDYy;cIw~;@M;&MfK_1Wmtp9Z1--9zzq~aDc+hK>sF{*;_s>-5}Og@pFT&3@049lg?6wn(fICnd6)CBUE!YUKB&D{Ag_ zv$%&VzCfIwgs7y(>UugB>@ZQ=X^w2o+g5E1DG>5`ZaUzzt(01B)I~H^N7AwrVNlxDVIQPYT35vWAEijCci}GCk zSp&O)ANA3FRB=Vw#QNLV=+l!Lmb(=hob7DD-uQt z&OQYjK4KN_G8aJC<&N#4f)5W#id_Wv#VjQ^-3GStu2<#!d0Dy?1ieUn=nC~R$ShMdrFaW zTCZ1=kwZ{Iedl{=xl01+B*Wn`pN-l3-Ma!R%ru9(gI{?$IBO@ji&c-$^T2J`Q?vYd z&f{-wonK&|vCz2<$h5@vMp@_7bX~fP^wOT+{-cnbEn4k|mL8ej&;1nwWxxv^G7I9O z=w&PnC$r%0kQq~0EZTUlwz?ROF8}SdUCPY4qSjg!zun?tN`qFEL0FI-=JL6nXL-&V zA#af>9ZprROMp3d$-XK!+tGC_AAG|P$FXPxckXM%yFg@mywr%KG0=dywQYVV-HSM6 zjrQg4Bx?9I?7I~w-Q4&;+PU&*Hqfqb+L`HBsw>)3Iy1G#)~eW-X*=abLxtGMps`14 zZxLqNqP0|uMD0^V5=&AdNhKn0ttC{6M8#Ir7Gjs!edc@5_x|~v^Zoxme?8}(d+s0i zKF>LK`Tg#tnf%P2JtH)^k{xC(rO1#U9@v`eK?E|-tu)M>`R-ZQ3Uburd}ZPLZ=C6x z#f@ofFZkqXo*yuH22z(g`bnAeWFl80-U#oWu{)x_zV)g;yu18ceowe=tW&3;mL|h_ zNpUGUDCvScZpto5Bt++?Vgir)7;P7u3*iBb>LNzW`betJdeA@>096ZApH$LP(fyX6 z9TTX#GmfzE1p862Cx6K_3f!%Lz3TY>g|JASuo?QqJdv);BWH?_77-)0uYZmtLf(+Yg_sG@iC}dq5n`CU7 z-n~!O)EGkd%)uz93=_N&g9@ihohl@xRo=~-%2m-N^VW^z!a&3==7{-DjFSZ}sYT@!aU&!M`^E5aGu zT1bpaG+s_Lb0<|RHqA#nZP#vhCHQ`cylLin4-4uV^GzzY9e8W>QXZ8|%uh``ui)Qy zJ5tuOyz8dzC-1dAKb0pHkPhIZPD7)tDN;}mAH7HE7cC&)?AbMg+kF6jE?zS8N7?7O zsx8_08c~EDFvfo2wN`hn@mh9HR@xMtD910++oNr-XJImS z+9MynsW_oonWyfJy)%S|)WzT9{D)2z#(u6fsC!s?J>_s%bZ zz^`BF*B_>PU3pJ7`5W`y#yl!uLpu}&e&(8m@9ROp7)CW489hJyk^V$b5}@)7`)4}` z$vNqD+EyE)-Yy!)p!Qc*2*P6VBZ}XblFV5 zcZFrz9@E6Rt%D!8+S+>W7DMi5&oD_6a>X<6_iXhjI`z}gvv`1(&P3*PIr?=pBdbB* zo68pBrEF+MTCO>zErruyd08p4;WMdQE#tq*a_P#9z81YH^FC352i>qBCF%tpfC6Z# zL&D((SjG3{o14sTRJ1>3dkF#+BN>gjXlTa;@YLK&ky@hR1TWZFCM_RSp9|xp*R?v? z$~^99>=yL1)H9;YCyt@7V7h7vZ+ZJ0W@l1dmKDRgSKhJaXAJTyfxh-Yscnl=r@rk- zkuBDJ)m-^4n%&l{U*#k{6{@@$WY6OvFijyHucJn*&_kZS8vOk_?bL@s?%{(Se50sO z;ulsWYa&yF&$Fw9_|-?0ypDi(c?4y&zJ!6OXvBw|xFdxi?2WbS!{WSOJ9gq6T3N}A zAa=HGxSbop%QNw7Rxru!g^TmO^YKl?WILNRWI4z+w5M)Ecxw67-kmHr%f<;!+DnlC zaS+$65g=cVMX4Gp8GQn3s#21T%rH_GNGZyh|&h zE|n@d%Ct+fiHCPjZev49Bb?uz<=rB$GnVOk^+k*0lEjK}P%B{`7<52iintz9=yg5f zWdZo&Vv9DOwFh(uS-vp{kKeRi8-tciuDs!im!g$XfLDd3ds-vbPH?_puPRr^>j-kL zA*Tm#f z9D*ic$S-*zf7L(`b!FWEo7UWJaGIa)t1M8Z;8ht)m<|7Xi!q40yy-`U$_O{d)tku4 z?8&t7XBgwa2ZTlH{zsDuqNcZ*f&cmLk~)Nw$y4;_TBbwW-J~Z`=dj_y7zdZvM$dAE zt7bZ5SE-jwRQzvhH1Hm!+oU>VaBB=^rAocfqgtxYy=1+4} z`%7f59Z#MN4?h$etRXu`Gocwv$~}JfcDlmz&k|j;SlX9Oyf)+R{fRV#{$6w^KKiYW zW6%H>farN|Zp~U+hjv7lTq?y_S?jo48<`@3RQ36$q;3)QaRmZQzNF^8;zE_dM5&8n zH)alm>blye!Y>F4$|(ZUOo$@ z!#F|E%*ICOX{r;+d2pdS6PC9Wg`TRK!c7L*7T8aCxIKc@+X@Hsp~u#9cHb7tc`Fj$ zxUIK094*pN3EKU!ksScIfE6|65o#!}A;zE;0->VYHf@N&fxVB{3e257OKPnQJp-u@ z-I$=FHobz0mR-zq{AP+>Mn^N)aoW=`ETn)m6glTkEaJ-QX{FXH4xNE3F-~dF9`GKq zqV^|DEgcQ7b5zP8_m4QAc#`Di>pyX=vkmYQ+WuK_??>sprIF?Cyjk-M*AVSw7Cy|; zer3ScYu!1vetUDDY$(4L{Ra37PbRwA4Xv|5eon)vpM$$d65sQ$7&W_Fzqp2Svk-2^ z`(Fe7m+-YmXhdeKVVq)##E_b%+YoSLn;+JAC`K`yiN&P0O)5#BOmMtlRX$cMDAAsB zxIw+kcfUP1q|6B6>GMr?IzT@>ZTHU|{%{A$_o%|-II8xY?dLolPPHqA+xE>~hQ;K4 z3g+R=yPoC9a_8x~gC$;#Jz7v+w@6a3ih4|Tpjodm`WaLNl9mpvUm04 z5;kl!Bex{V?Rema~2{fIc3*x+asdNcnX#861sJE6R=~` zZZ$wztVDucF@8ZV-9zSaMwMzi^|4p4J0&Z0{wT-pEO;%3xSF3(1} z#z-pj64Zx0G3@2;#UaF`pM5%3KM%ZA<@C(?0$c@fpUbt1;{|T2yQX=$HG6zLSk>sH zYppGy`x@X9a^13ZyHfK5V?~{W*Il)mx9y$}2uEO3ba<}QBKeB+f4P+_0ploah`#gi z{2tE2cPq_0+26$PSV1sMy>ryHEtEX^*aI^&=}C|Aonc9XL9u)ODtz8 ze6_twvd5P}DYbcU#XHNWl=m5ir(L-^Rz~60lIkH5)x-6p<=v~C+MFuKX8>}Gw6PM* z$D>v@=6m4_8P_Rg%I^N@A~ISf5jPH+6&_6L3!(-_tuJIp>D#eDh?O1LR3L8RhHfpY z%)qH0#j(<_4C84Rm#p39>%^E~isBs|!uwGQGD>%oZa<&pBQ}ywABwH4>#elk>X6;v z)jt$#(+ZyMTw;k_()D!gPG^T|Iz?$K7trexw4q)F0@WGVo)i4lRA|@SS(^Q}x%o7( zaONnB(y%3KnBZaz9!!`Sbj3Rd@zlq+7DD}i^UWCm>&%N1h!-r+recR5w{@yd*2scHG!4^&MmKAinp9;A3Gs8)JdlI(VLi*24FBd z3eUlUI@`=8x5>Muj-9CK7vYINiuj2s2c~x|Z?vsLCuNIE2U+=_S_{v^9exXUfD2tXZf7v&1{{M@>!*M}QF!;MoP( zL-RBvcNRW<(+aqaA_M00Jh~gyub0yq*K!0)??LTM~CQ3!jbMCc?DCJX@+r7(R{@}0>8LTusfdfqfDT> zMYlmm)7ZueoArJ{{_VJ>MC z*J7mO4stt9h3lU&O0s5s9pL4b=Dh>z`j=PKIx=Z)7={nzKMbR9G6+iM^MJBRd-~%S z&+!6ISmer^6ziK|;J)%L_XXM*Z_%xAH9S||x>0R35KeIqCM18ZM;Seeb)+AP?Pz%f zKiLCkuetFvZ!8DmZvze96UI}fJ_(Re^hXX&hurx4j8%|Yt*`HR&Eh-0*;O>;s^$2G zFjqn4Y^AE}`?=iwVd3t#*vV&s-b#j@w$&Bos6#QEU@xd~h~=wp@oWq!*tN`4FTgi) zIIcOw_E5}z;``3<%pX=0m3OJX^%g7CScheWfm|8RqSfui2R>@eQT`{)F5SIbQP1BF zX7LlgkeE1dEw6{=&oTPS)$4m3*}_^3WUorQ?`!W^O;4^2R9a*N#1PPxHoq0oG)-fW z=O*LNw#0OKAP4!gRdF@rX0%H+c%iYJ#poF)QT{fX^kyRgI|W&CELAfZ&oQ?7lBe+r zK)y`uTEP*I4}vxjJ;feqZE=ivO&3uma!_KIJvgO%yFoq69r#>uBYW64Qg=s2_&){d zHNmDZ4g`S)CIuOo+$*0WD^C{n*U{V_FEs50xYjs1s`aH0N*jTi>pUz2#U0^38r*9{oaNquKWIvDv~G2{D5HSb z-u_i{ELd$jjvpL|n=IS^Ha4*eym137J(+oGSxvX541tW9Ub~5LLn~{!%r|f-31MiJ0F@aC1$WlTZ*Qm!XCZWN6lL>a1f zsW;&AZ7epnpwJRR1z_Rw2U&L;soxZ!t58VoMXmO7^!lO)j{8@MI!R}W*Yjeb= zrF?_nJHEyaRn;J{9mMX^Vp$jJEeBFY(fgtTvZxRFr3)SHUrzVi;{pps?P_~UlQYIK zBfdUZU3@iU3lK&y&@Cz*Weg%2h8vesQT|?=*BPU#npc7};h*|QgLWgROF(%o6D19u zDU35CBAaHDq(?+1(${Z{gaQ4>?usG@6$e4uH3!NyeW;qL6FN5bpfov!eFQAUer2x? zw_|oFhOO^vv91ZSMa!?!dG>Ki3B=ulE;jx-gb4)H(~RAFb2nFh=_cJQooTJdt3oe_Kx*wqyD8*IUFxb}b&2x~nJ3TqDt-MZ6pGu%XdzeH)E!;;b1#5XmyBBNM z2rgJ*Jl*GH6zzzvy5)nnNB>C3 zk;?|HFfY(WP3F;emde z4Je-uoI10=OipiG9jL&{*>bb(N~tP@!EJPz`_(%qERr6n;S8&=@>1pO;XwqU95)EE zKzvY1bUqZ5D6yO1q1;vbS{x?B;voR5g(|wJXk%?X_D$8R@^G7g3|O&FT!=z(itQNd z0&^e~n?jn`>aThJwmiB|e_B%^|7bK@73xtr#|f*EC4BCW>_-unmh_f znH&vTSnZHNA&JIpb!-ghRBmGwTpyRX>GD6PqwIPxH6R6ooh>6V!5QXnEg9(Gme1G@ zo=L#n-rK|f!U{IcbL7Yc!Oik7KF4)yT$kF~g3lkoPUHi@}0A4(Dp(Xix#6%%N1%s3Rwats=MFz}Eu zF7A~ew>&@1{M?QO*M*|~5wOqwRy0_!LHYX8BhVER7&*|$|0LOFtSEt;%^lp6QJ^m@ z;8T;toi?}ueM4Wdwe52OKJL7AnN0G)w8@p>cul=bqYk^~y2>f_i*`$IPE$9pW>jwN4=00AQ z6>OGOerbS*G>%*3Yp4w%n{XG6d$oyMC&?sgMe_Y9Z5p$Rx$<48jx&urF1hsgI)b-p zAxVIwe{kJhH&8#dSFq=Y$NlZSZOa6kzkY-?YU*v$C^W=0@jmR*t{yjI87?OFFOz57 zq3-YmuVi4V6rQ8u>g(U<$ox@4cPB`z9&2Y?BDh*kj6F(VcdtJ{7ZaA9Im_!;YFJq9 zSAc?-FUWI(-5)A=u>rm=Z>@{KZD>8Dw9U%zB)f{z1?$L-`GdY4oAt`_Ts_)R9av+S zLfF~iKX|iN;(W9R6o=1tSqmv&I8I%{r+{>1(cV|9;1(vITZn1hVLEl2Im=bNpwB zQ$2aEXtb%YYjiVF4|a*8LT-`^Bp>)JS8bD|!=+wY!OvBb+U?*^*`Mc!hkRmoG6q(J zNM&2us#LhTr}6NiSgyL1PMS`3Hk`7wP37)e|83}u(kaMCu%S}DMP2V@Q_2~;J6Htl z#W-Kzp=D?z7ve;rZA2d&W5Z2#byAITN1>eYEx>RbW#2_JN9+>iBN*qH0V9`KflnR% zo+5@YN=AxrHtr}<^!M{Rt{PJeh5kFDAO5dkLdbDD^Q#n$J0g0#sv*ah;W@T%eMI*D z`-tPHbvu8Fi-`dyxeNS1RR1Lv6FXL^x!8m|_J_1R0=D_<2^lKX@w;D7oRFa~JNEw2 iY^mVxM2!5euGlexX6WC_{}%k`@*fZUe>`A*IP~A17zpzK diff --git a/res/bird.png b/res/bird.png deleted file mode 100644 index d80cc86b3dced295a4607caf9e875dfc35c1fd37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65913 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 diff --git a/res/pipe.png b/res/pipe.png deleted file mode 100644 index 04cd9e774828174bc45d7c0fd4f1b81bccdb7036..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69674 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 diff --git a/shaders/bg.frag b/shaders/bg.frag deleted file mode 100644 index 57415c7..0000000 --- a/shaders/bg.frag +++ /dev/null @@ -1,14 +0,0 @@ -# version 330 core - -layout (location = 0) out vec4 color; - -in DATA { - vec2 tc; -} fs_in; - -uniform sampler2D tex; - -void main() -{ - color = texture(tex, fs_in.tc); -} \ No newline at end of file diff --git a/shaders/bg.vert b/shaders/bg.vert deleted file mode 100644 index eb0ea51..0000000 --- a/shaders/bg.vert +++ /dev/null @@ -1,16 +0,0 @@ -# version 330 core - -layout (location = 0) in vec4 position; -layout (location = 2) in vec2 tc; - -uniform mat4 pr_matrix; - -out DATA { - vec2 tc; -} vs_out; - -void main() -{ - gl_Position = pr_matrix * position; - vs_out.tc = tc; -} \ No newline at end of file diff --git a/shaders/frag.frag b/shaders/frag.frag new file mode 100644 index 0000000..8728871 --- /dev/null +++ b/shaders/frag.frag @@ -0,0 +1,7 @@ +#version 330 core +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); +} \ No newline at end of file diff --git a/shaders/vert.vert b/shaders/vert.vert new file mode 100644 index 0000000..cdd6dd3 --- /dev/null +++ b/shaders/vert.vert @@ -0,0 +1,7 @@ +#version 330 core +layout (location = 0) in vec3 aPos; + +void main() +{ + gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); +} \ No newline at end of file diff --git a/src/HelloWorld.java b/src/HelloWorld.java deleted file mode 100644 index 046e316..0000000 --- a/src/HelloWorld.java +++ /dev/null @@ -1,115 +0,0 @@ -import org.lwjgl.*; -import org.lwjgl.glfw.*; -import org.lwjgl.opengl.*; -import org.lwjgl.system.*; - -import java.nio.*; - -import static org.lwjgl.glfw.Callbacks.*; -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.system.MemoryStack.*; -import static org.lwjgl.system.MemoryUtil.*; - -public class HelloWorld { - - // The window handle - private long window; - - public void run() { - System.out.println("Hello LWJGL " + Version.getVersion() + "!"); - - init(); - loop(); - - // Free the window callbacks and destroy the window - glfwFreeCallbacks(window); - glfwDestroyWindow(window); - - // Terminate GLFW and free the error callback - glfwTerminate(); - glfwSetErrorCallback(null).free(); - } - - private void init() { - // Setup an error callback. The default implementation - // will print the error message in System.err. - GLFWErrorCallback.createPrint(System.err).set(); - - // Initialize GLFW. Most GLFW functions will not work before doing this. - if ( !glfwInit() ) - throw new IllegalStateException("Unable to initialize GLFW"); - - // Configure GLFW - glfwDefaultWindowHints(); // optional, the current window hints are already the default - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable - - // Create the window - window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL); - if ( window == NULL ) - throw new RuntimeException("Failed to create the GLFW window"); - - // Setup a key callback. It will be called every time a key is pressed, repeated or released. - glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { - if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) - glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop - }); - - // Get the thread stack and push a new frame - try ( MemoryStack stack = stackPush() ) { - IntBuffer pWidth = stack.mallocInt(1); // int* - IntBuffer pHeight = stack.mallocInt(1); // int* - - // Get the window size passed to glfwCreateWindow - glfwGetWindowSize(window, pWidth, pHeight); - - // Get the resolution of the primary monitor - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - - // Center the window - glfwSetWindowPos( - window, - (vidmode.width() - pWidth.get(0)) / 2, - (vidmode.height() - pHeight.get(0)) / 2 - ); - } // the stack frame is popped automatically - - // Make the OpenGL context current - glfwMakeContextCurrent(window); - // Enable v-sync - glfwSwapInterval(1); - - // Make the window visible - glfwShowWindow(window); - } - - private void loop() { - // This line is critical for LWJGL's interoperation with GLFW's - // OpenGL context, or any context that is managed externally. - // LWJGL detects the context that is current in the current thread, - // creates the GLCapabilities instance and makes the OpenGL - // bindings available for use. - GL.createCapabilities(); - - // Set the clear color - glClearColor(1.0f, 0.0f, 1.0f, 0.0f); - - // Run the rendering loop until the user has attempted to close - // the window or has pressed the ESCAPE key. - while ( !glfwWindowShouldClose(window) ) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer - - glfwSwapBuffers(window); // swap the color buffers - - // Poll for window events. The key callback above will only be - // invoked during this call. - glfwPollEvents(); - } - } - - public static void main(String[] args) { - new HelloWorld().run(); - } - -} \ No newline at end of file diff --git a/src/Level.java b/src/Level.java deleted file mode 100644 index f0a8e44..0000000 --- a/src/Level.java +++ /dev/null @@ -1,40 +0,0 @@ -import static org.lwjgl.glfw.GLFW.*; - -public class Level { - - private VertexArray background; - private Texture bgTexture; - - public Level(){ - float[] vertices = new float[] { - -10.0f, -10.0f * 9.0f / 16.0f, 0.0f, - -10.0f, 10.0f * 9.0f / 16.0f, 0.0f, - 0f, 10.0f * 9.0f / 16.0f, 0.0f, - 0f, -10.0f * 9.0f / 16.0f, 0.0f, - }; - - byte[] indices = new byte[] { - 0, 1, 2, - 2, 3, 0 - }; - - float[] tcs = new float[] { - 0, 1, - 0, 0, - 1, 0, - 1, 1 - }; - - background = new VertexArray(vertices, indices, tcs); - bgTexture = new Texture("res/bg.jpeg"); - } - - public void render() { - bgTexture.bind(); - Shader.BG.enable(); - background.render(); - Shader.BG.disable(); - bgTexture.unbind(); - } - -} diff --git a/src/Texture.java b/src/Texture.java deleted file mode 100644 index 175210a..0000000 --- a/src/Texture.java +++ /dev/null @@ -1,57 +0,0 @@ -import java.awt.image.BufferedImage; -import java.io.FileInputStream; -import java.io.IOException; - -import javax.imageio.ImageIO; - -import static org.lwjgl.opengl.GL11.*; - -public class Texture { - - private int width, height; - private int texture; - - public Texture(String path) { - texture = load(path); - } - - private int load(String path) { - int[] pixels = null; - try { - BufferedImage image = ImageIO.read(new FileInputStream(path)); - width = image.getWidth(); - height = image.getHeight(); - pixels = new int[width * height]; - image.getRGB(0, 0, width, height, pixels, 0, width); - } catch (IOException e) { - e.printStackTrace(); - } - - int[] data = new int[width * height]; - for (int i = 0; i < width * height; i++) { - int a = (pixels[i] & 0xff000000) >> 24; - int r = (pixels[i] & 0xff0000) >> 16; - int g = (pixels[i] & 0xff00) >> 8; - int b = (pixels[i] & 0xff); - - data[i] = a << 24 | b << 16 | g << 8 | r; - } - - int result = glGenTextures(); - glBindTexture(GL_TEXTURE_2D, result); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); - glBindTexture(GL_TEXTURE_2D, 0); - return result; - } - - public void bind() { - glBindTexture(GL_TEXTURE_2D, texture); - } - - public void unbind() { - glBindTexture(GL_TEXTURE_2D, 0); - } - -} \ No newline at end of file diff --git a/src/VertexArray.java b/src/VertexArray.java deleted file mode 100644 index e3594fb..0000000 --- a/src/VertexArray.java +++ /dev/null @@ -1,72 +0,0 @@ -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL15.*; -import static org.lwjgl.opengl.GL30.*; - - -public class VertexArray { - - private int vao, vbo, ibo, tbo; - private int count; - - /* - Donne la description de l'agencement des differents buffer - */ - public VertexArray(float[] vertices, byte[] indices, float[] textureCoordinates){ - count = indices.length; - - /* - VERTEX ARRAY OBJECT - */ - vao = glGenVertexArrays(); - glBindVertexArray(vao); - - /* - VERTEX BUFFER OBJECT - */ - vbo = glGenBuffers(); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); - glVertexAttribPointer(Shader.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0); - glEnableVertexAttribArray(Shader.VERTEX_ATTRIB); - - /* - TEXTURE BUFFER OBJECT - */ - tbo = glGenBuffers(); - glBindBuffer(GL_ARRAY_BUFFER, tbo); - glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW); - glVertexAttribPointer(Shader.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0); - glEnableVertexAttribArray(Shader.TCOORD_ATTRIB); - - /* - INDICES BUFFER OBJECT OR ELEMENT BUFFER OBJECT - */ - ibo = glGenBuffers(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); - } - - public void bind(){ - glBindVertexArray(vao); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); - } - - public void unbind(){ - glBindVertexArray(0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - - public void draw(){ - glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); - } - - public void render(){ - bind(); - draw(); - } - -} diff --git a/src/BufferUtils.java b/src/engine/BufferUtils.java similarity index 98% rename from src/BufferUtils.java rename to src/engine/BufferUtils.java index b1e8d1f..234dc77 100644 --- a/src/BufferUtils.java +++ b/src/engine/BufferUtils.java @@ -1,3 +1,5 @@ +package engine; + import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; @@ -27,6 +29,4 @@ public class BufferUtils { return result; } - - } diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 2195c4e..286c6a7 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -6,9 +6,99 @@ * @author François Autin * */ - package engine; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.opengl.GL; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL13.*; +import static org.lwjgl.system.MemoryUtil.*; + public class Engine { + private long window; + + private Scene scene; + + private boolean running; + + public Engine(){ + this.running = false; + } + + private void init() { + if(!glfwInit()){ + // TODO Erreur d'initialisation + } + this.running = true; + glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //On utilise la version 3.3 d'openGL + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Compatible MAC + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Le core profile est l'interface 'avancé' d'openGL + + int width = 1280; + int height = 720; + this.window = glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL); + assert this.window != NULL; + + // On récupère les informations du moniteur principale + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + assert vidmode != null; + + glfwSetWindowPos(this.window, (vidmode.width() - width)/2, (vidmode.height() - height)/2); + + glfwSetKeyCallback(window, new Input()); + + // Contexte = zone cible des rendus + glfwMakeContextCurrent(this.window); + glfwShowWindow(this.window); + GL.createCapabilities(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + System.out.println("OpenGL: " + glGetString(GL_VERSION)); + + + this.scene = new Scene(); + + } + + /* + TODO gérer les input dans une autre classe + */ + private void processInput(){ + if(glfwGetKey(this.window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(this.window, true); + } + + private void update(){ + glfwPollEvents(); + } + + private void render(){ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); //SWAP avec le precedent + scene.render(); + int error = glGetError(); + if (error != GL_NO_ERROR) System.out.println(error); + glfwSwapBuffers(window); //Envoie le buffer vers le moniteur + } + + public void run(){ + init(); + while(running){ + processInput(); + update(); + render(); + if(glfwWindowShouldClose(window)) running = false; + } + } + + public static void main(String[] args) { + Engine engine = new Engine(); + engine.run(); + } + } diff --git a/src/FileUtils.java b/src/engine/FileUtils.java similarity index 97% rename from src/FileUtils.java rename to src/engine/FileUtils.java index 56c672f..1191880 100644 --- a/src/FileUtils.java +++ b/src/engine/FileUtils.java @@ -1,3 +1,5 @@ +package engine; + import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; diff --git a/src/Input.java b/src/engine/Input.java similarity index 95% rename from src/Input.java rename to src/engine/Input.java index 26efef9..c5b1e4e 100644 --- a/src/Input.java +++ b/src/engine/Input.java @@ -1,3 +1,5 @@ +package engine; + import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWKeyCallback; @@ -8,6 +10,5 @@ public class Input extends GLFWKeyCallback { @Override public void invoke(long window, int key, int scancode, int action, int mods) { keys[key] = action != GLFW.GLFW_RELEASE; - } } diff --git a/src/Matrix4f.java b/src/engine/Matrix4f.java similarity index 95% rename from src/Matrix4f.java rename to src/engine/Matrix4f.java index bdc9985..2c1dcc2 100644 --- a/src/Matrix4f.java +++ b/src/engine/Matrix4f.java @@ -1,3 +1,5 @@ +package engine; + import java.nio.FloatBuffer; public class Matrix4f { @@ -14,8 +16,8 @@ public class Matrix4f { for (int i = 0; i < SIZE; i++){ result.elements[i] = 0.0f; } - result.elements[0] = 1.0f; - result.elements[1 + 4] = 1.0f; + result.elements[0 + 0 * 4] = 1.0f; + result.elements[1 + 1 * 4] = 1.0f; result.elements[2 + 2 * 4] = 1.0f; result.elements[3 + 3 * 4] = 1.0f; return result; diff --git a/src/engine/Scene.java b/src/engine/Scene.java new file mode 100644 index 0000000..53d8fdb --- /dev/null +++ b/src/engine/Scene.java @@ -0,0 +1,25 @@ +package engine; + +public class Scene { + + float[] vertices; + + VertexArray vertexArray; + Shader shader; + + public Scene(){ + this.vertices = new float[] { + -0.5f, -0.5f, 0.0f, //left + 0.5f, -0.5f, 0.0f, //right + 0.0f, 0.5f, 0.0f //top + }; + this.shader = new Shader("shaders/vert.vert", "shaders/frag.frag"); + this.vertexArray = new VertexArray(this.vertices); + } + + public void render(){ + this.shader.enable(); + this.vertexArray.render(); + this.shader.disable(); + } +} diff --git a/src/Shader.java b/src/engine/Shader.java similarity index 88% rename from src/Shader.java rename to src/engine/Shader.java index c7173f1..c573885 100644 --- a/src/Shader.java +++ b/src/engine/Shader.java @@ -1,3 +1,5 @@ +package engine; + import java.util.HashMap; import java.util.Map; @@ -5,11 +7,6 @@ import static org.lwjgl.opengl.GL20.*; public class Shader { - public static final int VERTEX_ATTRIB = 0; - public static final int TCOORD_ATTRIB = 1; - - public static Shader BG; - private boolean enabled = false; //Identifiant du programme resultat de la compilation des shaders @@ -23,10 +20,6 @@ public class Shader { ID = ShaderUtils.load(vertex, fragment); } - public static void loadAll(){ - BG = new Shader("shaders/bg.vert", "shaders/bg.frag"); - } - public int getUniform(String name){ if (locationCache.containsKey(name)) return locationCache.get(name); int result = glGetUniformLocation(ID, name); diff --git a/src/ShaderUtils.java b/src/engine/ShaderUtils.java similarity index 99% rename from src/ShaderUtils.java rename to src/engine/ShaderUtils.java index 625c84c..caaf6ac 100644 --- a/src/ShaderUtils.java +++ b/src/engine/ShaderUtils.java @@ -1,3 +1,4 @@ +package engine; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL20.*; diff --git a/src/Vector3f.java b/src/engine/Vector3f.java similarity index 93% rename from src/Vector3f.java rename to src/engine/Vector3f.java index 3eb454e..928e33b 100644 --- a/src/Vector3f.java +++ b/src/engine/Vector3f.java @@ -1,3 +1,5 @@ +package engine; + public class Vector3f { public float x, y, z; diff --git a/src/engine/VertexArray.java b/src/engine/VertexArray.java new file mode 100644 index 0000000..03d3495 --- /dev/null +++ b/src/engine/VertexArray.java @@ -0,0 +1,47 @@ +package engine; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL30.*; + +public class VertexArray { + + private int VAO ,VBO; + + public VertexArray(float[] vertices){ + // VERTEX ARRAY OBJECT + VAO = glGenVertexArrays(); + + // VERTEX BUFFER OBJECT + VBO = glGenBuffers(); + glBindVertexArray(VAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + } + + public void bind(){ + glBindVertexArray(this.VAO); + } + + public void unbind(){ + glBindVertexArray(0); + } + + public void draw(){ + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + public void render(){ + bind(); + draw(); + } + +} From e2b43446c81fcb696a1569497abb70abe47612c8 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 13 May 2021 20:55:04 +0200 Subject: [PATCH 04/53] =?UTF-8?q?Gestion=20taille=20de=20rendu=20si=20modi?= =?UTF-8?q?fication=20de=20la=20taille=20de=20la=20fen=C3=AAtre=20+=20unbi?= =?UTF-8?q?nd()=20=C3=A0=20la=20fin=20du=20cycle=20de=20rendu=20du=20verte?= =?UTF-8?q?xArray?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/Engine.java | 16 +++++++++++++++- src/engine/VertexArray.java | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 286c6a7..d906554 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -8,6 +8,7 @@ */ package engine; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; @@ -44,7 +45,7 @@ public class Engine { this.window = glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL); assert this.window != NULL; - // On récupère les informations du moniteur principale + // On récupère les informations du moniteur principal GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); assert vidmode != null; @@ -57,6 +58,8 @@ public class Engine { glfwShowWindow(this.window); GL.createCapabilities(); + glfwSetFramebufferSizeCallback(window, resizeWindow); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); @@ -96,6 +99,17 @@ public class Engine { } } + /* + Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu + pour quelle corresponde à la taille de la fenêtre + */ + private static GLFWFramebufferSizeCallback resizeWindow = new GLFWFramebufferSizeCallback(){ + @Override + public void invoke(long window, int width, int height){ + glViewport(0,0,width,height); + } + }; + public static void main(String[] args) { Engine engine = new Engine(); engine.run(); diff --git a/src/engine/VertexArray.java b/src/engine/VertexArray.java index 03d3495..e7c64aa 100644 --- a/src/engine/VertexArray.java +++ b/src/engine/VertexArray.java @@ -42,6 +42,7 @@ public class VertexArray { public void render(){ bind(); draw(); + unbind(); } } From b5d18197ff1bd953f6e08afc031431cc5d76559f Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 13 May 2021 21:05:11 +0200 Subject: [PATCH 05/53] Ajout de classe primitive --- src/engine/Engine.java | 7 +++---- src/engine/Primitive.java | 11 +++++++++++ src/engine/Scene.java | 10 +++------- 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 src/engine/Primitive.java diff --git a/src/engine/Engine.java b/src/engine/Engine.java index d906554..8eba812 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -40,8 +40,8 @@ public class Engine { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Compatible MAC glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Le core profile est l'interface 'avancé' d'openGL - int width = 1280; - int height = 720; + int width = 800; + int height = 600; this.window = glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL); assert this.window != NULL; @@ -63,8 +63,7 @@ public class Engine { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); - - this.scene = new Scene(); + this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.triangle); } diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java new file mode 100644 index 0000000..887595e --- /dev/null +++ b/src/engine/Primitive.java @@ -0,0 +1,11 @@ +package engine; + +public class Primitive { + + public static float[] triangle = new float[] { + 0.0f, 0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + -0.5f, -0.5f, 0.0f, + }; + +} diff --git a/src/engine/Scene.java b/src/engine/Scene.java index 53d8fdb..301c470 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -7,13 +7,9 @@ public class Scene { VertexArray vertexArray; Shader shader; - public Scene(){ - this.vertices = new float[] { - -0.5f, -0.5f, 0.0f, //left - 0.5f, -0.5f, 0.0f, //right - 0.0f, 0.5f, 0.0f //top - }; - this.shader = new Shader("shaders/vert.vert", "shaders/frag.frag"); + public Scene(String vertPath, String fragPath, float[] vertices){ + this.vertices = vertices; + this.shader = new Shader(vertPath, fragPath); this.vertexArray = new VertexArray(this.vertices); } From e4c1c80e4a6c7138af76bb8a97e7264646799f4e Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 13 May 2021 21:51:40 +0200 Subject: [PATCH 06/53] Ajout du rectangle dans les primitives ajout de la gestion de l'EBO dans scene et VertexArray. Mode Wireframe avec le touche space. Gestion des inputs dans la classe Input --- src/engine/Engine.java | 21 ++++++--------------- src/engine/Input.java | 8 ++++++++ src/engine/Primitive.java | 16 ++++++++++++++++ src/engine/Scene.java | 6 ++++-- src/engine/VertexArray.java | 16 ++++++++++------ 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 8eba812..667430d 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -1,10 +1,10 @@ /** * CLASS ENGINE - * + * * Classe principale du moteur de jeu - * + * * @author François Autin - * + * */ package engine; @@ -14,7 +14,6 @@ import org.lwjgl.opengl.GL; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL13.*; import static org.lwjgl.system.MemoryUtil.*; public class Engine { @@ -52,6 +51,7 @@ public class Engine { glfwSetWindowPos(this.window, (vidmode.width() - width)/2, (vidmode.height() - height)/2); glfwSetKeyCallback(window, new Input()); + glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE); // Contexte = zone cible des rendus glfwMakeContextCurrent(this.window); @@ -63,18 +63,10 @@ public class Engine { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); - this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.triangle); + this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.rectangle, Primitive.rectangle_indices); } - /* - TODO gérer les input dans une autre classe - */ - private void processInput(){ - if(glfwGetKey(this.window, GLFW_KEY_ESCAPE) == GLFW_PRESS) - glfwSetWindowShouldClose(this.window, true); - } - private void update(){ glfwPollEvents(); } @@ -91,7 +83,6 @@ public class Engine { public void run(){ init(); while(running){ - processInput(); update(); render(); if(glfwWindowShouldClose(window)) running = false; @@ -102,7 +93,7 @@ public class Engine { Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu pour quelle corresponde à la taille de la fenêtre */ - private static GLFWFramebufferSizeCallback resizeWindow = new GLFWFramebufferSizeCallback(){ + private static final GLFWFramebufferSizeCallback resizeWindow = new GLFWFramebufferSizeCallback(){ @Override public void invoke(long window, int width, int height){ glViewport(0,0,width,height); diff --git a/src/engine/Input.java b/src/engine/Input.java index c5b1e4e..d93f485 100644 --- a/src/engine/Input.java +++ b/src/engine/Input.java @@ -3,6 +3,9 @@ package engine; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWKeyCallback; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; + public class Input extends GLFWKeyCallback { public static boolean[] keys = new boolean[65536]; @@ -10,5 +13,10 @@ public class Input extends GLFWKeyCallback { @Override public void invoke(long window, int key, int scancode, int action, int mods) { keys[key] = action != GLFW.GLFW_RELEASE; + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + else if(key == GLFW_KEY_SPACE && action == GLFW_PRESS) //Switch to wireframe + if (glGetInteger(GL_POLYGON_MODE) == GL_FILL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java index 887595e..34e1903 100644 --- a/src/engine/Primitive.java +++ b/src/engine/Primitive.java @@ -8,4 +8,20 @@ public class Primitive { -0.5f, -0.5f, 0.0f, }; + public static byte[] triangle_indices = new byte[]{ + 0, 1, 2 + }; + + public static float[] rectangle = new float[] { + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left + }; + + public static byte[] rectangle_indices = new byte[] { + 0, 1, 3, + 1, 2, 3 + }; + } diff --git a/src/engine/Scene.java b/src/engine/Scene.java index 301c470..3edf273 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -3,14 +3,16 @@ package engine; public class Scene { float[] vertices; + byte[] indices; VertexArray vertexArray; Shader shader; - public Scene(String vertPath, String fragPath, float[] vertices){ + public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices){ this.vertices = vertices; + this.indices = indices; this.shader = new Shader(vertPath, fragPath); - this.vertexArray = new VertexArray(this.vertices); + this.vertexArray = new VertexArray(this.vertices, this.indices); } public void render(){ diff --git a/src/engine/VertexArray.java b/src/engine/VertexArray.java index e7c64aa..d14daaf 100644 --- a/src/engine/VertexArray.java +++ b/src/engine/VertexArray.java @@ -6,22 +6,26 @@ import static org.lwjgl.opengl.GL30.*; public class VertexArray { - private int VAO ,VBO; + private int VAO ,VBO, EBO; + private int count; - public VertexArray(float[] vertices){ + public VertexArray(float[] vertices, byte[] indices){ + count = indices.length; // VERTEX ARRAY OBJECT VAO = glGenVertexArrays(); + glBindVertexArray(VAO); // VERTEX BUFFER OBJECT VBO = glGenBuffers(); - glBindVertexArray(VAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(0); + EBO = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -36,7 +40,7 @@ public class VertexArray { } public void draw(){ - glDrawArrays(GL_TRIANGLES, 0, 3); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); } public void render(){ From 049e735e1ab5cbf8395ede3d1029ef8e867929af Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 14 May 2021 15:34:08 +0200 Subject: [PATCH 07/53] setUniform4f in Shader --- shaders/frag.frag | 5 ++++- shaders/vert.vert | 3 +++ src/engine/Engine.java | 2 +- src/engine/Primitive.java | 41 +++++++++++++++++++++++++++++++++++++++ src/engine/Scene.java | 5 +++++ src/engine/Shader.java | 5 +++++ 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/shaders/frag.frag b/shaders/frag.frag index 8728871..98b8780 100644 --- a/shaders/frag.frag +++ b/shaders/frag.frag @@ -1,7 +1,10 @@ #version 330 core + +uniform float time; + out vec4 FragColor; void main() { - FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); + FragColor = vec4(sin(time), sin(time/2), sin(time/4), 1.0f); } \ No newline at end of file diff --git a/shaders/vert.vert b/shaders/vert.vert index cdd6dd3..4c407e2 100644 --- a/shaders/vert.vert +++ b/shaders/vert.vert @@ -1,4 +1,7 @@ #version 330 core + +uniform float time; + layout (location = 0) in vec3 aPos; void main() diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 667430d..57691d0 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -63,7 +63,7 @@ public class Engine { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); - this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.rectangle, Primitive.rectangle_indices); + this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.triangle, Primitive.triangle_indices); } diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java index 34e1903..cb6477d 100644 --- a/src/engine/Primitive.java +++ b/src/engine/Primitive.java @@ -2,6 +2,33 @@ package engine; public class Primitive { + public float[] vertices; + public byte[] indices; + + public Primitive(Vector3f vertex1, Vector3f vertex2, Vector3f vertex3){ + vertices = new float[]{ + vertex1.x, vertex1.y, vertex1.z, + vertex2.x, vertex2.y, vertex2.z, + vertex3.x, vertex3.y, vertex3.z + }; + indices = new byte[]{ + 0, 1, 2 + }; + } + + public Primitive(Vector3f vertex1, Vector3f vertex2, Vector3f vertex3, Vector3f vertex4){ + vertices = new float[]{ + vertex1.x, vertex1.y, vertex1.z, + vertex2.x, vertex2.y, vertex2.z, + vertex3.x, vertex3.y, vertex3.z, + vertex4.x, vertex4.y, vertex4.z + }; + indices = new byte[]{ + 0, 1, 2, + 1, 2, 3 + }; + } + public static float[] triangle = new float[] { 0.0f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, @@ -12,6 +39,20 @@ public class Primitive { 0, 1, 2 }; + public static float[] two_triangle = new float[] { + 0.0f, 0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + -0.5f, -0.5f, 0.0f, + -0.8f, -0.8f, 0.0f, + 0.8f, -0.8f, 0.0f, + 0.8f, -0.9f, 0.0f, + }; + + public static byte[] two_triangle_indices = new byte[]{ + 0, 1, 2, + 3, 4, 5 + }; + public static float[] rectangle = new float[] { 0.5f, 0.5f, 0.0f, // top right 0.5f, -0.5f, 0.0f, // bottom right diff --git a/src/engine/Scene.java b/src/engine/Scene.java index 3edf273..2608078 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -1,5 +1,9 @@ package engine; +import static java.lang.Math.cos; +import static java.lang.Math.sin; +import static org.lwjgl.glfw.GLFW.glfwGetTime; + public class Scene { float[] vertices; @@ -17,6 +21,7 @@ public class Scene { public void render(){ this.shader.enable(); + this.shader.setUniform1f("time", (float)glfwGetTime()); this.vertexArray.render(); this.shader.disable(); } diff --git a/src/engine/Shader.java b/src/engine/Shader.java index c573885..c656a3d 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -48,6 +48,11 @@ public class Shader { glUniform3f(getUniform(name), vector.x, vector.y, vector.z); } + public void setUniform4f(String name, float x, float y, float z, float w) { + if (!enabled) enable(); + glUniform4f(getUniform(name), x, y, z, w); + } + public void setUniformMat4f(String name, Matrix4f matrix){ if (!enabled) enable(); glUniformMatrix4fv(getUniform(name), false, matrix.toFloatBuffer()); From e1cbbb85eb7fe1ef457a481e07f0e9a36df97051 Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 14 May 2021 18:47:05 +0200 Subject: [PATCH 08/53] Ajout d'un canal couleur dans le vertexArray --- shaders/frag.frag | 6 ++-- shaders/vert.vert | 8 +++-- src/engine/Engine.java | 2 +- src/engine/Primitive.java | 14 +++++++++ src/engine/Scene.java | 10 +++++- src/engine/Texture.java | 61 ++++++++++++++++++++++++++++++++++++ src/engine/VertexArray.java | 46 +++++++++++++++++++++++---- textures/container.jpg | Bin 0 -> 184939 bytes 8 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 src/engine/Texture.java create mode 100644 textures/container.jpg diff --git a/shaders/frag.frag b/shaders/frag.frag index 98b8780..4a9e16a 100644 --- a/shaders/frag.frag +++ b/shaders/frag.frag @@ -1,10 +1,10 @@ #version 330 core -uniform float time; - out vec4 FragColor; +in vec3 Color; + void main() { - FragColor = vec4(sin(time), sin(time/2), sin(time/4), 1.0f); + FragColor = vec4(Color, 1.0); } \ No newline at end of file diff --git a/shaders/vert.vert b/shaders/vert.vert index 4c407e2..f4fdd74 100644 --- a/shaders/vert.vert +++ b/shaders/vert.vert @@ -1,10 +1,12 @@ #version 330 core -uniform float time; +layout (location = 0) in vec3 aPos; // la variable aPos a l'attribut de position 0 +layout (location = 1) in vec3 aColor; -layout (location = 0) in vec3 aPos; +out vec3 Color; void main() { - gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); + gl_Position = vec4(aPos, 1.0); + Color = aColor; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 57691d0..57f53fa 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -63,7 +63,7 @@ public class Engine { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); - this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.triangle, Primitive.triangle_indices); + this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.triangle, Primitive.triangle_indices, Primitive.triangle_couleur); } diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java index cb6477d..7f1da5d 100644 --- a/src/engine/Primitive.java +++ b/src/engine/Primitive.java @@ -35,6 +35,12 @@ public class Primitive { -0.5f, -0.5f, 0.0f, }; + public static float[] triangle_couleur = new float[] { + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f + }; + public static byte[] triangle_indices = new byte[]{ 0, 1, 2 }; @@ -60,6 +66,14 @@ public class Primitive { -0.5f, 0.5f, 0.0f // top left }; + public static float[] rectangle_texture = new float[]{ + // positions // colors // texture coords + 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom left + -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // top left + }; + public static byte[] rectangle_indices = new byte[] { 0, 1, 3, 1, 2, 3 diff --git a/src/engine/Scene.java b/src/engine/Scene.java index 2608078..eb6a8e3 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -7,6 +7,7 @@ import static org.lwjgl.glfw.GLFW.glfwGetTime; public class Scene { float[] vertices; + float[] color; byte[] indices; VertexArray vertexArray; @@ -19,9 +20,16 @@ public class Scene { this.vertexArray = new VertexArray(this.vertices, this.indices); } + public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices, float[] color){ + this.vertices = vertices; + this.indices = indices; + this.color = color; + this.shader = new Shader(vertPath, fragPath); + this.vertexArray = new VertexArray(this.vertices, this.indices, this.color); + } + public void render(){ this.shader.enable(); - this.shader.setUniform1f("time", (float)glfwGetTime()); this.vertexArray.render(); this.shader.disable(); } diff --git a/src/engine/Texture.java b/src/engine/Texture.java new file mode 100644 index 0000000..f3f8675 --- /dev/null +++ b/src/engine/Texture.java @@ -0,0 +1,61 @@ +package engine; + +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL30.*; + +public class Texture { + + private int width, height; + private int texture; + + public Texture(String path) { + texture = load(path); + } + + private int load(String path) { + int[] pixels = null; + try { + BufferedImage image = ImageIO.read(new FileInputStream(path)); + width = image.getWidth(); + height = image.getHeight(); + pixels = new int[width * height]; + image.getRGB(0, 0, width, height, pixels, 0, width); + } catch (IOException e) { + e.printStackTrace(); + } + + int[] data = new int[width * height]; + for (int i = 0; i < width * height; i++) { + int a = (pixels[i] & 0xff000000) >> 24; + int r = (pixels[i] & 0xff0000) >> 16; + int g = (pixels[i] & 0xff00) >> 8; + int b = (pixels[i] & 0xff); + + data[i] = a << 24 | b << 16 | g << 8 | r; + } + + int result = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, result); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + return result; + } + + public void bind() { + glBindTexture(GL_TEXTURE_2D, texture); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } + +} diff --git a/src/engine/VertexArray.java b/src/engine/VertexArray.java index d14daaf..a338b25 100644 --- a/src/engine/VertexArray.java +++ b/src/engine/VertexArray.java @@ -6,7 +6,7 @@ import static org.lwjgl.opengl.GL30.*; public class VertexArray { - private int VAO ,VBO, EBO; + private int VAO ,VBO, EBO, CBO; private int count; public VertexArray(float[] vertices, byte[] indices){ @@ -16,11 +16,7 @@ public class VertexArray { glBindVertexArray(VAO); // VERTEX BUFFER OBJECT - VBO = glGenBuffers(); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); - glEnableVertexAttribArray(0); + createVertexBufferObject(vertices); EBO = glGenBuffers(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); @@ -31,6 +27,44 @@ public class VertexArray { } + public VertexArray(float[] vertices, byte[] indices, float[] color) { + count = indices.length; + // VERTEX ARRAY OBJECT + VAO = glGenVertexArrays(); + glBindVertexArray(VAO); + + glEnableVertexAttribArray(0); + + // VERTEX BUFFER OBJECT + createVertexBufferObject(vertices); + // COLOR BUFFER OBJECT + createColorBufferObject(color); + + EBO = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + } + + private void createVertexBufferObject(float[] vertices){ + VBO = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(0); + } + + private void createColorBufferObject(float[] color){ + CBO = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, CBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(color), GL_STATIC_DRAW); + glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(1); + } + public void bind(){ glBindVertexArray(this.VAO); } diff --git a/textures/container.jpg b/textures/container.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d07bee4e3a679f5e2e4ad508ef3c4dc29a522492 GIT binary patch literal 184939 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

d`XyO#5L!x`fys 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[SQrQz8sg z+R!ql+}t^>Em*ss9mbWTqyuw5EAGD={} zk_i`ZVv1uOuW_`2rTg)EbNhou?r!C=J3aL>o6zn_iWW8W(zIKPG$L6OYUywo5POX| zQ@nQsK&KPCR`IjnT-VXhOKFUcPlwhqKJMy@`peuCt--a8`*GB76Z~m9_i=$V{JxMn zOw`eB!{(5@f2CUbsn{&N?g%}Fym_IYJv(d1cUq`v=`*I3lTT4C3`z(g1w7RT*Bef( zdlB4mm-BZBfN6EMT$CBYqCcgMnPEE;ToM}Ub9og6k0eTNPuY7L>)(h*ZE|oXmOEn8 zhY%_R{*}5}i?WBx!fEJ1i1{xl+8A6dl=ypip}g7j;7wJ_2GmD~WitN&vF}W2E z)ngf|Tv#@-1Pb(ilKOav(Y8^A3WD2VX#w$V2q(R-&96ILZKP?*8QB$%KS&umdWDKS zmSpqD5to)(6K%B(xc*$Y&s;odzvK)g%u}O!Lh1GeJT3i8r-G_!MLjws#dj$e(O8@J zJfib;kp$I-RN6>{)2=P)Um?@R)fk>vrdX3yhLzB|hP}}3_VdhU%h(Ni<0ocjDp>SW z(8%dNX)?;v$o869(r5DH1xzN zhzMQ89n1+P{f4}=x&6u#2s?VJ)?2*8goDDe<=;-d3dy+rWVwF;lK{lzr&O;#V@VXy zO}k+R0eK9D^XIq+LG9&lGndwpsNq|*$(c&dk+ZIYex-C5AZ96K$+*v*jNzF4l-Q*$ z6?4c^Sty;vO>tvr_plz)ejIkkFmXS$PYP!+vP$8lVk%Zy^e55=MW`7*oKAySgH$UY z&*R^oR`c8wx7}4cxWL3Dk6KM+)&w8jJ-MU2i|M4z?&1#$8}&Wz#k$YaTg6ppRs3(B^AmJC2T>O6SC z%$FFNS~Aqm6$=#US43FE@Z=bTw5M6>8dqTDc9OOu>gJJhvN1Ka*@TnjT}9GPnc`r< zCPJm261$c|dY*l#4a6sA!H`3l5 z+A{(;)5k0aZ9*L#=~${@nF5QNg{gTW$T)k_ zRNiS(sn=HQH8=ho*Yg7y8gONZoN6Ucr<^#(l(;Tl!s)O{om#SpQjOaKTGL*0kWV~ zdVlEEElFuQd*&?1--=ltNbc9ZjDC*Xsm@&oZ6rB8xZzNL%*RL~h#2~Dhn99^Up64y zEvDOE)&q_sHgbOIsmU1VsPECvnw4_hHNy=n8%Q1kLyO;AeH_ruoCof$Ig_)|NgTn@ zE|pgze<|s94fAn((nnoN)@^PT3LeAb#?_l#4Thg*DzxG~^_wd6OQy7q1J`tpknAOE zA*ZEV62Vk>_?w+LnC7=rGfO{}*~ZFS*Lrd2z95mxIxmH_Ln#kEL`Vtm*+Czl6{kD6 zDceQs$n444@2+@nrGAz)QAbJA%qo(hfDbfi(4k}PD&Pa*emHYy#xqtKJ+M{--&o@G z&C{`4zOyM|czsnN*wRHzk~;<)Zn7&_j|0YD&fLcuvl&Y;Nfhlbr;MX5J4OC(XfZ&F z?2|wRIxuFqKKAcr?Zk&Vd#0ABBwz|gucGX|jBLzxxlabcqZJVkW+Z)= zMgfC+IJVn8btyAQF0v+-vVuIb)4I%GG3H#2Q&k=dhh*75onW}O7`|bsH4f!WQVW1? znuFU;7Uow6P;nx4QQlUB7Rzcer)40pJfV%%^^YIJ@qDz6jpj;KijNef3ltWnRDwaZ zTl9OJ?mez`?aE&&ZGi_4G?vLTa8gIDOvGl)I2LomaE!;6s}`DY;)0sI?%z{RgdM^g z+BW^x_Upt}_VUqKG`8u^?ck0bHUHKa={k8+jG2Fwx>rF%D8vj>0?LxgwhW~EMeKP4 zDIzL-E_!Delo8alZmBhgpdism$i5` zdT**+J-$&SwJXJDx`F_(xc2ccc(lxlUge`J8-6tz>7HQlB^-}5B)qzKE|(Xz#rU8r zP!chwzT5%AoU-N@46P+rBT+sdG+@UJF`?c`JA3Kid35Sm3aa1=Rt&j>v~xnKDNPtW zs-T;j>#l@xQ()mpUBa31T(>$?%D8oE#S_@S&D(GRZ(z&McX-J)M5;w4>9S)hiL0xY zp$OXYDzN}}xcW~#8^&e@Td3`>Q{{*nswKjvK;qkkrBt1Jh2zOS%4fTSjVUOyb|L0@ zlI+`9cFZloq(MNmmSBC9V&`ymGu25#0*OPw6W61L*dOfPONHyW@tZbrBqr|TLhHu3sbCfI=byv+i*>;G~$G} z7}AdUDm5M~fXNF~$99ah*f9*k{{X|AQee_@MDZ55M@V~>v^JUp`v@Oz6H0?!~@K=>3^QCHt zficN6YE)lcLjpLlFKIg|g=NH|W}NG??jML^a7T#aPaN(c@+AmJd@ZLKQDm4HQ zHP`9oflNhGJ@t;nx+t+7ZdhBcWK)G;(}QKK4-&&oR8{LvDO4dtYp}kz7bNpUX*1JW zT7*HSm&>?KR8bhJmMUfq3(Om9f}>Nlhhy+OveKe(tLhvnrVf$i>_ZKl;j#*LyH#Qn zEUpBMP0r#7Q50V1;aFk>qOn1PX0 zU2knK?cyt!6oWH`DP*N-h{mQIRv-+sLk&C@-qOm*7^t{94<2pHlc%DpmNb}&q^>KC zVf8b+Ls1e&2#i!aC|4{mp>8{Aa23}Tw{29MYlats$BD9&GO|ikOWfN*!!$ZL zQWeOhYUN6rc#O0)am53N$R0e~mnls$-L|9U zOf|XV0>bHP=Wj0$qIkmz3oNXBNC=)bx$nfP&6^(DRE(j*s>Ks|^zdR28_P>j-eeNQ zh`_1$fgwR4TZJ+KNak_Y@-Y=Ok!ywxnY`aN15vI^b5;*bWZqy`)&rBaSF<(4VB=XIGRI$CxW6`XO3u|Nj={-G>Kd&0d2Tz%A$5vnEdu0Fs literal 0 HcmV?d00001 From 5d93325b3df4694bb22e2f2a1793a5c1843d7691 Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 14 May 2021 20:46:17 +0200 Subject: [PATCH 09/53] Ajout d'un canal texture dans le vertexArray --- shaders/frag.frag | 8 +++++--- shaders/vert.vert | 10 ++++++---- src/engine/Engine.java | 2 +- src/engine/Primitive.java | 16 +++++++++++----- src/engine/Scene.java | 14 +++++++++----- src/engine/VertexArray.java | 34 +++++++++++++++++++++++++++++++++- 6 files changed, 65 insertions(+), 19 deletions(-) diff --git a/shaders/frag.frag b/shaders/frag.frag index 4a9e16a..01a4d15 100644 --- a/shaders/frag.frag +++ b/shaders/frag.frag @@ -1,10 +1,12 @@ #version 330 core - out vec4 FragColor; -in vec3 Color; +in vec3 ourColor; +in vec2 TexCoord; + +uniform sampler2D ourTexture; void main() { - FragColor = vec4(Color, 1.0); + FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0f); } \ No newline at end of file diff --git a/shaders/vert.vert b/shaders/vert.vert index f4fdd74..311ec24 100644 --- a/shaders/vert.vert +++ b/shaders/vert.vert @@ -1,12 +1,14 @@ #version 330 core - -layout (location = 0) in vec3 aPos; // la variable aPos a l'attribut de position 0 +layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; +layout (location = 2) in vec2 aTexCoord; -out vec3 Color; +out vec3 ourColor; +out vec2 TexCoord; void main() { gl_Position = vec4(aPos, 1.0); - Color = aColor; + ourColor = aColor; + TexCoord = aTexCoord; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 57f53fa..1f3a370 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -63,7 +63,7 @@ public class Engine { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); - this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.triangle, Primitive.triangle_indices, Primitive.triangle_couleur); + this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.rectangle, Primitive.rectangle_indices, Primitive.rectangle_color, Primitive.rectangle_texture); } diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java index 7f1da5d..e4a44df 100644 --- a/src/engine/Primitive.java +++ b/src/engine/Primitive.java @@ -66,12 +66,18 @@ public class Primitive { -0.5f, 0.5f, 0.0f // top left }; + public static float[] rectangle_color = new float[]{ + 1.0f, 0.0f, 0.0f, // top right + 0.0f, 1.0f, 0.0f, // bottom right + 0.0f, 0.0f, 1.0f, // bottom left + 1.0f, 1.0f, 0.0f, // top left + }; + public static float[] rectangle_texture = new float[]{ - // positions // colors // texture coords - 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // top right - 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom right - -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom left - -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // top left + 1.0f, 1.0f, + 1.0f, 0.0f, + 0.0f, 0.0f, + 0.0f, 1.0f }; public static byte[] rectangle_indices = new byte[] { diff --git a/src/engine/Scene.java b/src/engine/Scene.java index eb6a8e3..5f791cf 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -1,13 +1,13 @@ package engine; -import static java.lang.Math.cos; -import static java.lang.Math.sin; -import static org.lwjgl.glfw.GLFW.glfwGetTime; +import static org.lwjgl.opengl.GL11.*; public class Scene { float[] vertices; float[] color; + float[] texture; + Texture texture_map; byte[] indices; VertexArray vertexArray; @@ -20,17 +20,21 @@ public class Scene { this.vertexArray = new VertexArray(this.vertices, this.indices); } - public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices, float[] color){ + public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices, float[] color, float[] texture){ this.vertices = vertices; this.indices = indices; this.color = color; this.shader = new Shader(vertPath, fragPath); - this.vertexArray = new VertexArray(this.vertices, this.indices, this.color); + this.texture_map = new Texture("textures/container.jpg"); + this.texture = texture; + this.vertexArray = new VertexArray(this.vertices, this.indices, this.color, this.texture); } public void render(){ this.shader.enable(); + this.texture_map.bind(); this.vertexArray.render(); + this.texture_map.unbind(); this.shader.disable(); } } diff --git a/src/engine/VertexArray.java b/src/engine/VertexArray.java index a338b25..9727ec3 100644 --- a/src/engine/VertexArray.java +++ b/src/engine/VertexArray.java @@ -6,7 +6,7 @@ import static org.lwjgl.opengl.GL30.*; public class VertexArray { - private int VAO ,VBO, EBO, CBO; + private int VAO ,VBO, EBO, CBO, TBO; private int count; public VertexArray(float[] vertices, byte[] indices){ @@ -49,6 +49,30 @@ public class VertexArray { } + public VertexArray(float[] vertices, byte[] indices, float[] color, float[] texture) { + count = indices.length; + // VERTEX ARRAY OBJECT + VAO = glGenVertexArrays(); + glBindVertexArray(VAO); + + glEnableVertexAttribArray(0); + + // VERTEX BUFFER OBJECT + createVertexBufferObject(vertices); + // COLOR BUFFER OBJECT + createColorBufferObject(color); + // TEXTURE BUFFER OBJECT + createTextureBufferObject(texture); + + EBO = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + } + private void createVertexBufferObject(float[] vertices){ VBO = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, VBO); @@ -65,6 +89,14 @@ public class VertexArray { glEnableVertexAttribArray(1); } + private void createTextureBufferObject(float[] texture){ + TBO = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, TBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(texture), GL_STATIC_DRAW); + glVertexAttribPointer(2, 2, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(2); + } + public void bind(){ glBindVertexArray(this.VAO); } From 202c141e4d1c2bd4212cdb35d28f171e82b2d625 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 05:39:23 +0200 Subject: [PATCH 10/53] Organisation --- shaders/frag.frag | 6 ++++-- shaders/vert.vert | 2 ++ src/engine/Primitive.java | 8 +++++--- src/engine/Scene.java | 14 ++++++++++++-- src/engine/{ => graphics}/Shader.java | 6 +++++- src/engine/{ => graphics}/Texture.java | 12 +++++++++--- src/engine/{ => graphics}/VertexArray.java | 7 +++++-- src/engine/{ => math}/Matrix4f.java | 4 +++- src/engine/{ => math}/Vector3f.java | 2 +- src/engine/{ => utils}/BufferUtils.java | 2 +- src/engine/{ => utils}/FileUtils.java | 2 +- src/engine/{ => utils}/ShaderUtils.java | 4 +++- textures/awesomeface.png | Bin 0 -> 40855 bytes 13 files changed, 51 insertions(+), 18 deletions(-) rename src/engine/{ => graphics}/Shader.java (94%) rename src/engine/{ => graphics}/Texture.java (81%) rename src/engine/{ => graphics}/VertexArray.java (94%) rename src/engine/{ => math}/Matrix4f.java (97%) rename src/engine/{ => math}/Vector3f.java (92%) rename src/engine/{ => utils}/BufferUtils.java (97%) rename src/engine/{ => utils}/FileUtils.java (96%) rename src/engine/{ => utils}/ShaderUtils.java (97%) create mode 100644 textures/awesomeface.png diff --git a/shaders/frag.frag b/shaders/frag.frag index 01a4d15..9642779 100644 --- a/shaders/frag.frag +++ b/shaders/frag.frag @@ -3,10 +3,12 @@ out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; +in vec4 position; -uniform sampler2D ourTexture; +uniform sampler2D texture1; +uniform sampler2D texture2; void main() { - FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0f); + FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord.x, -TexCoord.y)), position.x * position.y); } \ No newline at end of file diff --git a/shaders/vert.vert b/shaders/vert.vert index 311ec24..fac18d7 100644 --- a/shaders/vert.vert +++ b/shaders/vert.vert @@ -5,10 +5,12 @@ layout (location = 2) in vec2 aTexCoord; out vec3 ourColor; out vec2 TexCoord; +out vec4 position; void main() { gl_Position = vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; + position = gl_Position; } \ No newline at end of file diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java index e4a44df..e072155 100644 --- a/src/engine/Primitive.java +++ b/src/engine/Primitive.java @@ -1,5 +1,7 @@ package engine; +import engine.math.Vector3f; + public class Primitive { public float[] vertices; @@ -74,10 +76,10 @@ public class Primitive { }; public static float[] rectangle_texture = new float[]{ - 1.0f, 1.0f, - 1.0f, 0.0f, + 2.0f, 2.0f, + 2.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f + 0.0f, 2.0f }; public static byte[] rectangle_indices = new byte[] { diff --git a/src/engine/Scene.java b/src/engine/Scene.java index 5f791cf..a6f136a 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -1,13 +1,17 @@ package engine; -import static org.lwjgl.opengl.GL11.*; +import engine.graphics.Shader; +import engine.graphics.Texture; +import engine.graphics.VertexArray; public class Scene { float[] vertices; float[] color; float[] texture; + float[] texture2; Texture texture_map; + Texture texture_map2; byte[] indices; VertexArray vertexArray; @@ -25,15 +29,21 @@ public class Scene { this.indices = indices; this.color = color; this.shader = new Shader(vertPath, fragPath); - this.texture_map = new Texture("textures/container.jpg"); + this.texture_map = new Texture("textures/container.jpg", 0); this.texture = texture; + this.texture_map2 = new Texture("textures/awesomeface.png", 1); + this.texture2 = texture; this.vertexArray = new VertexArray(this.vertices, this.indices, this.color, this.texture); + shader.setUniform1i("texture1", 0); + shader.setUniform1i("texture2", 1); } public void render(){ this.shader.enable(); this.texture_map.bind(); + this.texture_map2.bind(); this.vertexArray.render(); + this.texture_map2.unbind(); this.texture_map.unbind(); this.shader.disable(); } diff --git a/src/engine/Shader.java b/src/engine/graphics/Shader.java similarity index 94% rename from src/engine/Shader.java rename to src/engine/graphics/Shader.java index c656a3d..19ae290 100644 --- a/src/engine/Shader.java +++ b/src/engine/graphics/Shader.java @@ -1,4 +1,8 @@ -package engine; +package engine.graphics; + +import engine.math.Matrix4f; +import engine.utils.ShaderUtils; +import engine.math.Vector3f; import java.util.HashMap; import java.util.Map; diff --git a/src/engine/Texture.java b/src/engine/graphics/Texture.java similarity index 81% rename from src/engine/Texture.java rename to src/engine/graphics/Texture.java index f3f8675..78a23a4 100644 --- a/src/engine/Texture.java +++ b/src/engine/graphics/Texture.java @@ -1,4 +1,7 @@ -package engine; +package engine.graphics; + +import engine.utils.BufferUtils; +import org.lwjgl.opengl.GL11; import java.awt.image.BufferedImage; import java.io.FileInputStream; @@ -13,8 +16,10 @@ public class Texture { private int width, height; private int texture; + private int index; - public Texture(String path) { + public Texture(String path, int index) { + this.index = index; texture = load(path); } @@ -44,13 +49,14 @@ public class Texture { glBindTexture(GL_TEXTURE_2D, result); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); + GL11.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); return result; } public void bind() { + glActiveTexture(GL_TEXTURE0 + this.index); glBindTexture(GL_TEXTURE_2D, texture); } diff --git a/src/engine/VertexArray.java b/src/engine/graphics/VertexArray.java similarity index 94% rename from src/engine/VertexArray.java rename to src/engine/graphics/VertexArray.java index 9727ec3..f76c654 100644 --- a/src/engine/VertexArray.java +++ b/src/engine/graphics/VertexArray.java @@ -1,4 +1,7 @@ -package engine; +package engine.graphics; + +import engine.utils.BufferUtils; +import org.lwjgl.opengl.GL15; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL15.*; @@ -20,7 +23,7 @@ public class VertexArray { EBO = glGenBuffers(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + GL15.glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); diff --git a/src/engine/Matrix4f.java b/src/engine/math/Matrix4f.java similarity index 97% rename from src/engine/Matrix4f.java rename to src/engine/math/Matrix4f.java index 2c1dcc2..f8be361 100644 --- a/src/engine/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -1,4 +1,6 @@ -package engine; +package engine.math; + +import engine.utils.BufferUtils; import java.nio.FloatBuffer; diff --git a/src/engine/Vector3f.java b/src/engine/math/Vector3f.java similarity index 92% rename from src/engine/Vector3f.java rename to src/engine/math/Vector3f.java index 928e33b..af4015b 100644 --- a/src/engine/Vector3f.java +++ b/src/engine/math/Vector3f.java @@ -1,4 +1,4 @@ -package engine; +package engine.math; public class Vector3f { diff --git a/src/engine/BufferUtils.java b/src/engine/utils/BufferUtils.java similarity index 97% rename from src/engine/BufferUtils.java rename to src/engine/utils/BufferUtils.java index 234dc77..d420b8e 100644 --- a/src/engine/BufferUtils.java +++ b/src/engine/utils/BufferUtils.java @@ -1,4 +1,4 @@ -package engine; +package engine.utils; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/src/engine/FileUtils.java b/src/engine/utils/FileUtils.java similarity index 96% rename from src/engine/FileUtils.java rename to src/engine/utils/FileUtils.java index 1191880..4f82877 100644 --- a/src/engine/FileUtils.java +++ b/src/engine/utils/FileUtils.java @@ -1,4 +1,4 @@ -package engine; +package engine.utils; import java.io.BufferedReader; import java.io.FileNotFoundException; diff --git a/src/engine/ShaderUtils.java b/src/engine/utils/ShaderUtils.java similarity index 97% rename from src/engine/ShaderUtils.java rename to src/engine/utils/ShaderUtils.java index caaf6ac..69879ba 100644 --- a/src/engine/ShaderUtils.java +++ b/src/engine/utils/ShaderUtils.java @@ -1,4 +1,6 @@ -package engine; +package engine.utils; + +import engine.utils.FileUtils; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL20.*; diff --git a/textures/awesomeface.png b/textures/awesomeface.png new file mode 100644 index 0000000000000000000000000000000000000000..c7bfec6614d6120050aa7c36f582f91ba8a271bc GIT binary patch literal 40855 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 z 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 BeDDAO literal 0 HcmV?d00001 From 565bc5edfe8cddd49d2031ba6a8c96be9b8b208b Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 05:45:55 +0200 Subject: [PATCH 11/53] Presentation Matrix4f --- src/engine/math/Matrix4f.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java index f8be361..7e32c00 100644 --- a/src/engine/math/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -28,11 +28,11 @@ public class Matrix4f { public static Matrix4f orthographic(float left, float right, float bottom, float top, float near, float far){ Matrix4f result = identity(); - result.elements[0] = 2.0f / (right - left); - result.elements[1 + 4] = 2.0f / (top - bottom); + result.elements[0 + 0 * 4] = 2.0f / (right - left); + result.elements[1 + 1 * 4] = 2.0f / (top - bottom); result.elements[2 + 2 * 4] = 2.0f / (near - far); - result.elements[3 * 4] = (left + right) / (left - right); + result.elements[0 + 3 * 4] = (left + right) / (left - right); result.elements[1 + 3 * 4] = (bottom + top) / (bottom - top); result.elements[2 + 3 * 4] = (far + near) / (far - near); From 4c7e32f44b00d3e9d47e043687830a346fd545f2 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 05:52:22 +0200 Subject: [PATCH 12/53] Added rotation on the x and y axis --- src/engine/math/Matrix4f.java | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java index 7e32c00..45fc12c 100644 --- a/src/engine/math/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -48,17 +48,32 @@ public class Matrix4f { return result; } - public static Matrix4f rotate(float angle){ + public static Matrix4f rotateX(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(angle); + float sin = (float) Math.sin(angle); + + result.elements[1 + 1 * 4] = cos; + result.elements[2 + 1 * 4] = -sin; + + result.elements[1 + 2 * 4] = sin; + result.elements[2 + 2 * 4] = cos; + + return result; + } + + public static Matrix4f rotateY(float angle){ Matrix4f result = identity(); float r = (float) Math.toRadians(angle); float cos = (float) Math.cos(angle); float sin = (float) Math.sin(angle); result.elements[0 + 0 * 4] = cos; - result.elements[1 + 0 * 4] = sin; + result.elements[2 + 0 * 4] = sin; - result.elements[0 + 1 * 4] = -sin; - result.elements[1 + 1 * 4] = cos; + result.elements[0 + 2 * 4] = -sin; + result.elements[2 + 2 * 4] = cos; return result; } From e241c7c9b150b3f844d64c7e95cbc1ad6e6479e0 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 05:56:37 +0200 Subject: [PATCH 13/53] Added rotation on the z axis --- src/engine/math/Matrix4f.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java index 45fc12c..441237e 100644 --- a/src/engine/math/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -78,6 +78,21 @@ public class Matrix4f { return result; } + public static Matrix4f rotateZ(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(angle); + float sin = (float) Math.sin(angle); + + result.elements[0 + 0 * 4] = cos; + result.elements[1 + 0 * 4] = -sin; + + result.elements[0 + 1 * 4] = sin; + result.elements[2 + 1 * 4] = cos; + + return result; + } + public Matrix4f multiply(Matrix4f matrix){ Matrix4f result = new Matrix4f(); for (int y = 0; y< 4; y++){ From 2570553b939b1272ba18c360ec993d5f88b0705a Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 14:53:16 +0200 Subject: [PATCH 14/53] transform added --- shaders/vert.vert | 4 +++- src/engine/Scene.java | 7 +++++++ src/engine/math/Matrix4f.java | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/shaders/vert.vert b/shaders/vert.vert index fac18d7..0b3006e 100644 --- a/shaders/vert.vert +++ b/shaders/vert.vert @@ -7,9 +7,11 @@ out vec3 ourColor; out vec2 TexCoord; out vec4 position; +uniform mat4 transform; + void main() { - gl_Position = vec4(aPos, 1.0); + gl_Position = transform * vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; position = gl_Position; diff --git a/src/engine/Scene.java b/src/engine/Scene.java index a6f136a..588e594 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -3,6 +3,7 @@ package engine; import engine.graphics.Shader; import engine.graphics.Texture; import engine.graphics.VertexArray; +import engine.math.Matrix4f; public class Scene { @@ -14,6 +15,8 @@ public class Scene { Texture texture_map2; byte[] indices; + Matrix4f transform; + VertexArray vertexArray; Shader shader; @@ -36,9 +39,13 @@ public class Scene { this.vertexArray = new VertexArray(this.vertices, this.indices, this.color, this.texture); shader.setUniform1i("texture1", 0); shader.setUniform1i("texture2", 1); + this.transform = Matrix4f.rotateZ(3.14f/2.0f); + shader.setUniformMat4f("transform", this.transform); } public void render(){ +// this.transform = this.transform.multiply(Matrix4f.rotateZ(0.001f)); + shader.setUniformMat4f("transform", this.transform); this.shader.enable(); this.texture_map.bind(); this.texture_map2.bind(); diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java index 441237e..af542db 100644 --- a/src/engine/math/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -88,7 +88,7 @@ public class Matrix4f { result.elements[1 + 0 * 4] = -sin; result.elements[0 + 1 * 4] = sin; - result.elements[2 + 1 * 4] = cos; + result.elements[1 + 1 * 4] = cos; return result; } From 5f4b2aa56e685001457160c6b08c355891e24f5e Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 18:29:58 +0200 Subject: [PATCH 15/53] Problem where the angle given in the rotation function was not converted to a radian --- src/engine/Engine.java | 2 +- src/engine/Scene.java | 7 +++++-- src/engine/math/Matrix4f.java | 12 ++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 1f3a370..5673693 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -60,7 +60,7 @@ public class Engine { glfwSetFramebufferSizeCallback(window, resizeWindow); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.rectangle, Primitive.rectangle_indices, Primitive.rectangle_color, Primitive.rectangle_texture); diff --git a/src/engine/Scene.java b/src/engine/Scene.java index 588e594..fce704e 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -4,6 +4,7 @@ import engine.graphics.Shader; import engine.graphics.Texture; import engine.graphics.VertexArray; import engine.math.Matrix4f; +import engine.math.Vector3f; public class Scene { @@ -39,12 +40,14 @@ public class Scene { this.vertexArray = new VertexArray(this.vertices, this.indices, this.color, this.texture); shader.setUniform1i("texture1", 0); shader.setUniform1i("texture2", 1); - this.transform = Matrix4f.rotateZ(3.14f/2.0f); + this.transform = Matrix4f.translate(new Vector3f(-0.1f, 0.2f, 0.0f)); + this.transform = this.transform.multiply(Matrix4f.rotateZ(90.0f)); shader.setUniformMat4f("transform", this.transform); } public void render(){ -// this.transform = this.transform.multiply(Matrix4f.rotateZ(0.001f)); + this.transform = this.transform.multiply(Matrix4f.rotateZ(1.0f)); + this.transform = this.transform.multiply(Matrix4f.translate(new Vector3f(-0.001f, 0.0f, 0.0f))); shader.setUniformMat4f("transform", this.transform); this.shader.enable(); this.texture_map.bind(); diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java index af542db..8ad6cb9 100644 --- a/src/engine/math/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -51,8 +51,8 @@ public class Matrix4f { public static Matrix4f rotateX(float angle){ Matrix4f result = identity(); float r = (float) Math.toRadians(angle); - float cos = (float) Math.cos(angle); - float sin = (float) Math.sin(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); result.elements[1 + 1 * 4] = cos; result.elements[2 + 1 * 4] = -sin; @@ -66,8 +66,8 @@ public class Matrix4f { public static Matrix4f rotateY(float angle){ Matrix4f result = identity(); float r = (float) Math.toRadians(angle); - float cos = (float) Math.cos(angle); - float sin = (float) Math.sin(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); result.elements[0 + 0 * 4] = cos; result.elements[2 + 0 * 4] = sin; @@ -81,8 +81,8 @@ public class Matrix4f { public static Matrix4f rotateZ(float angle){ Matrix4f result = identity(); float r = (float) Math.toRadians(angle); - float cos = (float) Math.cos(angle); - float sin = (float) Math.sin(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); result.elements[0 + 0 * 4] = cos; result.elements[1 + 0 * 4] = -sin; From 2aa23512be766f19dd39628a365401c989dba3d2 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 17 May 2021 19:05:45 +0200 Subject: [PATCH 16/53] z-buffer --- src/engine/Engine.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 5673693..8bee48c 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -60,6 +60,7 @@ public class Engine { glfwSetFramebufferSizeCallback(window, resizeWindow); + glEnable(GL_DEPTH_TEST); // Z-Buffer glClearColor(0.2f, 0.3f, 0.3f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); @@ -73,7 +74,7 @@ public class Engine { private void render(){ glClearColor(0.2f, 0.3f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); //SWAP avec le precedent + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //SWAP avec le precedent scene.render(); int error = glGetError(); if (error != GL_NO_ERROR) System.out.println(error); From da6e5e3be84633159defd383c68f0731c05c4f9e Mon Sep 17 00:00:00 2001 From: Noliere Leo Date: Tue, 18 May 2021 16:38:18 +0000 Subject: [PATCH 17/53] Upload Architecture moteur --- docs/Architecture_Moteur_V1.pdf | Bin 0 -> 33733 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Architecture_Moteur_V1.pdf diff --git a/docs/Architecture_Moteur_V1.pdf b/docs/Architecture_Moteur_V1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..32d5633a4dbf22fe32cb0aa6c5ad24ab535fc841 GIT binary patch literal 33733 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 literal 0 HcmV?d00001 From 4abc87597d41feec965361a2f29a205d8b8dbd07 Mon Sep 17 00:00:00 2001 From: Antoine Date: Tue, 18 May 2021 19:01:18 +0200 Subject: [PATCH 18/53] Added scaling operation in Matrix4f --- shaders/frag.glsl | 10 +++++++ shaders/vert.glsl | 12 +++++++++ src/engine/Engine.java | 3 +++ src/engine/ObjectGl.java | 50 +++++++++++++++++++++++++++++++++++ src/engine/Scene.java | 21 ++++++++++----- src/engine/math/Matrix4f.java | 9 +++++++ 6 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 shaders/frag.glsl create mode 100644 shaders/vert.glsl create mode 100644 src/engine/ObjectGl.java diff --git a/shaders/frag.glsl b/shaders/frag.glsl new file mode 100644 index 0000000..280912a --- /dev/null +++ b/shaders/frag.glsl @@ -0,0 +1,10 @@ +#version 330 core +out vec4 FragColor; + +in vec4 position; +in vec4 Color; + +void main() +{ + FragColor = Color; +} \ No newline at end of file diff --git a/shaders/vert.glsl b/shaders/vert.glsl new file mode 100644 index 0000000..5a32523 --- /dev/null +++ b/shaders/vert.glsl @@ -0,0 +1,12 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; + +out vec4 position; +out vec4 Color; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + Color = vec4(aColor, 1.0f); +} \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 8bee48c..05bf3f9 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -21,6 +21,7 @@ public class Engine { private long window; private Scene scene; + private Scene scene2; private boolean running; @@ -65,6 +66,7 @@ public class Engine { System.out.println("OpenGL: " + glGetString(GL_VERSION)); this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.rectangle, Primitive.rectangle_indices, Primitive.rectangle_color, Primitive.rectangle_texture); + this.scene2 = new Scene("shaders/vert.glsl", "shaders/frag.frag", Primitive.triangle, Primitive.triangle_indices); } @@ -76,6 +78,7 @@ public class Engine { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //SWAP avec le precedent scene.render(); + scene2.render(); int error = glGetError(); if (error != GL_NO_ERROR) System.out.println(error); glfwSwapBuffers(window); //Envoie le buffer vers le moniteur diff --git a/src/engine/ObjectGl.java b/src/engine/ObjectGl.java new file mode 100644 index 0000000..fd148b5 --- /dev/null +++ b/src/engine/ObjectGl.java @@ -0,0 +1,50 @@ +package engine; + +import engine.graphics.VertexArray; +import engine.graphics.Shader; +import engine.math.Matrix4f; +import engine.math.Vector3f; + +public class ObjectGl { + + private VertexArray vertexArray; + private Shader shader; + private Matrix4f transform; + + public ObjectGl(float x, float y, float h, float w){ + // TODO Create a rectangle + this.transform = Matrix4f.identity(); + } + + public void resetTransform(){ + this.transform = Matrix4f.identity(); + } + + public void translate(Vector3f vec){ + this.transform = this.transform.multiply(Matrix4f.translate(vec)); + } + + public void scale(Vector3f vec){ + this.transform = this.transform.multiply(Matrix4f.scale(vec)); + } + + public void rotateX(float angle){ + this.transform = this.transform.multiply(Matrix4f.rotateX(angle)); + } + + public void rotateY(float angle){ + this.transform = this.transform.multiply(Matrix4f.rotateY(angle)); + } + + public void rotateZ(float angle){ + this.transform = this.transform.multiply(Matrix4f.rotateZ(angle)); + } + + public void render(){ + this.shader.enable(); + this.vertexArray.render(); + this.shader.disable(); + } + + +} diff --git a/src/engine/Scene.java b/src/engine/Scene.java index fce704e..ec03c4d 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -46,15 +46,22 @@ public class Scene { } public void render(){ - this.transform = this.transform.multiply(Matrix4f.rotateZ(1.0f)); - this.transform = this.transform.multiply(Matrix4f.translate(new Vector3f(-0.001f, 0.0f, 0.0f))); - shader.setUniformMat4f("transform", this.transform); + if (this.transform != null){ + this.transform = this.transform.multiply(Matrix4f.rotateZ(1.0f)); + this.transform = this.transform.multiply(Matrix4f.translate(new Vector3f(-0.001f, 0.0f, 0.0f))); + this.transform = this.transform.multiply(Matrix4f.rotateX(1.0f)); + shader.setUniformMat4f("transform", this.transform); + } this.shader.enable(); - this.texture_map.bind(); - this.texture_map2.bind(); + if (this.texture_map != null){ + this.texture_map.bind(); + this.texture_map2.bind(); + } this.vertexArray.render(); - this.texture_map2.unbind(); - this.texture_map.unbind(); + if (this.texture_map != null) { + this.texture_map2.unbind(); + this.texture_map.unbind(); + } this.shader.disable(); } } diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java index 8ad6cb9..6291eb9 100644 --- a/src/engine/math/Matrix4f.java +++ b/src/engine/math/Matrix4f.java @@ -48,6 +48,15 @@ public class Matrix4f { return result; } + public static Matrix4f scale(Vector3f vector){ + Matrix4f result= identity(); + result.elements[0 + 0*4] = vector.x; + result.elements[1 + 1*4] = vector.y; + result.elements[2 + 2*4] = vector.z; + + return result; + } + public static Matrix4f rotateX(float angle){ Matrix4f result = identity(); float r = (float) Math.toRadians(angle); From c6181c96feb3e7c5e2f1c69d5e70c819ace1f8d7 Mon Sep 17 00:00:00 2001 From: Antoine Date: Tue, 18 May 2021 21:18:26 +0200 Subject: [PATCH 19/53] ObjectGl, ObjectGlColor, ObjectGlTex added --- shaders/ObjectGl/frag.glsl | 8 +++++ shaders/ObjectGl/vert.glsl | 8 +++++ shaders/ObjectGlColor/frag.glsl | 10 ++++++ shaders/{ => ObjectGlColor}/vert.glsl | 6 ++-- shaders/ObjectGlTex/frag.glsl | 10 ++++++ shaders/ObjectGlTex/vert.glsl | 12 +++++++ shaders/frag.glsl | 10 ------ src/engine/Primitive.java | 9 ++++++ src/engine/Scene.java | 2 +- src/engine/graphics/VertexArray.java | 45 ++------------------------- src/engine/{ => object}/ObjectGl.java | 18 ++++++----- src/engine/object/ObjectGlColor.java | 18 +++++++++++ src/engine/object/ObjectGlTex.java | 18 +++++++++++ 13 files changed, 110 insertions(+), 64 deletions(-) create mode 100644 shaders/ObjectGl/frag.glsl create mode 100644 shaders/ObjectGl/vert.glsl create mode 100644 shaders/ObjectGlColor/frag.glsl rename shaders/{ => ObjectGlColor}/vert.glsl (68%) create mode 100644 shaders/ObjectGlTex/frag.glsl create mode 100644 shaders/ObjectGlTex/vert.glsl delete mode 100644 shaders/frag.glsl rename src/engine/{ => object}/ObjectGl.java (69%) create mode 100644 src/engine/object/ObjectGlColor.java create mode 100644 src/engine/object/ObjectGlTex.java diff --git a/shaders/ObjectGl/frag.glsl b/shaders/ObjectGl/frag.glsl new file mode 100644 index 0000000..9713fc4 --- /dev/null +++ b/shaders/ObjectGl/frag.glsl @@ -0,0 +1,8 @@ +#version 330 core + +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); +} \ No newline at end of file diff --git a/shaders/ObjectGl/vert.glsl b/shaders/ObjectGl/vert.glsl new file mode 100644 index 0000000..2565d38 --- /dev/null +++ b/shaders/ObjectGl/vert.glsl @@ -0,0 +1,8 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; + +void main() +{ + gl_Position = vec4(aPos, 1.0); +} \ No newline at end of file diff --git a/shaders/ObjectGlColor/frag.glsl b/shaders/ObjectGlColor/frag.glsl new file mode 100644 index 0000000..f8d3a94 --- /dev/null +++ b/shaders/ObjectGlColor/frag.glsl @@ -0,0 +1,10 @@ +#version 330 core + +in vec3 Color; + +out vec4 FragColor; + +void main() +{ + FragColor = vec4(Color, 1.0f); +} \ No newline at end of file diff --git a/shaders/vert.glsl b/shaders/ObjectGlColor/vert.glsl similarity index 68% rename from shaders/vert.glsl rename to shaders/ObjectGlColor/vert.glsl index 5a32523..caf6adb 100644 --- a/shaders/vert.glsl +++ b/shaders/ObjectGlColor/vert.glsl @@ -1,12 +1,12 @@ #version 330 core + layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; -out vec4 position; -out vec4 Color; +out vec3 Color; void main() { gl_Position = vec4(aPos, 1.0); - Color = vec4(aColor, 1.0f); + Color = aColor; } \ No newline at end of file diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl new file mode 100644 index 0000000..f8d3a94 --- /dev/null +++ b/shaders/ObjectGlTex/frag.glsl @@ -0,0 +1,10 @@ +#version 330 core + +in vec3 Color; + +out vec4 FragColor; + +void main() +{ + FragColor = vec4(Color, 1.0f); +} \ No newline at end of file diff --git a/shaders/ObjectGlTex/vert.glsl b/shaders/ObjectGlTex/vert.glsl new file mode 100644 index 0000000..caf6adb --- /dev/null +++ b/shaders/ObjectGlTex/vert.glsl @@ -0,0 +1,12 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; + +out vec3 Color; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + Color = aColor; +} \ No newline at end of file diff --git a/shaders/frag.glsl b/shaders/frag.glsl deleted file mode 100644 index 280912a..0000000 --- a/shaders/frag.glsl +++ /dev/null @@ -1,10 +0,0 @@ -#version 330 core -out vec4 FragColor; - -in vec4 position; -in vec4 Color; - -void main() -{ - FragColor = Color; -} \ No newline at end of file diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java index e072155..9f7ec19 100644 --- a/src/engine/Primitive.java +++ b/src/engine/Primitive.java @@ -31,6 +31,15 @@ public class Primitive { }; } + public static float[] createRectangle(float x, float y, float z, float w, float h){ + return new float[] { + x , y , z, + x + h, y , z, + x + h, y + w, z, + x , y + w, z + }; + } + public static float[] triangle = new float[] { 0.0f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, diff --git a/src/engine/Scene.java b/src/engine/Scene.java index ec03c4d..7e697a3 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -25,7 +25,7 @@ public class Scene { this.vertices = vertices; this.indices = indices; this.shader = new Shader(vertPath, fragPath); - this.vertexArray = new VertexArray(this.vertices, this.indices); + this.vertexArray = new VertexArray(this.vertices, this.indices, null, null); } public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices, float[] color, float[] texture){ diff --git a/src/engine/graphics/VertexArray.java b/src/engine/graphics/VertexArray.java index f76c654..c37d437 100644 --- a/src/engine/graphics/VertexArray.java +++ b/src/engine/graphics/VertexArray.java @@ -1,7 +1,6 @@ package engine.graphics; import engine.utils.BufferUtils; -import org.lwjgl.opengl.GL15; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL15.*; @@ -12,46 +11,6 @@ public class VertexArray { private int VAO ,VBO, EBO, CBO, TBO; private int count; - public VertexArray(float[] vertices, byte[] indices){ - count = indices.length; - // VERTEX ARRAY OBJECT - VAO = glGenVertexArrays(); - glBindVertexArray(VAO); - - // VERTEX BUFFER OBJECT - createVertexBufferObject(vertices); - - EBO = glGenBuffers(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - GL15.glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - } - - public VertexArray(float[] vertices, byte[] indices, float[] color) { - count = indices.length; - // VERTEX ARRAY OBJECT - VAO = glGenVertexArrays(); - glBindVertexArray(VAO); - - glEnableVertexAttribArray(0); - - // VERTEX BUFFER OBJECT - createVertexBufferObject(vertices); - // COLOR BUFFER OBJECT - createColorBufferObject(color); - - EBO = glGenBuffers(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - } - public VertexArray(float[] vertices, byte[] indices, float[] color, float[] texture) { count = indices.length; // VERTEX ARRAY OBJECT @@ -63,9 +22,9 @@ public class VertexArray { // VERTEX BUFFER OBJECT createVertexBufferObject(vertices); // COLOR BUFFER OBJECT - createColorBufferObject(color); + if (color != null) createColorBufferObject(color); // TEXTURE BUFFER OBJECT - createTextureBufferObject(texture); + if (texture != null) createTextureBufferObject(texture); EBO = glGenBuffers(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); diff --git a/src/engine/ObjectGl.java b/src/engine/object/ObjectGl.java similarity index 69% rename from src/engine/ObjectGl.java rename to src/engine/object/ObjectGl.java index fd148b5..fefa176 100644 --- a/src/engine/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -1,19 +1,24 @@ -package engine; +package engine.object; +import engine.Primitive; import engine.graphics.VertexArray; import engine.graphics.Shader; import engine.math.Matrix4f; import engine.math.Vector3f; +/** + * + */ public class ObjectGl { - private VertexArray vertexArray; - private Shader shader; - private Matrix4f transform; + protected VertexArray vertexArray; + protected Shader shader; + protected Matrix4f transform; - public ObjectGl(float x, float y, float h, float w){ - // TODO Create a rectangle + public ObjectGl(float x, float y, float z, float h, float w){ + this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, null); this.transform = Matrix4f.identity(); + this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); } public void resetTransform(){ @@ -46,5 +51,4 @@ public class ObjectGl { this.shader.disable(); } - } diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java new file mode 100644 index 0000000..ee4042d --- /dev/null +++ b/src/engine/object/ObjectGlColor.java @@ -0,0 +1,18 @@ +package engine.object; + +import engine.graphics.Shader; + +public class ObjectGlColor extends ObjectGl{ + + public ObjectGlColor(float x, float y, float h, float w, float[] color) { + super(x, y, h, w); + this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); + } + + @Override + public void render() { + this.shader.enable(); + this.vertexArray.render(); + this.shader.disable(); + } +} diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java new file mode 100644 index 0000000..0fea018 --- /dev/null +++ b/src/engine/object/ObjectGlTex.java @@ -0,0 +1,18 @@ +package engine.object; + +import engine.graphics.Shader; + +public class ObjectGlTex extends ObjectGl{ + + public ObjectGlTex(float x, float y, float h, float w, float[] texCoord) { + super(x, y, h, w); + this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); + } + + @Override + public void render() { + this.shader.enable(); + this.vertexArray.render(); + this.shader.disable(); + } +} From e78b6803893c0998163ace8cbae2b20ff32e84f5 Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 02:52:15 +0200 Subject: [PATCH 20/53] new engine --- .../{frag.frag => ObjectGlTexColor/frag.glsl} | 2 +- .../{vert.vert => ObjectGlTexColor/vert.glsl} | 0 src/Main.java | 2 +- src/engine/Engine.java | 81 ++++++++++---- src/engine/Primitive.java | 99 ------------------ src/engine/math/Primitive.java | 28 +++++ src/engine/object/ObjectGl.java | 6 +- src/engine/object/ObjectGlColor.java | 9 +- src/engine/object/ObjectGlTex.java | 20 +++- src/engine/object/ObjectGlTexColor.java | 20 ++++ textures/zangief_sprite.png | Bin 0 -> 27955 bytes 11 files changed, 138 insertions(+), 129 deletions(-) rename shaders/{frag.frag => ObjectGlTexColor/frag.glsl} (56%) rename shaders/{vert.vert => ObjectGlTexColor/vert.glsl} (100%) delete mode 100644 src/engine/Primitive.java create mode 100644 src/engine/math/Primitive.java create mode 100644 src/engine/object/ObjectGlTexColor.java create mode 100644 textures/zangief_sprite.png diff --git a/shaders/frag.frag b/shaders/ObjectGlTexColor/frag.glsl similarity index 56% rename from shaders/frag.frag rename to shaders/ObjectGlTexColor/frag.glsl index 9642779..17a5870 100644 --- a/shaders/frag.frag +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -10,5 +10,5 @@ uniform sampler2D texture2; void main() { - FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord.x, -TexCoord.y)), position.x * position.y); + FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord.x, -TexCoord.y)), position.x * position.y); } \ No newline at end of file diff --git a/shaders/vert.vert b/shaders/ObjectGlTexColor/vert.glsl similarity index 100% rename from shaders/vert.vert rename to shaders/ObjectGlTexColor/vert.glsl diff --git a/src/Main.java b/src/Main.java index d4c19c8..c769c8c 100644 --- a/src/Main.java +++ b/src/Main.java @@ -7,8 +7,8 @@ * */ -import launcher.Launcher; import engine.Engine; +import launcher.Launcher; public class Main { diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 05bf3f9..36a0998 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -8,10 +8,15 @@ */ package engine; +import engine.object.ObjectGl; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.system.MemoryUtil.*; @@ -20,28 +25,35 @@ public class Engine { private long window; - private Scene scene; - private Scene scene2; + private List objectsGl; private boolean running; - public Engine(){ + /** + * Create the engine and initial attributes use .init() to start the engine + */ + public Engine() { this.running = false; + this.objectsGl = new ArrayList(); } + /** + * Start the engine + * Create the window + */ private void init() { - if(!glfwInit()){ - // TODO Erreur d'initialisation - } + glfwInit(); + this.running = true; + glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //On utilise la version 3.3 d'openGL glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Compatible MAC glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Le core profile est l'interface 'avancé' d'openGL - int width = 800; - int height = 600; + int width = 1280; + int height = 720; this.window = glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL); assert this.window != NULL; @@ -49,6 +61,7 @@ public class Engine { GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); assert vidmode != null; + // On met la fenêtre au centre de l'écran principale glfwSetWindowPos(this.window, (vidmode.width() - width)/2, (vidmode.height() - height)/2); glfwSetKeyCallback(window, new Input()); @@ -64,38 +77,54 @@ public class Engine { glEnable(GL_DEPTH_TEST); // Z-Buffer glClearColor(0.2f, 0.3f, 0.3f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); - - this.scene = new Scene("shaders/vert.vert", "shaders/frag.frag", Primitive.rectangle, Primitive.rectangle_indices, Primitive.rectangle_color, Primitive.rectangle_texture); - this.scene2 = new Scene("shaders/vert.glsl", "shaders/frag.frag", Primitive.triangle, Primitive.triangle_indices); - } + /** + * + */ private void update(){ glfwPollEvents(); } + /** + * + */ private void render(){ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //SWAP avec le precedent - scene.render(); - scene2.render(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + for (ObjectGl objectGl : objectsGl) { + objectGl.render(); + } + int error = glGetError(); if (error != GL_NO_ERROR) System.out.println(error); glfwSwapBuffers(window); //Envoie le buffer vers le moniteur } + /** + * TODO la boucle de rendu doit être inscrite dans la boucle de gameplay: cette methode doit disparaitre + */ public void run(){ - init(); - while(running){ - update(); - render(); - if(glfwWindowShouldClose(window)) running = false; + while(running){ + update(); + render(); + if(glfwWindowShouldClose(window)) running = false; } } - /* - Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu - pour quelle corresponde à la taille de la fenêtre + public void add_objectGl(ObjectGl obj){ + objectsGl.add(obj); + } + + public void remove_objectGl(ObjectGl obj){ + objectsGl.remove(obj); + } + + /** + * Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu + * pour quelle corresponde à la taille de la fenêtre */ private static final GLFWFramebufferSizeCallback resizeWindow = new GLFWFramebufferSizeCallback(){ @Override @@ -106,6 +135,12 @@ public class Engine { public static void main(String[] args) { Engine engine = new Engine(); + engine.init(); + // Add objects to render + ObjectGl cube = new ObjectGl(-0.2f,-0.2f,0.0f,1.0f,1.0f); + engine.add_objectGl(cube); +// engine.remove_objectGl(cube); + engine.run(); } diff --git a/src/engine/Primitive.java b/src/engine/Primitive.java deleted file mode 100644 index 9f7ec19..0000000 --- a/src/engine/Primitive.java +++ /dev/null @@ -1,99 +0,0 @@ -package engine; - -import engine.math.Vector3f; - -public class Primitive { - - public float[] vertices; - public byte[] indices; - - public Primitive(Vector3f vertex1, Vector3f vertex2, Vector3f vertex3){ - vertices = new float[]{ - vertex1.x, vertex1.y, vertex1.z, - vertex2.x, vertex2.y, vertex2.z, - vertex3.x, vertex3.y, vertex3.z - }; - indices = new byte[]{ - 0, 1, 2 - }; - } - - public Primitive(Vector3f vertex1, Vector3f vertex2, Vector3f vertex3, Vector3f vertex4){ - vertices = new float[]{ - vertex1.x, vertex1.y, vertex1.z, - vertex2.x, vertex2.y, vertex2.z, - vertex3.x, vertex3.y, vertex3.z, - vertex4.x, vertex4.y, vertex4.z - }; - indices = new byte[]{ - 0, 1, 2, - 1, 2, 3 - }; - } - - public static float[] createRectangle(float x, float y, float z, float w, float h){ - return new float[] { - x , y , z, - x + h, y , z, - x + h, y + w, z, - x , y + w, z - }; - } - - public static float[] triangle = new float[] { - 0.0f, 0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - -0.5f, -0.5f, 0.0f, - }; - - public static float[] triangle_couleur = new float[] { - 1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 1.0f - }; - - public static byte[] triangle_indices = new byte[]{ - 0, 1, 2 - }; - - public static float[] two_triangle = new float[] { - 0.0f, 0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - -0.5f, -0.5f, 0.0f, - -0.8f, -0.8f, 0.0f, - 0.8f, -0.8f, 0.0f, - 0.8f, -0.9f, 0.0f, - }; - - public static byte[] two_triangle_indices = new byte[]{ - 0, 1, 2, - 3, 4, 5 - }; - - public static float[] rectangle = new float[] { - 0.5f, 0.5f, 0.0f, // top right - 0.5f, -0.5f, 0.0f, // bottom right - -0.5f, -0.5f, 0.0f, // bottom left - -0.5f, 0.5f, 0.0f // top left - }; - - public static float[] rectangle_color = new float[]{ - 1.0f, 0.0f, 0.0f, // top right - 0.0f, 1.0f, 0.0f, // bottom right - 0.0f, 0.0f, 1.0f, // bottom left - 1.0f, 1.0f, 0.0f, // top left - }; - - public static float[] rectangle_texture = new float[]{ - 2.0f, 2.0f, - 2.0f, 0.0f, - 0.0f, 0.0f, - 0.0f, 2.0f - }; - - public static byte[] rectangle_indices = new byte[] { - 0, 1, 3, - 1, 2, 3 - }; - -} diff --git a/src/engine/math/Primitive.java b/src/engine/math/Primitive.java new file mode 100644 index 0000000..5eaff03 --- /dev/null +++ b/src/engine/math/Primitive.java @@ -0,0 +1,28 @@ +package engine.math; + +import engine.math.Vector3f; + +public class Primitive { + + public static float[] createRectangle(float x, float y, float z, float w, float h){ + return new float[] { + x , y , z, + x + w, y , z, + x + w, y - h, z, + x , y - h, z + }; + } + + public static float[] stdTexWrap = new float[] { + 1.0f, 1.0f, + 1.0f, 0.0f, + 0.0f, 0.0f, + 0.0f, 1.0f + }; + + public static byte[] rectangle_indices = new byte[] { + 0, 1, 3, + 1, 2, 3 + }; + +} diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index fefa176..6563ad7 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -1,6 +1,6 @@ package engine.object; -import engine.Primitive; +import engine.math.Primitive; import engine.graphics.VertexArray; import engine.graphics.Shader; import engine.math.Matrix4f; @@ -15,6 +15,10 @@ public class ObjectGl { protected Shader shader; protected Matrix4f transform; + public ObjectGl(){ + + } + public ObjectGl(float x, float y, float z, float h, float w){ this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, null); this.transform = Matrix4f.identity(); diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java index ee4042d..d3759a3 100644 --- a/src/engine/object/ObjectGlColor.java +++ b/src/engine/object/ObjectGlColor.java @@ -1,11 +1,16 @@ package engine.object; +import engine.math.Primitive; import engine.graphics.Shader; +import engine.graphics.VertexArray; +import engine.math.Matrix4f; public class ObjectGlColor extends ObjectGl{ - public ObjectGlColor(float x, float y, float h, float w, float[] color) { - super(x, y, h, w); + public ObjectGlColor(float x, float y, float z, float h, float w, float[] color) { + super(); + this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, null); + this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 0fea018..d636b10 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -1,12 +1,28 @@ package engine.object; +import engine.math.Primitive; import engine.graphics.Shader; +import engine.graphics.Texture; +import engine.graphics.VertexArray; +import engine.math.Matrix4f; + +import java.util.List; public class ObjectGlTex extends ObjectGl{ - public ObjectGlTex(float x, float y, float h, float w, float[] texCoord) { - super(x, y, h, w); + List textures; + + public ObjectGlTex(){ + + } + + public ObjectGlTex(float x, float y, float z, float h, float w, List texPath, float[] texCoord) { + super(); + this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, texCoord); + this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); + + // TODO tex } @Override diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java new file mode 100644 index 0000000..3c1641c --- /dev/null +++ b/src/engine/object/ObjectGlTexColor.java @@ -0,0 +1,20 @@ +package engine.object; + +import engine.math.Primitive; +import engine.graphics.Shader; +import engine.graphics.VertexArray; +import engine.math.Matrix4f; + +import java.util.List; + +public class ObjectGlTexColor extends ObjectGlTex{ + + public ObjectGlTexColor(float x, float y, float z, float h, float w, List texPath, float[] texCoord, float[] color) { + super(); + this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, texCoord); + this.transform = Matrix4f.identity(); + this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); + + // TODO Create texture + } +} diff --git a/textures/zangief_sprite.png b/textures/zangief_sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..862b9fb71ede5cbd851a260e0f6c89a7043ecfdc GIT binary patch literal 27955 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 literal 0 HcmV?d00001 From 58e94314cce92739b8b121b8aa4233b14354ce67 Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 03:35:23 +0200 Subject: [PATCH 21/53] cleanup Engine.java --- shaders/ObjectGl/vert.glsl | 4 +- src/engine/Engine.java | 65 +++++++++++++++++---------------- src/engine/object/ObjectGl.java | 1 + 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/shaders/ObjectGl/vert.glsl b/shaders/ObjectGl/vert.glsl index 2565d38..f6c1771 100644 --- a/shaders/ObjectGl/vert.glsl +++ b/shaders/ObjectGl/vert.glsl @@ -2,7 +2,9 @@ layout (location = 0) in vec3 aPos; +uniform mat4 transform; + void main() { - gl_Position = vec4(aPos, 1.0); + gl_Position = transform * vec4(aPos, 1.0); } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 36a0998..8500905 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -1,13 +1,6 @@ -/** - * CLASS ENGINE - * - * Classe principale du moteur de jeu - * - * @author François Autin - * - */ package engine; +import engine.math.Vector3f; import engine.object.ObjectGl; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; @@ -15,17 +8,16 @@ import org.lwjgl.opengl.GL; import java.util.ArrayList; import java.util.List; -import java.util.ListIterator; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.system.MemoryUtil.*; +import static org.lwjgl.system.MemoryUtil.NULL; public class Engine { private long window; - private List objectsGl; + private final List objectsGl; private boolean running; @@ -34,14 +26,14 @@ public class Engine { */ public Engine() { this.running = false; - this.objectsGl = new ArrayList(); + this.objectsGl = new ArrayList<>(); } /** * Start the engine * Create the window */ - private void init() { + public void init() { glfwInit(); this.running = true; @@ -82,14 +74,14 @@ public class Engine { /** * */ - private void update(){ + public void update(){ glfwPollEvents(); } /** * */ - private void render(){ + public void render(){ glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -103,17 +95,6 @@ public class Engine { glfwSwapBuffers(window); //Envoie le buffer vers le moniteur } - /** - * TODO la boucle de rendu doit être inscrite dans la boucle de gameplay: cette methode doit disparaitre - */ - public void run(){ - while(running){ - update(); - render(); - if(glfwWindowShouldClose(window)) running = false; - } - } - public void add_objectGl(ObjectGl obj){ objectsGl.add(obj); } @@ -122,6 +103,18 @@ public class Engine { objectsGl.remove(obj); } + public boolean isRunning(){ + return running; + } + + public void setRunning(boolean b){ + running = b; + } + + public boolean shouldClose(){ + return glfwWindowShouldClose(window); + } + /** * Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu * pour quelle corresponde à la taille de la fenêtre @@ -136,12 +129,22 @@ public class Engine { public static void main(String[] args) { Engine engine = new Engine(); engine.init(); - // Add objects to render - ObjectGl cube = new ObjectGl(-0.2f,-0.2f,0.0f,1.0f,1.0f); - engine.add_objectGl(cube); -// engine.remove_objectGl(cube); - engine.run(); + // Add objects to render + ObjectGl cube = new ObjectGl(-0.5f,0.5f,0.0f,1.0f,1.0f); + engine.add_objectGl(cube); + + while(engine.isRunning()){ + + // Game logic should fit here + cube.rotateZ(1.0f); + cube.translate(new Vector3f(0.01f, 0.0f, 0.0f)); + + //essential part v + engine.update(); + engine.render(); + if(engine.shouldClose()) engine.setRunning(false); + } } } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 6563ad7..7729df9 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -51,6 +51,7 @@ public class ObjectGl { public void render(){ this.shader.enable(); + this.shader.setUniformMat4f("transform", this.transform); this.vertexArray.render(); this.shader.disable(); } From 7f491ad5974d6b06b0e72f3f2702c8228435783f Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 05:37:45 +0200 Subject: [PATCH 22/53] added ObjectGlTexColor, tweaked shaders --- shaders/ObjectGlColor/frag.glsl | 4 ++-- shaders/ObjectGlColor/vert.glsl | 8 +++++--- shaders/ObjectGlTex/frag.glsl | 9 ++++++--- shaders/ObjectGlTex/vert.glsl | 10 ++++++---- shaders/ObjectGlTexColor/frag.glsl | 3 +-- shaders/ObjectGlTexColor/vert.glsl | 12 +++++------- src/engine/Engine.java | 3 ++- src/engine/object/ObjectGlColor.java | 8 -------- src/engine/object/ObjectGlTex.java | 20 ++++++++++++++++++-- src/engine/object/ObjectGlTexColor.java | 7 ++++++- 10 files changed, 51 insertions(+), 33 deletions(-) diff --git a/shaders/ObjectGlColor/frag.glsl b/shaders/ObjectGlColor/frag.glsl index f8d3a94..45e8b9c 100644 --- a/shaders/ObjectGlColor/frag.glsl +++ b/shaders/ObjectGlColor/frag.glsl @@ -1,10 +1,10 @@ #version 330 core -in vec3 Color; +in vec3 color; out vec4 FragColor; void main() { - FragColor = vec4(Color, 1.0f); + FragColor = vec4(color, 1.0f); } \ No newline at end of file diff --git a/shaders/ObjectGlColor/vert.glsl b/shaders/ObjectGlColor/vert.glsl index caf6adb..801c25b 100644 --- a/shaders/ObjectGlColor/vert.glsl +++ b/shaders/ObjectGlColor/vert.glsl @@ -3,10 +3,12 @@ layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; -out vec3 Color; +out vec3 color; + +uniform mat4 transform; void main() { - gl_Position = vec4(aPos, 1.0); - Color = aColor; + gl_Position = transform * vec4(aPos, 1.0); + color = aColor; } \ No newline at end of file diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl index f8d3a94..cb15fd4 100644 --- a/shaders/ObjectGlTex/frag.glsl +++ b/shaders/ObjectGlTex/frag.glsl @@ -1,10 +1,13 @@ #version 330 core -in vec3 Color; - out vec4 FragColor; +in vec2 TexCoord; + +uniform sampler2D texture1; +uniform sampler2D texture2; + void main() { - FragColor = vec4(Color, 1.0f); + FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord)), 0.5); } \ No newline at end of file diff --git a/shaders/ObjectGlTex/vert.glsl b/shaders/ObjectGlTex/vert.glsl index caf6adb..3420530 100644 --- a/shaders/ObjectGlTex/vert.glsl +++ b/shaders/ObjectGlTex/vert.glsl @@ -1,12 +1,14 @@ #version 330 core layout (location = 0) in vec3 aPos; -layout (location = 1) in vec3 aColor; +layout (location = 2) in vec3 aTexCoord; -out vec3 Color; +out vec3 texCoord; + +uniform mat4 transform; void main() { - gl_Position = vec4(aPos, 1.0); - Color = aColor; + gl_Position = transform * vec4(aPos, 1.0); + texCoord = aTexCoord; } \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl index 17a5870..920d4ee 100644 --- a/shaders/ObjectGlTexColor/frag.glsl +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -3,12 +3,11 @@ out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; -in vec4 position; uniform sampler2D texture1; uniform sampler2D texture2; void main() { - FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord.x, -TexCoord.y)), position.x * position.y); + FragColor = Color * mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5); } \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/vert.glsl b/shaders/ObjectGlTexColor/vert.glsl index 0b3006e..3d1ef50 100644 --- a/shaders/ObjectGlTexColor/vert.glsl +++ b/shaders/ObjectGlTexColor/vert.glsl @@ -1,18 +1,16 @@ #version 330 core + layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; -layout (location = 2) in vec2 aTexCoord; +layout (location = 2) in vec3 aTexCoord; -out vec3 ourColor; -out vec2 TexCoord; -out vec4 position; +out vec3 texCoord; uniform mat4 transform; void main() { gl_Position = transform * vec4(aPos, 1.0); - ourColor = aColor; - TexCoord = aTexCoord; - position = gl_Position; + Color = aColor; + texCoord = aTexCoord; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 8500905..659e015 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -2,6 +2,7 @@ package engine; import engine.math.Vector3f; import engine.object.ObjectGl; +import engine.object.ObjectGlColor; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; @@ -131,7 +132,7 @@ public class Engine { engine.init(); // Add objects to render - ObjectGl cube = new ObjectGl(-0.5f,0.5f,0.0f,1.0f,1.0f); + ObjectGl cube = new ObjectGlColor(-0.5f,0.5f,0.0f,1.0f,1.0f, new float[] {0.2f, 0.2f, 0.8f}); engine.add_objectGl(cube); while(engine.isRunning()){ diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java index d3759a3..9b2da72 100644 --- a/src/engine/object/ObjectGlColor.java +++ b/src/engine/object/ObjectGlColor.java @@ -6,18 +6,10 @@ import engine.graphics.VertexArray; import engine.math.Matrix4f; public class ObjectGlColor extends ObjectGl{ - public ObjectGlColor(float x, float y, float z, float h, float w, float[] color) { super(); this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, null); this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); } - - @Override - public void render() { - this.shader.enable(); - this.vertexArray.render(); - this.shader.disable(); - } } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index d636b10..3cebffd 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -10,7 +10,7 @@ import java.util.List; public class ObjectGlTex extends ObjectGl{ - List textures; + protected List textures; public ObjectGlTex(){ @@ -22,13 +22,29 @@ public class ObjectGlTex extends ObjectGl{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); - // TODO tex + int count = 0; + for (String path : texPath){ + textures.add(new Texture(path, count)); + count++; + } } @Override public void render() { this.shader.enable(); + + this.shader.setUniformMat4f("transform", this.transform); + + for (Texture t : textures){ + t.bind(); + } + this.vertexArray.render(); + + for (Texture t : textures){ + t.unbind(); + } + this.shader.disable(); } } diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index 3c1641c..e58091c 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -1,5 +1,6 @@ package engine.object; +import engine.graphics.Texture; import engine.math.Primitive; import engine.graphics.Shader; import engine.graphics.VertexArray; @@ -15,6 +16,10 @@ public class ObjectGlTexColor extends ObjectGlTex{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); - // TODO Create texture + int count = 0; + for (String path : texPath){ + textures.add(new Texture(path, count)); + count++; + } } } From 57b6be084d884acd5b255423ed05979d93cc9015 Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 15:00:12 +0200 Subject: [PATCH 23/53] ObjectGlTex & ObjectGlTexColor now working :o) --- shaders/ObjectGlTex/frag.glsl | 5 +- shaders/ObjectGlTex/vert.glsl | 4 +- shaders/ObjectGlTexColor/frag.glsl | 8 +-- shaders/ObjectGlTexColor/vert.glsl | 7 +-- src/engine/Engine.java | 13 +++-- src/engine/Scene.java | 67 ------------------------- src/engine/object/ObjectGlTex.java | 2 + src/engine/object/ObjectGlTexColor.java | 2 + 8 files changed, 26 insertions(+), 82 deletions(-) delete mode 100644 src/engine/Scene.java diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl index cb15fd4..94acf40 100644 --- a/shaders/ObjectGlTex/frag.glsl +++ b/shaders/ObjectGlTex/frag.glsl @@ -2,12 +2,11 @@ out vec4 FragColor; -in vec2 TexCoord; +in vec2 texCoord; uniform sampler2D texture1; -uniform sampler2D texture2; void main() { - FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord)), 0.5); + FragColor = texture(texture1, vec2(texCoord.y, -texCoord.x)); } \ No newline at end of file diff --git a/shaders/ObjectGlTex/vert.glsl b/shaders/ObjectGlTex/vert.glsl index 3420530..138602e 100644 --- a/shaders/ObjectGlTex/vert.glsl +++ b/shaders/ObjectGlTex/vert.glsl @@ -1,9 +1,9 @@ #version 330 core layout (location = 0) in vec3 aPos; -layout (location = 2) in vec3 aTexCoord; +layout (location = 2) in vec2 aTexCoord; -out vec3 texCoord; +out vec2 texCoord; uniform mat4 transform; diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl index 920d4ee..c24a496 100644 --- a/shaders/ObjectGlTexColor/frag.glsl +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -1,13 +1,13 @@ #version 330 core + out vec4 FragColor; -in vec3 ourColor; -in vec2 TexCoord; +in vec4 color; +in vec2 texCoord; uniform sampler2D texture1; -uniform sampler2D texture2; void main() { - FragColor = Color * mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5); + FragColor = texture(texture1, vec2(texCoord.y, -texCoord.x)) * color; } \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/vert.glsl b/shaders/ObjectGlTexColor/vert.glsl index 3d1ef50..fa6695f 100644 --- a/shaders/ObjectGlTexColor/vert.glsl +++ b/shaders/ObjectGlTexColor/vert.glsl @@ -2,15 +2,16 @@ layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; -layout (location = 2) in vec3 aTexCoord; +layout (location = 2) in vec2 aTexCoord; -out vec3 texCoord; +out vec2 texCoord; +out vec4 color; uniform mat4 transform; void main() { gl_Position = transform * vec4(aPos, 1.0); - Color = aColor; + color = vec4(aColor, 1.0f); texCoord = aTexCoord; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 659e015..4a07df7 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -1,8 +1,11 @@ package engine; +import engine.math.Primitive; import engine.math.Vector3f; import engine.object.ObjectGl; import engine.object.ObjectGlColor; +import engine.object.ObjectGlTex; +import engine.object.ObjectGlTexColor; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; @@ -68,6 +71,10 @@ public class Engine { glfwSetFramebufferSizeCallback(window, resizeWindow); glEnable(GL_DEPTH_TEST); // Z-Buffer + + glEnable(GL_BLEND); // Transparence + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); System.out.println("OpenGL: " + glGetString(GL_VERSION)); } @@ -132,14 +139,14 @@ public class Engine { engine.init(); // Add objects to render - ObjectGl cube = new ObjectGlColor(-0.5f,0.5f,0.0f,1.0f,1.0f, new float[] {0.2f, 0.2f, 0.8f}); + List path = new ArrayList<>(); + path.add("textures/awesomeface.png"); + ObjectGlTexColor cube = new ObjectGlTexColor(-0.5f,0.5f,0.0f,1.0f,1.0f, path, Primitive.stdTexWrap, new float[] {1.0f,1.0f,1.0f}); engine.add_objectGl(cube); while(engine.isRunning()){ // Game logic should fit here - cube.rotateZ(1.0f); - cube.translate(new Vector3f(0.01f, 0.0f, 0.0f)); //essential part v engine.update(); diff --git a/src/engine/Scene.java b/src/engine/Scene.java deleted file mode 100644 index 7e697a3..0000000 --- a/src/engine/Scene.java +++ /dev/null @@ -1,67 +0,0 @@ -package engine; - -import engine.graphics.Shader; -import engine.graphics.Texture; -import engine.graphics.VertexArray; -import engine.math.Matrix4f; -import engine.math.Vector3f; - -public class Scene { - - float[] vertices; - float[] color; - float[] texture; - float[] texture2; - Texture texture_map; - Texture texture_map2; - byte[] indices; - - Matrix4f transform; - - VertexArray vertexArray; - Shader shader; - - public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices){ - this.vertices = vertices; - this.indices = indices; - this.shader = new Shader(vertPath, fragPath); - this.vertexArray = new VertexArray(this.vertices, this.indices, null, null); - } - - public Scene(String vertPath, String fragPath, float[] vertices, byte[] indices, float[] color, float[] texture){ - this.vertices = vertices; - this.indices = indices; - this.color = color; - this.shader = new Shader(vertPath, fragPath); - this.texture_map = new Texture("textures/container.jpg", 0); - this.texture = texture; - this.texture_map2 = new Texture("textures/awesomeface.png", 1); - this.texture2 = texture; - this.vertexArray = new VertexArray(this.vertices, this.indices, this.color, this.texture); - shader.setUniform1i("texture1", 0); - shader.setUniform1i("texture2", 1); - this.transform = Matrix4f.translate(new Vector3f(-0.1f, 0.2f, 0.0f)); - this.transform = this.transform.multiply(Matrix4f.rotateZ(90.0f)); - shader.setUniformMat4f("transform", this.transform); - } - - public void render(){ - if (this.transform != null){ - this.transform = this.transform.multiply(Matrix4f.rotateZ(1.0f)); - this.transform = this.transform.multiply(Matrix4f.translate(new Vector3f(-0.001f, 0.0f, 0.0f))); - this.transform = this.transform.multiply(Matrix4f.rotateX(1.0f)); - shader.setUniformMat4f("transform", this.transform); - } - this.shader.enable(); - if (this.texture_map != null){ - this.texture_map.bind(); - this.texture_map2.bind(); - } - this.vertexArray.render(); - if (this.texture_map != null) { - this.texture_map2.unbind(); - this.texture_map.unbind(); - } - this.shader.disable(); - } -} diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 3cebffd..1695410 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -6,6 +6,7 @@ import engine.graphics.Texture; import engine.graphics.VertexArray; import engine.math.Matrix4f; +import java.util.ArrayList; import java.util.List; public class ObjectGlTex extends ObjectGl{ @@ -22,6 +23,7 @@ public class ObjectGlTex extends ObjectGl{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); + this.textures = new ArrayList<>(); int count = 0; for (String path : texPath){ textures.add(new Texture(path, count)); diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index e58091c..686d9cb 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -6,6 +6,7 @@ import engine.graphics.Shader; import engine.graphics.VertexArray; import engine.math.Matrix4f; +import java.util.ArrayList; import java.util.List; public class ObjectGlTexColor extends ObjectGlTex{ @@ -16,6 +17,7 @@ public class ObjectGlTexColor extends ObjectGlTex{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); + this.textures = new ArrayList<>(); int count = 0; for (String path : texPath){ textures.add(new Texture(path, count)); From e9935403b61ad2fb6981dd9cd83b53bd94efe294 Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 17:59:05 +0200 Subject: [PATCH 24/53] Orthographic camera, viewpoint, correct aspect ratio --- shaders/ObjectGl/vert.glsl | 4 +++- shaders/ObjectGlColor/vert.glsl | 4 +++- shaders/ObjectGlTex/vert.glsl | 4 +++- shaders/ObjectGlTexColor/vert.glsl | 4 +++- src/engine/Engine.java | 36 +++++++++++++++++++++--------- src/engine/object/ObjectGl.java | 7 ++++++ src/engine/object/ObjectGlTex.java | 2 ++ 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/shaders/ObjectGl/vert.glsl b/shaders/ObjectGl/vert.glsl index f6c1771..58be87a 100644 --- a/shaders/ObjectGl/vert.glsl +++ b/shaders/ObjectGl/vert.glsl @@ -2,9 +2,11 @@ layout (location = 0) in vec3 aPos; +uniform mat4 projection; +uniform mat4 view; uniform mat4 transform; void main() { - gl_Position = transform * vec4(aPos, 1.0); + gl_Position = projection * view * transform * vec4(aPos, 1.0); } \ No newline at end of file diff --git a/shaders/ObjectGlColor/vert.glsl b/shaders/ObjectGlColor/vert.glsl index 801c25b..2a867f6 100644 --- a/shaders/ObjectGlColor/vert.glsl +++ b/shaders/ObjectGlColor/vert.glsl @@ -5,10 +5,12 @@ layout (location = 1) in vec3 aColor; out vec3 color; +uniform mat4 projection; +uniform mat4 view; uniform mat4 transform; void main() { - gl_Position = transform * vec4(aPos, 1.0); + gl_Position = projection * view * transform * vec4(aPos, 1.0); color = aColor; } \ No newline at end of file diff --git a/shaders/ObjectGlTex/vert.glsl b/shaders/ObjectGlTex/vert.glsl index 138602e..b90059c 100644 --- a/shaders/ObjectGlTex/vert.glsl +++ b/shaders/ObjectGlTex/vert.glsl @@ -5,10 +5,12 @@ layout (location = 2) in vec2 aTexCoord; out vec2 texCoord; +uniform mat4 projection; +uniform mat4 view; uniform mat4 transform; void main() { - gl_Position = transform * vec4(aPos, 1.0); + gl_Position = projection * view * transform * vec4(aPos, 1.0); texCoord = aTexCoord; } \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/vert.glsl b/shaders/ObjectGlTexColor/vert.glsl index fa6695f..a591694 100644 --- a/shaders/ObjectGlTexColor/vert.glsl +++ b/shaders/ObjectGlTexColor/vert.glsl @@ -7,11 +7,13 @@ layout (location = 2) in vec2 aTexCoord; out vec2 texCoord; out vec4 color; +uniform mat4 projection; +uniform mat4 view; uniform mat4 transform; void main() { - gl_Position = transform * vec4(aPos, 1.0); + gl_Position = projection * view * transform * vec4(aPos, 1.0); color = vec4(aColor, 1.0f); texCoord = aTexCoord; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 4a07df7..5b8226b 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -1,11 +1,7 @@ package engine; -import engine.math.Primitive; -import engine.math.Vector3f; -import engine.object.ObjectGl; -import engine.object.ObjectGlColor; -import engine.object.ObjectGlTex; -import engine.object.ObjectGlTexColor; +import engine.math.*; +import engine.object.*; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; @@ -31,6 +27,8 @@ public class Engine { public Engine() { this.running = false; this.objectsGl = new ArrayList<>(); + ObjectGl.projection = Matrix4f.orthographic(-10.0f, 10.0f, -10.0f * 9.0f / 16.0f, 10.0f * 9.0f / 16.0f, 0.1f, 100.0f); + ObjectGl.view = Matrix4f.translate(new Vector3f(0.0f,0.0f,1.0f)); } /** @@ -70,7 +68,7 @@ public class Engine { glfwSetFramebufferSizeCallback(window, resizeWindow); - glEnable(GL_DEPTH_TEST); // Z-Buffer + glEnable(GL_DEPTH_TEST); // Z-Buffer plus z est petit plus l'objet est proche de la camera limite à 0.1f au dela l'objet disparait glEnable(GL_BLEND); // Transparence glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -141,16 +139,34 @@ public class Engine { // Add objects to render List path = new ArrayList<>(); path.add("textures/awesomeface.png"); - ObjectGlTexColor cube = new ObjectGlTexColor(-0.5f,0.5f,0.0f,1.0f,1.0f, path, Primitive.stdTexWrap, new float[] {1.0f,1.0f,1.0f}); - engine.add_objectGl(cube); + + ObjectGl smiley = new ObjectGlTex(-0.5f,0.5f,0.0f,1.0f,1.0f, path, Primitive.stdTexWrap); + engine.add_objectGl(smiley); + + ObjectGl smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,1.0f,1.0f, path, Primitive.stdTexWrap); + engine.add_objectGl(smiley2); + smiley2.translate(new Vector3f(0.5f,0.0f,10.0f)); + + long timer = System.currentTimeMillis(); + int frame = 0; while(engine.isRunning()){ - // Game logic should fit here + smiley.rotateY(1.0f); + smiley2.rotateY(0.8f); //essential part v engine.update(); engine.render(); + + frame++; + + if (System.currentTimeMillis() - timer > 1000) { + timer += 1000; + System.out.println("FPS: " + frame); + frame = 0; + } + if(engine.shouldClose()) engine.setRunning(false); } } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 7729df9..9455a09 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -15,6 +15,9 @@ public class ObjectGl { protected Shader shader; protected Matrix4f transform; + public static Matrix4f projection; + public static Matrix4f view; + public ObjectGl(){ } @@ -51,7 +54,11 @@ public class ObjectGl { public void render(){ this.shader.enable(); + + this.shader.setUniformMat4f("projection", projection); + this.shader.setUniformMat4f("view", view); this.shader.setUniformMat4f("transform", this.transform); + this.vertexArray.render(); this.shader.disable(); } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 1695410..f431c1d 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -35,6 +35,8 @@ public class ObjectGlTex extends ObjectGl{ public void render() { this.shader.enable(); + this.shader.setUniformMat4f("projection", projection); + this.shader.setUniformMat4f("view", view); this.shader.setUniformMat4f("transform", this.transform); for (Texture t : textures){ From cf4f1210692983561e0bb2069cb6602c72b9afd5 Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 18:41:28 +0200 Subject: [PATCH 25/53] Correct projection width --- src/engine/Engine.java | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 5b8226b..2855d5d 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -27,7 +27,8 @@ public class Engine { public Engine() { this.running = false; this.objectsGl = new ArrayList<>(); - ObjectGl.projection = Matrix4f.orthographic(-10.0f, 10.0f, -10.0f * 9.0f / 16.0f, 10.0f * 9.0f / 16.0f, 0.1f, 100.0f); + float width = 1280.0f; + ObjectGl.projection = Matrix4f.orthographic(-width, width, -width * 9.0f / 16.0f, width * 9.0f / 16.0f, 0.1f, 100.0f); ObjectGl.view = Matrix4f.translate(new Vector3f(0.0f,0.0f,1.0f)); } @@ -68,7 +69,7 @@ public class Engine { glfwSetFramebufferSizeCallback(window, resizeWindow); - glEnable(GL_DEPTH_TEST); // Z-Buffer plus z est petit plus l'objet est proche de la camera limite à 0.1f au dela l'objet disparait + glEnable(GL_DEPTH_TEST); // Z-Buffer plus z est grand plus l'objet est proche de la camera limite à 100.0f au dela l'objet disparait glEnable(GL_BLEND); // Transparence glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -103,6 +104,7 @@ public class Engine { public void add_objectGl(ObjectGl obj){ objectsGl.add(obj); + // TODO trié en fonction de la distance z sinon bug d'affichage } public void remove_objectGl(ObjectGl obj){ @@ -140,20 +142,24 @@ public class Engine { List path = new ArrayList<>(); path.add("textures/awesomeface.png"); - ObjectGl smiley = new ObjectGlTex(-0.5f,0.5f,0.0f,1.0f,1.0f, path, Primitive.stdTexWrap); + ObjectGl smiley = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path, Primitive.stdTexWrap); engine.add_objectGl(smiley); + smiley.translate(new Vector3f(-2.5f,0.0f,0.0f)); - ObjectGl smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,1.0f,1.0f, path, Primitive.stdTexWrap); + ObjectGl smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path, Primitive.stdTexWrap); engine.add_objectGl(smiley2); - smiley2.translate(new Vector3f(0.5f,0.0f,10.0f)); + smiley2.translate(new Vector3f(0.0f,0.0f,10.0f)); long timer = System.currentTimeMillis(); int frame = 0; while(engine.isRunning()){ // Game logic should fit here - smiley.rotateY(1.0f); - smiley2.rotateY(0.8f); + double time = glfwGetTime(); + + smiley.translate(new Vector3f( (float) Math.sin(time), (float) Math.cos(time), 0.0f)); + + smiley2.rotateZ(0.8f); //essential part v engine.update(); From 7a79c19c9825bbb9c9bf8c3f6ef5ea22fb98b34f Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 19 May 2021 19:08:09 +0200 Subject: [PATCH 26/53] added framerate control --- src/engine/Engine.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 2855d5d..593aa05 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -151,11 +151,14 @@ public class Engine { smiley2.translate(new Vector3f(0.0f,0.0f,10.0f)); long timer = System.currentTimeMillis(); + long lastFrame = System.currentTimeMillis(); int frame = 0; + boolean nextFrame = false; while(engine.isRunning()){ // Game logic should fit here double time = glfwGetTime(); + lastFrame = System.currentTimeMillis(); smiley.translate(new Vector3f( (float) Math.sin(time), (float) Math.cos(time), 0.0f)); @@ -173,6 +176,11 @@ public class Engine { frame = 0; } + while(!nextFrame){ + nextFrame = System.currentTimeMillis() - lastFrame >= 16.66f; + } + + nextFrame = false; if(engine.shouldClose()) engine.setRunning(false); } } From 8506065de0d8787f532c3ba63d1b54b7c7abf4b4 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 20 May 2021 15:52:20 +0200 Subject: [PATCH 27/53] drawing sprite back to front --- shaders/ObjectGlTex/frag.glsl | 2 +- shaders/ObjectGlTexColor/frag.glsl | 2 +- src/engine/Engine.java | 16 +++++++++------- src/engine/object/ObjectGl.java | 6 ++++++ src/engine/object/ObjectGlColor.java | 1 + src/engine/object/ObjectGlTex.java | 1 + src/engine/object/ObjectGlTexColor.java | 1 + src/engine/object/SortZ.java | 11 +++++++++++ 8 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/engine/object/SortZ.java diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl index 94acf40..37f0ba7 100644 --- a/shaders/ObjectGlTex/frag.glsl +++ b/shaders/ObjectGlTex/frag.glsl @@ -8,5 +8,5 @@ uniform sampler2D texture1; void main() { - FragColor = texture(texture1, vec2(texCoord.y, -texCoord.x)); + FragColor = texture(texture1, vec2(-texCoord.y, -texCoord.x)); } \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl index c24a496..9b044ff 100644 --- a/shaders/ObjectGlTexColor/frag.glsl +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -9,5 +9,5 @@ uniform sampler2D texture1; void main() { - FragColor = texture(texture1, vec2(texCoord.y, -texCoord.x)) * color; + FragColor = texture(texture1, vec2(-texCoord.y, -texCoord.x)) * color; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 593aa05..75ed333 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -7,6 +7,7 @@ import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static org.lwjgl.glfw.GLFW.*; @@ -93,6 +94,8 @@ public class Engine { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + objectsGl.sort(new SortZ()); + // TODO trié en fonction de la distance z sinon bug d'affichage for (ObjectGl objectGl : objectsGl) { objectGl.render(); } @@ -104,7 +107,6 @@ public class Engine { public void add_objectGl(ObjectGl obj){ objectsGl.add(obj); - // TODO trié en fonction de la distance z sinon bug d'affichage } public void remove_objectGl(ObjectGl obj){ @@ -140,18 +142,18 @@ public class Engine { // Add objects to render List path = new ArrayList<>(); - path.add("textures/awesomeface.png"); + path.add("textures/zangief_sprite.png"); - ObjectGl smiley = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path, Primitive.stdTexWrap); + ObjectGl smiley = new ObjectGlTex(-200.5f,200.5f,0.0f,320.0f*6,578.0f*6, path, Primitive.stdTexWrap); engine.add_objectGl(smiley); - smiley.translate(new Vector3f(-2.5f,0.0f,0.0f)); + smiley.translate(new Vector3f(-600.0f,0.0f,9.0f)); ObjectGl smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path, Primitive.stdTexWrap); engine.add_objectGl(smiley2); - smiley2.translate(new Vector3f(0.0f,0.0f,10.0f)); + smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); long timer = System.currentTimeMillis(); - long lastFrame = System.currentTimeMillis(); + long lastFrame; int frame = 0; boolean nextFrame = false; @@ -160,7 +162,7 @@ public class Engine { double time = glfwGetTime(); lastFrame = System.currentTimeMillis(); - smiley.translate(new Vector3f( (float) Math.sin(time), (float) Math.cos(time), 0.0f)); + smiley.translate(new Vector3f( (float) Math.sin(time)*5, (float) Math.cos(time)*5, 0.0f)); smiley2.rotateZ(0.8f); diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 9455a09..4ab9484 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -6,6 +6,8 @@ import engine.graphics.Shader; import engine.math.Matrix4f; import engine.math.Vector3f; +import java.util.Comparator; + /** * */ @@ -18,6 +20,8 @@ public class ObjectGl { public static Matrix4f projection; public static Matrix4f view; + public float zPos; + public ObjectGl(){ } @@ -26,6 +30,7 @@ public class ObjectGl { this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, null); this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); + this.zPos = z; } public void resetTransform(){ @@ -34,6 +39,7 @@ public class ObjectGl { public void translate(Vector3f vec){ this.transform = this.transform.multiply(Matrix4f.translate(vec)); + this.zPos += vec.z; } public void scale(Vector3f vec){ diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java index 9b2da72..952105d 100644 --- a/src/engine/object/ObjectGlColor.java +++ b/src/engine/object/ObjectGlColor.java @@ -11,5 +11,6 @@ public class ObjectGlColor extends ObjectGl{ this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, null); this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); + this.zPos = z; } } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index f431c1d..2c14ab6 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -22,6 +22,7 @@ public class ObjectGlTex extends ObjectGl{ this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, texCoord); this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); + this.zPos = z; this.textures = new ArrayList<>(); int count = 0; diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index 686d9cb..8eeaabc 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -16,6 +16,7 @@ public class ObjectGlTexColor extends ObjectGlTex{ this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, texCoord); this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); + this.zPos = z; this.textures = new ArrayList<>(); int count = 0; diff --git a/src/engine/object/SortZ.java b/src/engine/object/SortZ.java new file mode 100644 index 0000000..cc66e86 --- /dev/null +++ b/src/engine/object/SortZ.java @@ -0,0 +1,11 @@ +package engine.object; + +import java.util.Comparator; + +public class SortZ implements Comparator +{ + public int compare(ObjectGl a, ObjectGl b) + { + return (int) (a.zPos - b.zPos); + } +} \ No newline at end of file From 59d822015a1f00546a9981897177ccd6b7f566ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Thu, 20 May 2021 15:54:54 +0200 Subject: [PATCH 28/53] Keyboard input added In Engine.java --- src/engine/Engine.java | 51 +++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 593aa05..523955e 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -2,6 +2,8 @@ package engine; import engine.math.*; import engine.object.*; + +import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; @@ -15,7 +17,7 @@ import static org.lwjgl.system.MemoryUtil.NULL; public class Engine { - private long window; + private static long window; private final List objectsGl; @@ -49,25 +51,28 @@ public class Engine { int width = 1280; int height = 720; - this.window = glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL); - assert this.window != NULL; + this.setWindow(glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL)); + assert this.getWindow() != NULL; + + boolean present = glfwJoystickPresent(GLFW_JOYSTICK_1); + System.out.println(present); // On récupère les informations du moniteur principal GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); assert vidmode != null; // On met la fenêtre au centre de l'écran principale - glfwSetWindowPos(this.window, (vidmode.width() - width)/2, (vidmode.height() - height)/2); + glfwSetWindowPos(this.getWindow(), (vidmode.width() - width)/2, (vidmode.height() - height)/2); - glfwSetKeyCallback(window, new Input()); - glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE); + glfwSetKeyCallback(getWindow(), new Input()); + glfwSetInputMode(getWindow(), GLFW_STICKY_KEYS, GLFW_TRUE); // Contexte = zone cible des rendus - glfwMakeContextCurrent(this.window); - glfwShowWindow(this.window); + glfwMakeContextCurrent(this.getWindow()); + glfwShowWindow(this.getWindow()); GL.createCapabilities(); - glfwSetFramebufferSizeCallback(window, resizeWindow); + glfwSetFramebufferSizeCallback(getWindow(), resizeWindow); glEnable(GL_DEPTH_TEST); // Z-Buffer plus z est grand plus l'objet est proche de la camera limite à 100.0f au dela l'objet disparait @@ -96,10 +101,13 @@ public class Engine { for (ObjectGl objectGl : objectsGl) { objectGl.render(); } + + + int error = glGetError(); if (error != GL_NO_ERROR) System.out.println(error); - glfwSwapBuffers(window); //Envoie le buffer vers le moniteur + glfwSwapBuffers(getWindow()); //Envoie le buffer vers le moniteur } public void add_objectGl(ObjectGl obj){ @@ -120,7 +128,7 @@ public class Engine { } public boolean shouldClose(){ - return glfwWindowShouldClose(window); + return glfwWindowShouldClose(getWindow()); } /** @@ -136,6 +144,7 @@ public class Engine { public static void main(String[] args) { Engine engine = new Engine(); + int speed = 2 ; //vitesse déplacement Object engine.init(); // Add objects to render @@ -163,6 +172,11 @@ public class Engine { smiley.translate(new Vector3f( (float) Math.sin(time), (float) Math.cos(time), 0.0f)); smiley2.rotateZ(0.8f); + + input(smiley, speed); + input(smiley2, speed); + + //essential part v engine.update(); @@ -184,5 +198,20 @@ public class Engine { if(engine.shouldClose()) engine.setRunning(false); } } + + public static void input(ObjectGl token, int speed) { + if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); + } + + public static long getWindow() { + return window; + } + + public void setWindow(long window) { + this.window = window; + } } From 282a8b028382e1c6a6820fede460dd28468c2c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Thu, 20 May 2021 16:26:13 +0200 Subject: [PATCH 29/53] added isKeyDown Class for keyboard input --- .gitignore | 1 + src/engine/Input.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 2880f64..3972786 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.idea/ /.gitignore /jeu-de-combat.iml +/target/ diff --git a/src/engine/Input.java b/src/engine/Input.java index d93f485..194335c 100644 --- a/src/engine/Input.java +++ b/src/engine/Input.java @@ -19,4 +19,8 @@ public class Input extends GLFWKeyCallback { if (glGetInteger(GL_POLYGON_MODE) == GL_FILL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } + public static boolean isKeyDown(int keyCode) { + return glfwGetKey(Engine.getWindow(), keyCode) == 1; + } + } From 859344b131afb834f2824832f128d8e0700797ad Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 20 May 2021 18:02:24 +0200 Subject: [PATCH 30/53] setTexture added --- src/engine/Engine.java | 57 +++++++++++-------------- src/engine/object/ObjectGlTex.java | 3 ++ src/engine/object/ObjectGlTexColor.java | 8 +--- 3 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 7ea9629..1f730df 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -9,7 +9,6 @@ import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static org.lwjgl.glfw.GLFW.*; @@ -53,7 +52,7 @@ public class Engine { int width = 1280; int height = 720; this.setWindow(glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL)); - assert this.getWindow() != NULL; + assert getWindow() != NULL; boolean present = glfwJoystickPresent(GLFW_JOYSTICK_1); System.out.println(present); @@ -63,14 +62,14 @@ public class Engine { assert vidmode != null; // On met la fenêtre au centre de l'écran principale - glfwSetWindowPos(this.getWindow(), (vidmode.width() - width)/2, (vidmode.height() - height)/2); + glfwSetWindowPos(getWindow(), (vidmode.width() - width)/2, (vidmode.height() - height)/2); glfwSetKeyCallback(getWindow(), new Input()); glfwSetInputMode(getWindow(), GLFW_STICKY_KEYS, GLFW_TRUE); // Contexte = zone cible des rendus - glfwMakeContextCurrent(this.getWindow()); - glfwShowWindow(this.getWindow()); + glfwMakeContextCurrent(getWindow()); + glfwShowWindow(getWindow()); GL.createCapabilities(); glfwSetFramebufferSizeCallback(getWindow(), resizeWindow); @@ -100,13 +99,9 @@ public class Engine { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); objectsGl.sort(new SortZ()); - // TODO trié en fonction de la distance z sinon bug d'affichage for (ObjectGl objectGl : objectsGl) { objectGl.render(); } - - - int error = glGetError(); if (error != GL_NO_ERROR) System.out.println(error); @@ -133,6 +128,14 @@ public class Engine { return glfwWindowShouldClose(getWindow()); } + public static long getWindow() { + return window; + } + + public void setWindow(long window) { + Engine.window = window; + } + /** * Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu * pour quelle corresponde à la taille de la fenêtre @@ -153,11 +156,14 @@ public class Engine { List path = new ArrayList<>(); path.add("textures/zangief_sprite.png"); - ObjectGl smiley = new ObjectGlTex(-200.5f,200.5f,0.0f,320.0f*6,578.0f*6, path, Primitive.stdTexWrap); + List path2 = new ArrayList<>(); + path2.add("textures/awesomeface.png"); + + ObjectGlTex smiley = new ObjectGlTex(-200.5f,200.5f,0.0f,320.0f*6,578.0f*6, path, Primitive.stdTexWrap); engine.add_objectGl(smiley); smiley.translate(new Vector3f(-600.0f,0.0f,9.0f)); - ObjectGl smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path, Primitive.stdTexWrap); + ObjectGlTex smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path2, Primitive.stdTexWrap); engine.add_objectGl(smiley2); smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); @@ -167,18 +173,11 @@ public class Engine { boolean nextFrame = false; while(engine.isRunning()){ - // Game logic should fit here - double time = glfwGetTime(); lastFrame = System.currentTimeMillis(); - - smiley.translate(new Vector3f( (float) Math.sin(time)*5, (float) Math.cos(time)*5, 0.0f)); - - smiley2.rotateZ(0.8f); + // Game logic should fit here - input(smiley, speed); - input(smiley2, speed); - - + input(smiley, speed); +// input(smiley2, speed); //essential part v engine.update(); @@ -202,18 +201,10 @@ public class Engine { } public static void input(ObjectGl token, int speed) { - if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); - if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); - if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); - if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); + if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); } - public static long getWindow() { - return window; - } - - public void setWindow(long window) { - this.window = window; - } - } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 2c14ab6..a61b242 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -23,7 +23,10 @@ public class ObjectGlTex extends ObjectGl{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); this.zPos = z; + this.setTexture(texPath); + } + public void setTexture(List texPath){ this.textures = new ArrayList<>(); int count = 0; for (String path : texPath){ diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index 8eeaabc..a221772 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -17,12 +17,6 @@ public class ObjectGlTexColor extends ObjectGlTex{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); this.zPos = z; - - this.textures = new ArrayList<>(); - int count = 0; - for (String path : texPath){ - textures.add(new Texture(path, count)); - count++; - } + this.setTexture(texPath); } } From 4fc87e7b08352be256e81df786fcd78227b50844 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 20 May 2021 19:02:28 +0200 Subject: [PATCH 31/53] swapTexWrap added --- shaders/ObjectGlTex/frag.glsl | 2 +- src/engine/Engine.java | 3 +++ src/engine/graphics/Texture.java | 8 ++++++++ src/engine/graphics/VertexArray.java | 10 ++++++++++ src/engine/math/Primitive.java | 22 ++++++++++++++++------ src/engine/object/ObjectGlTex.java | 18 ++++++++++++++++++ 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl index 37f0ba7..b3d6cfd 100644 --- a/shaders/ObjectGlTex/frag.glsl +++ b/shaders/ObjectGlTex/frag.glsl @@ -8,5 +8,5 @@ uniform sampler2D texture1; void main() { - FragColor = texture(texture1, vec2(-texCoord.y, -texCoord.x)); + FragColor = texture(texture1, texCoord); } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 1f730df..2923794 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -166,6 +166,7 @@ public class Engine { ObjectGlTex smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path2, Primitive.stdTexWrap); engine.add_objectGl(smiley2); smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); + float[] texWrap = Primitive.upperHalfTexWrap; long timer = System.currentTimeMillis(); long lastFrame; @@ -173,11 +174,13 @@ public class Engine { boolean nextFrame = false; while(engine.isRunning()){ + double time = glfwGetTime(); lastFrame = System.currentTimeMillis(); // Game logic should fit here input(smiley, speed); // input(smiley2, speed); + smiley.setTextureWrap(0,0,200,200); //essential part v engine.update(); diff --git a/src/engine/graphics/Texture.java b/src/engine/graphics/Texture.java index 78a23a4..4f06db9 100644 --- a/src/engine/graphics/Texture.java +++ b/src/engine/graphics/Texture.java @@ -64,4 +64,12 @@ public class Texture { glBindTexture(GL_TEXTURE_2D, 0); } + public int getWidth(){ + return width; + } + + public int getHeight(){ + return height; + } + } diff --git a/src/engine/graphics/VertexArray.java b/src/engine/graphics/VertexArray.java index c37d437..c96feec 100644 --- a/src/engine/graphics/VertexArray.java +++ b/src/engine/graphics/VertexArray.java @@ -59,6 +59,16 @@ public class VertexArray { glEnableVertexAttribArray(2); } + public void swapVertexBufferObject(float[] vertices){ + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + } + + public void swapTextureBufferObject(float [] texture){ + glBindBuffer(GL_ARRAY_BUFFER, TBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(texture), GL_STATIC_DRAW); + } + public void bind(){ glBindVertexArray(this.VAO); } diff --git a/src/engine/math/Primitive.java b/src/engine/math/Primitive.java index 5eaff03..964ccf1 100644 --- a/src/engine/math/Primitive.java +++ b/src/engine/math/Primitive.java @@ -6,20 +6,30 @@ public class Primitive { public static float[] createRectangle(float x, float y, float z, float w, float h){ return new float[] { - x , y , z, - x + w, y , z, - x + w, y - h, z, - x , y - h, z + x , y , z, // Haut gauche + x + w, y , z, // Haut droit + x + w, y - h, z, // Bas droit + x , y - h, z // Bas gauche }; } + /** + * Chaque point correspond à un vertex de la primite le reste est interpolé + */ public static float[] stdTexWrap = new float[] { - 1.0f, 1.0f, - 1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f }; + public static float[] upperHalfTexWrap = new float[] { + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 0.5f, + 0.0f, 0.5f + }; + public static byte[] rectangle_indices = new byte[] { 0, 1, 3, 1, 2, 3 diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index a61b242..179fa73 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -5,6 +5,7 @@ import engine.graphics.Shader; import engine.graphics.Texture; import engine.graphics.VertexArray; import engine.math.Matrix4f; +import engine.math.Vector3f; import java.util.ArrayList; import java.util.List; @@ -35,6 +36,23 @@ public class ObjectGlTex extends ObjectGl{ } } + public void setTextureWrap(float x, float y, float w, float h){ + int texWidth = this.textures.get(0).getWidth(); + int texHeight = this.textures.get(0).getHeight(); + float[] result = { + x / texWidth, y / texHeight, + x + w / texWidth, y / texHeight, + x + w / texWidth, y + h / texHeight, + x / texWidth, y + h / texHeight, + }; + // TODO scaling object + this.setTextureWrap(result); + } + + public void setTextureWrap(float[] texture){ + this.vertexArray.swapTextureBufferObject(texture); + } + @Override public void render() { this.shader.enable(); From 29685f8e40a8f9bdad8670df9a66dcf440a23a43 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 20 May 2021 20:04:16 +0200 Subject: [PATCH 32/53] swapTexWrap from pixels coordinates of the source image --- shaders/ObjectGlTexColor/frag.glsl | 2 +- src/engine/Engine.java | 16 ++++++++++------ src/engine/object/ObjectGl.java | 4 ++++ src/engine/object/ObjectGlColor.java | 2 ++ src/engine/object/ObjectGlTex.java | 14 ++++++++++---- src/engine/object/ObjectGlTexColor.java | 2 ++ 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl index 9b044ff..554ab5f 100644 --- a/shaders/ObjectGlTexColor/frag.glsl +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -9,5 +9,5 @@ uniform sampler2D texture1; void main() { - FragColor = texture(texture1, vec2(-texCoord.y, -texCoord.x)) * color; + FragColor = texture(texture1, texCoord) * color; } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 2923794..81d10de 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -159,9 +159,10 @@ public class Engine { List path2 = new ArrayList<>(); path2.add("textures/awesomeface.png"); - ObjectGlTex smiley = new ObjectGlTex(-200.5f,200.5f,0.0f,320.0f*6,578.0f*6, path, Primitive.stdTexWrap); - engine.add_objectGl(smiley); - smiley.translate(new Vector3f(-600.0f,0.0f,9.0f)); + ObjectGlTex zangief = new ObjectGlTex(0f,0f,0f,400f,900f, path, Primitive.stdTexWrap); + engine.add_objectGl(zangief); + zangief.translate(new Vector3f(-600.0f,-100.0f,10.0f)); + zangief.setTextureWrap(52,2,70,83); ObjectGlTex smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path2, Primitive.stdTexWrap); engine.add_objectGl(smiley2); @@ -178,11 +179,14 @@ public class Engine { lastFrame = System.currentTimeMillis(); // Game logic should fit here - input(smiley, speed); + input(zangief, speed); // input(smiley2, speed); - smiley.setTextureWrap(0,0,200,200); - //essential part v + /* + ******************** + * essential part v * + ******************** + */ engine.update(); engine.render(); diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 4ab9484..513c13d 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -21,6 +21,8 @@ public class ObjectGl { public static Matrix4f view; public float zPos; + public float width; + public float height; public ObjectGl(){ @@ -31,6 +33,8 @@ public class ObjectGl { this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); this.zPos = z; + this.width = w; + this.height = h; } public void resetTransform(){ diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java index 952105d..ef93be9 100644 --- a/src/engine/object/ObjectGlColor.java +++ b/src/engine/object/ObjectGlColor.java @@ -12,5 +12,7 @@ public class ObjectGlColor extends ObjectGl{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); this.zPos = z; + this.width = w; + this.height = h; } } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 179fa73..5423658 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -24,6 +24,8 @@ public class ObjectGlTex extends ObjectGl{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); this.zPos = z; + this.width = w; + this.height = h; this.setTexture(texPath); } @@ -39,11 +41,15 @@ public class ObjectGlTex extends ObjectGl{ public void setTextureWrap(float x, float y, float w, float h){ int texWidth = this.textures.get(0).getWidth(); int texHeight = this.textures.get(0).getHeight(); + x /= texWidth; + w /= texWidth; + y /= texHeight; + h /= texHeight; float[] result = { - x / texWidth, y / texHeight, - x + w / texWidth, y / texHeight, - x + w / texWidth, y + h / texHeight, - x / texWidth, y + h / texHeight, + x , y , + x + w , y , + x + w , y + h , + x , y + h , }; // TODO scaling object this.setTextureWrap(result); diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index a221772..ec5090e 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -17,6 +17,8 @@ public class ObjectGlTexColor extends ObjectGlTex{ this.transform = Matrix4f.identity(); this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); this.zPos = z; + this.width = w; + this.height = h; this.setTexture(texPath); } } From bdc21441451ac6c941c321e7e1a6a411c7595f97 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 20 May 2021 20:55:51 +0200 Subject: [PATCH 33/53] test with Zangief --- src/engine/Engine.java | 22 ++++++++++++++++++++-- src/engine/object/ObjectGl.java | 2 -- src/engine/object/ObjectGlTex.java | 5 +---- src/engine/object/ObjectGlTexColor.java | 4 +--- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 81d10de..1e5c5c6 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -178,7 +178,8 @@ public class Engine { double time = glfwGetTime(); lastFrame = System.currentTimeMillis(); // Game logic should fit here - + + zangief.setTextureWrap(52,2,70,83); input(zangief, speed); // input(smiley2, speed); @@ -207,11 +208,28 @@ public class Engine { } } - public static void input(ObjectGl token, int speed) { + public static void input(ObjectGl token, int speed) { if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); } + public static void input(ObjectGlTex token, int speed) { + if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { + token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); + } + if (Input.isKeyDown(GLFW.GLFW_KEY_A)) { + token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); + token.setTextureWrap(121,0,57,80); + } + if (Input.isKeyDown(GLFW.GLFW_KEY_S)) { + token.setTextureWrap(161,260,56,59); + } + if (Input.isKeyDown(GLFW.GLFW_KEY_D)) { + token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); + token.setTextureWrap(178,0,62,82); + } + } + } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 513c13d..3a101b1 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -6,8 +6,6 @@ import engine.graphics.Shader; import engine.math.Matrix4f; import engine.math.Vector3f; -import java.util.Comparator; - /** * */ diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 5423658..1f04e99 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -1,11 +1,8 @@ package engine.object; import engine.math.Primitive; -import engine.graphics.Shader; -import engine.graphics.Texture; -import engine.graphics.VertexArray; +import engine.graphics.*; import engine.math.Matrix4f; -import engine.math.Vector3f; import java.util.ArrayList; import java.util.List; diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index ec5090e..7d8aa7d 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -1,12 +1,10 @@ package engine.object; -import engine.graphics.Texture; -import engine.math.Primitive; import engine.graphics.Shader; import engine.graphics.VertexArray; +import engine.math.Primitive; import engine.math.Matrix4f; -import java.util.ArrayList; import java.util.List; public class ObjectGlTexColor extends ObjectGlTex{ From e3623072ff6dfd50ab25f7e3c4d32a63b4e25a64 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 20 May 2021 23:28:45 +0200 Subject: [PATCH 34/53] dynamic scaling vertexArray with textureWrap (no more stretching) --- src/engine/Engine.java | 21 ++++++++++----------- src/engine/math/Vector3f.java | 5 +++++ src/engine/object/ObjectGl.java | 12 +++++++++++- src/engine/object/ObjectGlColor.java | 5 ++++- src/engine/object/ObjectGlTex.java | 7 +++++-- src/engine/object/ObjectGlTexColor.java | 5 ++++- 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 1e5c5c6..f571ff0 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -159,12 +159,11 @@ public class Engine { List path2 = new ArrayList<>(); path2.add("textures/awesomeface.png"); - ObjectGlTex zangief = new ObjectGlTex(0f,0f,0f,400f,900f, path, Primitive.stdTexWrap); + ObjectGlTex zangief = new ObjectGlTex(0f,0f,0f,60f,80f,5f, path, Primitive.stdTexWrap); engine.add_objectGl(zangief); - zangief.translate(new Vector3f(-600.0f,-100.0f,10.0f)); - zangief.setTextureWrap(52,2,70,83); +// zangief.translate(new Vector3f(-600.0f,100,10.0f)); - ObjectGlTex smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, path2, Primitive.stdTexWrap); + ObjectGlTex smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, 1f, path2, Primitive.stdTexWrap); engine.add_objectGl(smiley2); smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); float[] texWrap = Primitive.upperHalfTexWrap; @@ -208,16 +207,16 @@ public class Engine { } } - public static void input(ObjectGl token, int speed) { - if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); - if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); - if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); - if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); - } +// public static void input(ObjectGl token, int speed) { +// if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); +// if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); +// if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); +// if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); +// } public static void input(ObjectGlTex token, int speed) { if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { - token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); + token.scale(new Vector3f (1.01f, 1.01f, 0.0f)); } if (Input.isKeyDown(GLFW.GLFW_KEY_A)) { token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); diff --git a/src/engine/math/Vector3f.java b/src/engine/math/Vector3f.java index af4015b..4f7c98b 100644 --- a/src/engine/math/Vector3f.java +++ b/src/engine/math/Vector3f.java @@ -16,4 +16,9 @@ public class Vector3f { this.z = z; } + public void divXY(float div){ + this.x /= div; + this.y /= div; + } + } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 3a101b1..ea9c8c6 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -21,14 +21,17 @@ public class ObjectGl { public float zPos; public float width; public float height; + public float scalingFactor; public ObjectGl(){ } - public ObjectGl(float x, float y, float z, float h, float w){ + public ObjectGl(float x, float y, float z, float h, float w, float size){ this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, null); this.transform = Matrix4f.identity(); + this.scale(new Vector3f(size, size,1f)); + this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); this.zPos = z; this.width = w; @@ -40,11 +43,18 @@ public class ObjectGl { } public void translate(Vector3f vec){ + vec.divXY(this.scalingFactor); this.transform = this.transform.multiply(Matrix4f.translate(vec)); this.zPos += vec.z; } + /** + * Comme on foncitonne avec des sprite on part du principe que x et y sont scale de manière uniforme + * ou tout du moins que this.scalingFactor corresponds au scaling de x + * @param vec + */ public void scale(Vector3f vec){ + this.scalingFactor *= vec.x; this.transform = this.transform.multiply(Matrix4f.scale(vec)); } diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java index ef93be9..809689d 100644 --- a/src/engine/object/ObjectGlColor.java +++ b/src/engine/object/ObjectGlColor.java @@ -4,12 +4,15 @@ import engine.math.Primitive; import engine.graphics.Shader; import engine.graphics.VertexArray; import engine.math.Matrix4f; +import engine.math.Vector3f; public class ObjectGlColor extends ObjectGl{ - public ObjectGlColor(float x, float y, float z, float h, float w, float[] color) { + public ObjectGlColor(float x, float y, float z, float h, float w, float size, float[] color) { super(); this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, null); this.transform = Matrix4f.identity(); + this.scale(new Vector3f(size, size,1)); + this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); this.zPos = z; this.width = w; diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index 1f04e99..a5b0e72 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -3,6 +3,7 @@ package engine.object; import engine.math.Primitive; import engine.graphics.*; import engine.math.Matrix4f; +import engine.math.Vector3f; import java.util.ArrayList; import java.util.List; @@ -15,10 +16,12 @@ public class ObjectGlTex extends ObjectGl{ } - public ObjectGlTex(float x, float y, float z, float h, float w, List texPath, float[] texCoord) { + public ObjectGlTex(float x, float y, float z, float h, float w, float size, List texPath, float[] texCoord) { super(); this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, texCoord); this.transform = Matrix4f.identity(); + this.scale(new Vector3f(size, size,1)); + this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); this.zPos = z; this.width = w; @@ -36,6 +39,7 @@ public class ObjectGlTex extends ObjectGl{ } public void setTextureWrap(float x, float y, float w, float h){ + this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(0.0f,0.0f,this.zPos,w,h)); int texWidth = this.textures.get(0).getWidth(); int texHeight = this.textures.get(0).getHeight(); x /= texWidth; @@ -48,7 +52,6 @@ public class ObjectGlTex extends ObjectGl{ x + w , y + h , x , y + h , }; - // TODO scaling object this.setTextureWrap(result); } diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index 7d8aa7d..88f8dc6 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -4,15 +4,18 @@ import engine.graphics.Shader; import engine.graphics.VertexArray; import engine.math.Primitive; import engine.math.Matrix4f; +import engine.math.Vector3f; import java.util.List; public class ObjectGlTexColor extends ObjectGlTex{ - public ObjectGlTexColor(float x, float y, float z, float h, float w, List texPath, float[] texCoord, float[] color) { + public ObjectGlTexColor(float x, float y, float z, float h, float w, float size, List texPath, float[] texCoord, float[] color) { super(); this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, texCoord); this.transform = Matrix4f.identity(); + this.scale(new Vector3f(size, size,1)); + this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); this.zPos = z; this.width = w; From 3cf4372912cfc738a19cc457d10c2e08e590d2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Fri, 21 May 2021 15:08:02 +0200 Subject: [PATCH 35/53] test gamepad Inputs --- .gitignore | 1 + src/engine/Engine.java | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/.gitignore b/.gitignore index 3972786..763ac98 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.gitignore /jeu-de-combat.iml /target/ +/bin/ diff --git a/src/engine/Engine.java b/src/engine/Engine.java index f571ff0..52f85cf 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -8,6 +8,8 @@ import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; @@ -179,6 +181,7 @@ public class Engine { // Game logic should fit here zangief.setTextureWrap(52,2,70,83); + gamepadInput(zangief, speed); input(zangief, speed); // input(smiley2, speed); @@ -213,6 +216,49 @@ public class Engine { // if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); // if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); // } + private static void gamepadInput(ObjectGl token, int speed) { + ByteBuffer gamepadButton = glfwGetJoystickButtons(GLFW_JOYSTICK_1); + FloatBuffer gamepadAxes = glfwGetJoystickAxes(GLFW_JOYSTICK_1); + String name = GLFW.glfwGetJoystickName(GLFW_JOYSTICK_1); + + //System.out.println("GamePad Name :" + name); + for (int i =0 ; i < gamepadAxes.capacity(); i++) { + System.out.println(i + " :" + gamepadAxes.get(i)); + } + System.out.println(); + System.out.println(); + + if (gamepadButton.get(0) ==1 ) { // appuie sur croix(PlayStation) A (Xbox) + token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); + } + + /* Buttons + 0 : Croix / A + 1: rond /B + 2: carré / X + 3: triangle / Y + 4: L1 / LB + 5: R1 / RB + 6:select + 7:start + 8:L3 + 9:R3 + 10: haut + 11: droite + 12: bas + 13: gauche + */ + + /* Axes + 0 : left X axe ( right : 1 left -1) + 1: left Y axe ( down : 1 , Up -1) + 2: right X axe ( right : 1 left -1) + 3: right Y axe ( down : 1 , Up -1) + 4:L2 / LT : 1 active, -1 unactive + 5: R2 /RT : 1 active, -1 unactive + */ + + } public static void input(ObjectGlTex token, int speed) { if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { From 7bcbad579272583c0e5243a34efe3c35c508560a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Fri, 21 May 2021 16:43:45 +0200 Subject: [PATCH 36/53] added Rightjoystick controls for zangief --- src/engine/Engine.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 52f85cf..417a674 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -225,13 +225,21 @@ public class Engine { for (int i =0 ; i < gamepadAxes.capacity(); i++) { System.out.println(i + " :" + gamepadAxes.get(i)); } - System.out.println(); - System.out.println(); + System.out.flush(); + if (gamepadButton.get(0) ==1 ) { // appuie sur croix(PlayStation) A (Xbox) token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); } + if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche + token.translate(new Vector3f (5*speed * gamepadAxes.get(2), 0.0f, 0.0f)); + } + + if ( (gamepadAxes.get(3) < -0.1 || gamepadAxes.get(3) > 0.1) ) { // de haut en bas + token.translate(new Vector3f (0.0f, -5* speed * gamepadAxes.get(3), 0.0f)); + } + /* Buttons 0 : Croix / A 1: rond /B From 3e12e98010a7eb5cbe8d739560f67fb3a8ee7aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Fri, 21 May 2021 17:26:35 +0200 Subject: [PATCH 37/53] left joystick control ( textures update) --- src/engine/Engine.java | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 417a674..839c9c5 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -216,28 +216,32 @@ public class Engine { // if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); // if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); // } - private static void gamepadInput(ObjectGl token, int speed) { + private static void gamepadInput(ObjectGlTex token, int speed) { ByteBuffer gamepadButton = glfwGetJoystickButtons(GLFW_JOYSTICK_1); FloatBuffer gamepadAxes = glfwGetJoystickAxes(GLFW_JOYSTICK_1); String name = GLFW.glfwGetJoystickName(GLFW_JOYSTICK_1); - //System.out.println("GamePad Name :" + name); - for (int i =0 ; i < gamepadAxes.capacity(); i++) { - System.out.println(i + " :" + gamepadAxes.get(i)); - } - System.out.flush(); +// for (int i =0 ; i < gamepadAxes.capacity(); i++) { +// System.out.println(i + " :" + gamepadAxes.get(i)); +// } if (gamepadButton.get(0) ==1 ) { // appuie sur croix(PlayStation) A (Xbox) - token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); + token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); } - if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche + if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche //joystick gauche token.translate(new Vector3f (5*speed * gamepadAxes.get(2), 0.0f, 0.0f)); + if ( gamepadAxes.get(2) < -0.1 ){ + token.setTextureWrap(121,0,57,80); + }else if (gamepadAxes.get(2) > 0.1) { + token.setTextureWrap(178,0,62,82); + } } - if ( (gamepadAxes.get(3) < -0.1 || gamepadAxes.get(3) > 0.1) ) { // de haut en bas + if ( (gamepadAxes.get(3) < -0.1 || gamepadAxes.get(3) > 0.1) ) { // de haut en bas //joystick gauche token.translate(new Vector3f (0.0f, -5* speed * gamepadAxes.get(3), 0.0f)); + } /* Buttons From ecbe08fdc3af4d2492acebc3ef2d2d4cfdedc3fe Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 21 May 2021 18:20:49 +0200 Subject: [PATCH 38/53] Zangief idle wrap correction --- src/engine/Engine.java | 4 ++-- src/engine/object/ObjectGl.java | 9 ++++----- src/engine/object/ObjectGlColor.java | 2 -- src/engine/object/ObjectGlTex.java | 3 +-- src/engine/object/ObjectGlTexColor.java | 2 -- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 839c9c5..5ec76a5 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -161,7 +161,7 @@ public class Engine { List path2 = new ArrayList<>(); path2.add("textures/awesomeface.png"); - ObjectGlTex zangief = new ObjectGlTex(0f,0f,0f,60f,80f,5f, path, Primitive.stdTexWrap); + ObjectGlTex zangief = new ObjectGlTex(0f,0f,0f,60f,80f,10f, path, Primitive.stdTexWrap); engine.add_objectGl(zangief); // zangief.translate(new Vector3f(-600.0f,100,10.0f)); @@ -180,7 +180,7 @@ public class Engine { lastFrame = System.currentTimeMillis(); // Game logic should fit here - zangief.setTextureWrap(52,2,70,83); + zangief.setTextureWrap(58,0,62,84); gamepadInput(zangief, speed); input(zangief, speed); // input(smiley2, speed); diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index ea9c8c6..f302cec 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -19,8 +19,6 @@ public class ObjectGl { public static Matrix4f view; public float zPos; - public float width; - public float height; public float scalingFactor; public ObjectGl(){ @@ -29,13 +27,14 @@ public class ObjectGl { public ObjectGl(float x, float y, float z, float h, float w, float size){ this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, null); + this.transform = Matrix4f.identity(); this.scale(new Vector3f(size, size,1f)); - this.scalingFactor = size; + this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); + + this.scalingFactor = size; this.zPos = z; - this.width = w; - this.height = h; } public void resetTransform(){ diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java index 809689d..c7fc3ee 100644 --- a/src/engine/object/ObjectGlColor.java +++ b/src/engine/object/ObjectGlColor.java @@ -15,7 +15,5 @@ public class ObjectGlColor extends ObjectGl{ this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); this.zPos = z; - this.width = w; - this.height = h; } } diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java index a5b0e72..a4f9d9a 100644 --- a/src/engine/object/ObjectGlTex.java +++ b/src/engine/object/ObjectGlTex.java @@ -24,8 +24,7 @@ public class ObjectGlTex extends ObjectGl{ this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); this.zPos = z; - this.width = w; - this.height = h; + this.setTexture(texPath); } diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java index 88f8dc6..e1f8444 100644 --- a/src/engine/object/ObjectGlTexColor.java +++ b/src/engine/object/ObjectGlTexColor.java @@ -18,8 +18,6 @@ public class ObjectGlTexColor extends ObjectGlTex{ this.scalingFactor = size; this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); this.zPos = z; - this.width = w; - this.height = h; this.setTexture(texPath); } } From 770d1347228be91effb5fa3b5a56ad421eab5d95 Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 21 May 2021 19:15:48 +0200 Subject: [PATCH 39/53] =?UTF-8?q?J'ai=20vir=C3=A9=20les=20classes=20useles?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/Engine.java | 25 ++++---- src/engine/math/Primitive.java | 10 +-- src/engine/object/ObjectGl.java | 80 +++++++++++++++++++++--- src/engine/object/ObjectGlColor.java | 19 ------ src/engine/object/ObjectGlTex.java | 81 ------------------------- src/engine/object/ObjectGlTexColor.java | 23 ------- 6 files changed, 90 insertions(+), 148 deletions(-) delete mode 100644 src/engine/object/ObjectGlColor.java delete mode 100644 src/engine/object/ObjectGlTex.java delete mode 100644 src/engine/object/ObjectGlTexColor.java diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 5ec76a5..b0774ba 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -155,20 +155,17 @@ public class Engine { engine.init(); // Add objects to render - List path = new ArrayList<>(); - path.add("textures/zangief_sprite.png"); + String path = "textures/zangief_sprite.png"; - List path2 = new ArrayList<>(); - path2.add("textures/awesomeface.png"); + String path2 = "textures/awesomeface.png"; - ObjectGlTex zangief = new ObjectGlTex(0f,0f,0f,60f,80f,10f, path, Primitive.stdTexWrap); + ObjectGl zangief = new ObjectGl(0f,60f,80f,10f, path, null); engine.add_objectGl(zangief); // zangief.translate(new Vector3f(-600.0f,100,10.0f)); - ObjectGlTex smiley2 = new ObjectGlTex(-0.5f,0.5f,0.0f,500.0f,500.0f, 1f, path2, Primitive.stdTexWrap); + ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); engine.add_objectGl(smiley2); smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); - float[] texWrap = Primitive.upperHalfTexWrap; long timer = System.currentTimeMillis(); long lastFrame; @@ -176,7 +173,6 @@ public class Engine { boolean nextFrame = false; while(engine.isRunning()){ - double time = glfwGetTime(); lastFrame = System.currentTimeMillis(); // Game logic should fit here @@ -216,9 +212,13 @@ public class Engine { // if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); // if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); // } - private static void gamepadInput(ObjectGlTex token, int speed) { + private static void gamepadInput(ObjectGl token, int speed) { ByteBuffer gamepadButton = glfwGetJoystickButtons(GLFW_JOYSTICK_1); FloatBuffer gamepadAxes = glfwGetJoystickAxes(GLFW_JOYSTICK_1); + + assert gamepadAxes != null; + assert gamepadButton != null; + String name = GLFW.glfwGetJoystickName(GLFW_JOYSTICK_1); //System.out.println("GamePad Name :" + name); // for (int i =0 ; i < gamepadAxes.capacity(); i++) { @@ -228,9 +228,8 @@ public class Engine { if (gamepadButton.get(0) ==1 ) { // appuie sur croix(PlayStation) A (Xbox) token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); - } - - if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche //joystick gauche + } + if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche //joystick gauche token.translate(new Vector3f (5*speed * gamepadAxes.get(2), 0.0f, 0.0f)); if ( gamepadAxes.get(2) < -0.1 ){ token.setTextureWrap(121,0,57,80); @@ -272,7 +271,7 @@ public class Engine { } - public static void input(ObjectGlTex token, int speed) { + public static void input(ObjectGl token, int speed) { if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { token.scale(new Vector3f (1.01f, 1.01f, 0.0f)); } diff --git a/src/engine/math/Primitive.java b/src/engine/math/Primitive.java index 964ccf1..56f2f95 100644 --- a/src/engine/math/Primitive.java +++ b/src/engine/math/Primitive.java @@ -4,12 +4,12 @@ import engine.math.Vector3f; public class Primitive { - public static float[] createRectangle(float x, float y, float z, float w, float h){ + public static float[] createRectangle(float z, float w, float h){ return new float[] { - x , y , z, // Haut gauche - x + w, y , z, // Haut droit - x + w, y - h, z, // Bas droit - x , y - h, z // Bas gauche + 0 , 0 , z, // Haut gauche + 0 + w, 0 , z, // Haut droit + 0 + w, 0 - h, z, // Bas droit + 0 , 0 - h, z // Bas gauche }; } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index f302cec..f22e1ca 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -1,11 +1,15 @@ package engine.object; +import engine.graphics.Texture; import engine.math.Primitive; import engine.graphics.VertexArray; import engine.graphics.Shader; import engine.math.Matrix4f; import engine.math.Vector3f; +import java.util.ArrayList; +import java.util.List; + /** * */ @@ -21,20 +25,54 @@ public class ObjectGl { public float zPos; public float scalingFactor; + private Texture texture; + public ObjectGl(){ } - public ObjectGl(float x, float y, float z, float h, float w, float size){ - this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, null); + /** + * Create a rectangle shape + * @param z depth of your model the larger it is the more it will be "close" to the camera + * @param h height of the rectangle + * @param w width of the rectangle + * @param size scaling factor of the rectangle, the model could not show up because this value is too small or too large, a good compromise is between 2 and 15 + * @param tex set to null if you don't want a tex on your model + * @param color set to null if you don't want a Color on your model + */ + public ObjectGl(float z, float h, float w, float size, String tex, Vector3f color){ + float[] colorBuffer = null; + // Check des options + if (color != null){ + colorBuffer = new float[] { + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z + }; + } - this.transform = Matrix4f.identity(); - this.scale(new Vector3f(size, size,1f)); + if (tex != null){ + this.texture = new Texture(tex, 0); + } - this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); + this.vertexArray = new VertexArray(Primitive.createRectangle(z, h, w), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); this.scalingFactor = size; this.zPos = z; + this.transform = Matrix4f.identity(); + this.scale(new Vector3f(size, size,1f)); + + // use different shader for each set of option + if (tex == null && color == null){ + this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); + } else if (tex == null){ + this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); // TODO + } else if (color == null){ + this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); + } else { + this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); // TODO + } } public void resetTransform(){ @@ -48,9 +86,9 @@ public class ObjectGl { } /** - * Comme on foncitonne avec des sprite on part du principe que x et y sont scale de manière uniforme + * Comme on fonctionne avec des sprites on part du principe que x et y sont scale de manière uniforme * ou tout du moins que this.scalingFactor corresponds au scaling de x - * @param vec + * @param vec le vecteur de transformation */ public void scale(Vector3f vec){ this.scalingFactor *= vec.x; @@ -69,14 +107,42 @@ public class ObjectGl { this.transform = this.transform.multiply(Matrix4f.rotateZ(angle)); } + public void setTexture(String texPath){ + this.texture = new Texture(texPath, 0); + } + + public void setTextureWrap(float x, float y, float w, float h){ + this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(this.zPos, w, h)); + int texWidth = this.texture.getWidth(); + int texHeight = this.texture.getHeight(); + x /= texWidth; + w /= texWidth; + y /= texHeight; + h /= texHeight; + float[] result = { + x , y , + x + w , y , + x + w , y + h , + x , y + h , + }; + this.setTextureWrap(result); + } + + public void setTextureWrap(float[] texture){ + this.vertexArray.swapTextureBufferObject(texture); + } + public void render(){ this.shader.enable(); + if (this.texture != null) this.texture.bind(); this.shader.setUniformMat4f("projection", projection); this.shader.setUniformMat4f("view", view); this.shader.setUniformMat4f("transform", this.transform); this.vertexArray.render(); + + if (this.texture != null) this.texture.unbind(); this.shader.disable(); } diff --git a/src/engine/object/ObjectGlColor.java b/src/engine/object/ObjectGlColor.java deleted file mode 100644 index c7fc3ee..0000000 --- a/src/engine/object/ObjectGlColor.java +++ /dev/null @@ -1,19 +0,0 @@ -package engine.object; - -import engine.math.Primitive; -import engine.graphics.Shader; -import engine.graphics.VertexArray; -import engine.math.Matrix4f; -import engine.math.Vector3f; - -public class ObjectGlColor extends ObjectGl{ - public ObjectGlColor(float x, float y, float z, float h, float w, float size, float[] color) { - super(); - this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, null); - this.transform = Matrix4f.identity(); - this.scale(new Vector3f(size, size,1)); - this.scalingFactor = size; - this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); - this.zPos = z; - } -} diff --git a/src/engine/object/ObjectGlTex.java b/src/engine/object/ObjectGlTex.java deleted file mode 100644 index a4f9d9a..0000000 --- a/src/engine/object/ObjectGlTex.java +++ /dev/null @@ -1,81 +0,0 @@ -package engine.object; - -import engine.math.Primitive; -import engine.graphics.*; -import engine.math.Matrix4f; -import engine.math.Vector3f; - -import java.util.ArrayList; -import java.util.List; - -public class ObjectGlTex extends ObjectGl{ - - protected List textures; - - public ObjectGlTex(){ - - } - - public ObjectGlTex(float x, float y, float z, float h, float w, float size, List texPath, float[] texCoord) { - super(); - this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, null, texCoord); - this.transform = Matrix4f.identity(); - this.scale(new Vector3f(size, size,1)); - this.scalingFactor = size; - this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); - this.zPos = z; - - this.setTexture(texPath); - } - - public void setTexture(List texPath){ - this.textures = new ArrayList<>(); - int count = 0; - for (String path : texPath){ - textures.add(new Texture(path, count)); - count++; - } - } - - public void setTextureWrap(float x, float y, float w, float h){ - this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(0.0f,0.0f,this.zPos,w,h)); - int texWidth = this.textures.get(0).getWidth(); - int texHeight = this.textures.get(0).getHeight(); - x /= texWidth; - w /= texWidth; - y /= texHeight; - h /= texHeight; - float[] result = { - x , y , - x + w , y , - x + w , y + h , - x , y + h , - }; - this.setTextureWrap(result); - } - - public void setTextureWrap(float[] texture){ - this.vertexArray.swapTextureBufferObject(texture); - } - - @Override - public void render() { - this.shader.enable(); - - this.shader.setUniformMat4f("projection", projection); - this.shader.setUniformMat4f("view", view); - this.shader.setUniformMat4f("transform", this.transform); - - for (Texture t : textures){ - t.bind(); - } - - this.vertexArray.render(); - - for (Texture t : textures){ - t.unbind(); - } - - this.shader.disable(); - } -} diff --git a/src/engine/object/ObjectGlTexColor.java b/src/engine/object/ObjectGlTexColor.java deleted file mode 100644 index e1f8444..0000000 --- a/src/engine/object/ObjectGlTexColor.java +++ /dev/null @@ -1,23 +0,0 @@ -package engine.object; - -import engine.graphics.Shader; -import engine.graphics.VertexArray; -import engine.math.Primitive; -import engine.math.Matrix4f; -import engine.math.Vector3f; - -import java.util.List; - -public class ObjectGlTexColor extends ObjectGlTex{ - - public ObjectGlTexColor(float x, float y, float z, float h, float w, float size, List texPath, float[] texCoord, float[] color) { - super(); - this.vertexArray = new VertexArray(Primitive.createRectangle(x, y, z, h, w), Primitive.rectangle_indices, color, texCoord); - this.transform = Matrix4f.identity(); - this.scale(new Vector3f(size, size,1)); - this.scalingFactor = size; - this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); - this.zPos = z; - this.setTexture(texPath); - } -} From 574e31e02d6e84923db59fb360ee5f27d3808a1e Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 21 May 2021 19:51:29 +0200 Subject: [PATCH 40/53] Correct frag shader when using texture + color --- shaders/ObjectGlTexColor/frag.glsl | 2 +- src/engine/Engine.java | 11 ++--------- src/engine/object/ObjectGl.java | 5 +++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl index 554ab5f..758da47 100644 --- a/shaders/ObjectGlTexColor/frag.glsl +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -9,5 +9,5 @@ uniform sampler2D texture1; void main() { - FragColor = texture(texture1, texCoord) * color; + FragColor = mix(texture(texture1, texCoord), color, 0.5); } \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index b0774ba..cd37062 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -151,7 +151,7 @@ public class Engine { public static void main(String[] args) { Engine engine = new Engine(); - int speed = 2 ; //vitesse déplacement Object + int speed = 10 ; //vitesse déplacement Object engine.init(); // Add objects to render @@ -205,13 +205,7 @@ public class Engine { if(engine.shouldClose()) engine.setRunning(false); } } - -// public static void input(ObjectGl token, int speed) { -// if (Input.isKeyDown(GLFW.GLFW_KEY_W))token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); -// if (Input.isKeyDown(GLFW.GLFW_KEY_A))token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); -// if (Input.isKeyDown(GLFW.GLFW_KEY_S))token.translate(new Vector3f ( 0.0f,speed * -5.0f, 0.0f)); -// if (Input.isKeyDown(GLFW.GLFW_KEY_D))token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); -// } + private static void gamepadInput(ObjectGl token, int speed) { ByteBuffer gamepadButton = glfwGetJoystickButtons(GLFW_JOYSTICK_1); FloatBuffer gamepadAxes = glfwGetJoystickAxes(GLFW_JOYSTICK_1); @@ -287,5 +281,4 @@ public class Engine { token.setTextureWrap(178,0,62,82); } } - } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index f22e1ca..72edfbb 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -32,7 +32,7 @@ public class ObjectGl { } /** - * Create a rectangle shape + * Create a rectangle shape, use setTextureWrap to correctly align the texture with the model * @param z depth of your model the larger it is the more it will be "close" to the camera * @param h height of the rectangle * @param w width of the rectangle @@ -112,6 +112,7 @@ public class ObjectGl { } public void setTextureWrap(float x, float y, float w, float h){ + // TODO set sticky property this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(this.zPos, w, h)); int texWidth = this.texture.getWidth(); int texHeight = this.texture.getHeight(); @@ -128,7 +129,7 @@ public class ObjectGl { this.setTextureWrap(result); } - public void setTextureWrap(float[] texture){ + private void setTextureWrap(float[] texture){ this.vertexArray.swapTextureBufferObject(texture); } From ee8473e34476ada73c5db59c24f24adb4080e1ff Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 21 May 2021 21:01:42 +0200 Subject: [PATCH 41/53] Doc d'ObjectGl dans un Anglais approximatif --- src/engine/Engine.java | 2 +- src/engine/object/ObjectGl.java | 82 +++++++++++++++++++++++---------- src/engine/object/SortZ.java | 2 +- 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index cd37062..1642672 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -139,7 +139,7 @@ public class Engine { } /** - * Est appelé à chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu + * Est appelé àa chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu * pour quelle corresponde à la taille de la fenêtre */ private static final GLFWFramebufferSizeCallback resizeWindow = new GLFWFramebufferSizeCallback(){ diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 72edfbb..73f840a 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -1,14 +1,7 @@ package engine.object; -import engine.graphics.Texture; -import engine.math.Primitive; -import engine.graphics.VertexArray; -import engine.graphics.Shader; -import engine.math.Matrix4f; -import engine.math.Vector3f; - -import java.util.ArrayList; -import java.util.List; +import engine.graphics.*; +import engine.math.*; /** * @@ -19,28 +12,29 @@ public class ObjectGl { protected Shader shader; protected Matrix4f transform; + /** + * Projection and view matrix are set by the engine do not modify + */ public static Matrix4f projection; public static Matrix4f view; - public float zPos; - public float scalingFactor; + private float zPos; + private float width; // To be used in setTextureWrap + private float height; + private float scalingFactor; private Texture texture; - public ObjectGl(){ - - } - /** * Create a rectangle shape, use setTextureWrap to correctly align the texture with the model * @param z depth of your model the larger it is the more it will be "close" to the camera - * @param h height of the rectangle - * @param w width of the rectangle + * @param w height of the rectangle + * @param h width of the rectangle * @param size scaling factor of the rectangle, the model could not show up because this value is too small or too large, a good compromise is between 2 and 15 * @param tex set to null if you don't want a tex on your model * @param color set to null if you don't want a Color on your model */ - public ObjectGl(float z, float h, float w, float size, String tex, Vector3f color){ + public ObjectGl(float z, float w, float h, float size, String tex, Vector3f color){ float[] colorBuffer = null; // Check des options if (color != null){ @@ -56,10 +50,12 @@ public class ObjectGl { this.texture = new Texture(tex, 0); } - this.vertexArray = new VertexArray(Primitive.createRectangle(z, h, w), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); + this.vertexArray = new VertexArray(Primitive.createRectangle(z, w, h), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); - this.scalingFactor = size; this.zPos = z; + this.height = h; + this.width = w; + this.scalingFactor = size; this.transform = Matrix4f.identity(); this.scale(new Vector3f(size, size,1f)); @@ -75,10 +71,19 @@ public class ObjectGl { } } + /** + * Reset the transform matrix, the model will appear at the 0.0.0 coordinate, his scaleFactor will be set to zero + * Because the model is at position 0 on the z axis he will not show up on screen + */ public void resetTransform(){ this.transform = Matrix4f.identity(); + this.scalingFactor = 1; } + /** + * Move the object according to vec, direction can change if rotation method have been used + * @param vec Vector3f + */ public void translate(Vector3f vec){ vec.divXY(this.scalingFactor); this.transform = this.transform.multiply(Matrix4f.translate(vec)); @@ -86,33 +91,55 @@ public class ObjectGl { } /** - * Comme on fonctionne avec des sprites on part du principe que x et y sont scale de manière uniforme - * ou tout du moins que this.scalingFactor corresponds au scaling de x - * @param vec le vecteur de transformation + * Scale the model with the vec vector, the x component is used to mitigate size modification in the behavior of other transformation method + * @param vec Vector3f */ public void scale(Vector3f vec){ this.scalingFactor *= vec.x; this.transform = this.transform.multiply(Matrix4f.scale(vec)); } + /** + * rotate the model by angle degree on the local x axis, beware this will change the behavior of the translate method + * @param angle in degree + */ public void rotateX(float angle){ this.transform = this.transform.multiply(Matrix4f.rotateX(angle)); } + /** + * rotate the model by angle degree on the local y axis, beware this will change the behavior of the translate method + * @param angle in degree + */ public void rotateY(float angle){ this.transform = this.transform.multiply(Matrix4f.rotateY(angle)); } + /** + * rotate the model by angle degree on the local z axis, beware this will change the behavior of the translate method + * @param angle in degree + */ public void rotateZ(float angle){ this.transform = this.transform.multiply(Matrix4f.rotateZ(angle)); } + /** + * Set a new texture to be used on the model. You may need to use setTextureWrap tp get the correct wrap + * @param texPath path to the new texture + */ public void setTexture(String texPath){ this.texture = new Texture(texPath, 0); } + /** + * Change the wrapping coordinate + * @param x starting wrapping on the horizontal axis + * @param y starting wrapping on the vertical axis + * @param w the length of the wrapping on the horizontal axis + * @param h the length of the wrapping on the vertical axis + */ public void setTextureWrap(float x, float y, float w, float h){ - // TODO set sticky property + // TODO set sticky property + precision issue this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(this.zPos, w, h)); int texWidth = this.texture.getWidth(); int texHeight = this.texture.getHeight(); @@ -133,6 +160,13 @@ public class ObjectGl { this.vertexArray.swapTextureBufferObject(texture); } + public float getZPos(){ + return zPos; + } + + /** + * Do shader binding, texture binding and vertexArray drawing + */ public void render(){ this.shader.enable(); if (this.texture != null) this.texture.bind(); diff --git a/src/engine/object/SortZ.java b/src/engine/object/SortZ.java index cc66e86..5b9cd33 100644 --- a/src/engine/object/SortZ.java +++ b/src/engine/object/SortZ.java @@ -6,6 +6,6 @@ public class SortZ implements Comparator { public int compare(ObjectGl a, ObjectGl b) { - return (int) (a.zPos - b.zPos); + return (int) (a.getZPos() - b.getZPos()); } } \ No newline at end of file From 5c48a88be40d581346b444e7b6dac21a68b77f60 Mon Sep 17 00:00:00 2001 From: Antoine Date: Sun, 23 May 2021 00:57:29 +0200 Subject: [PATCH 42/53] =?UTF-8?q?syst=C3=A8me=20pour=20contr=C3=B4ler=20le?= =?UTF-8?q?=20rendu=20de=20sprite=20de=20taille=20differente=20#=20Pour=20?= =?UTF-8?q?l'instant=20toutes=20les=20sprites=20d'un=20m=C3=AAme=20=C3=A9t?= =?UTF-8?q?at=20doivent=20=C3=AAtre=20de=20la=20m=C3=AAme=20taille?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/Engine.java | 34 ++++++++++++++++++++------------- src/engine/object/ObjectGl.java | 27 +++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 1642672..65dbe19 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -160,8 +160,9 @@ public class Engine { String path2 = "textures/awesomeface.png"; ObjectGl zangief = new ObjectGl(0f,60f,80f,10f, path, null); + zangief.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); engine.add_objectGl(zangief); -// zangief.translate(new Vector3f(-600.0f,100,10.0f)); + zangief.translate(new Vector3f(-5000.0f,0.0f,10.0f)); ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); engine.add_objectGl(smiley2); @@ -176,8 +177,7 @@ public class Engine { lastFrame = System.currentTimeMillis(); // Game logic should fit here - zangief.setTextureWrap(58,0,62,84); - gamepadInput(zangief, speed); +// gamepadInput(zangief, speed); input(zangief, speed); // input(smiley2, speed); @@ -226,9 +226,9 @@ public class Engine { if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche //joystick gauche token.translate(new Vector3f (5*speed * gamepadAxes.get(2), 0.0f, 0.0f)); if ( gamepadAxes.get(2) < -0.1 ){ - token.setTextureWrap(121,0,57,80); + token.setTextureWrap(121,0,57,80, ObjectGl.DEFAULT); }else if (gamepadAxes.get(2) > 0.1) { - token.setTextureWrap(178,0,62,82); + token.setTextureWrap(178,0,62,82, ObjectGl.DEFAULT); } } @@ -266,19 +266,27 @@ public class Engine { } public static void input(ObjectGl token, int speed) { - if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { - token.scale(new Vector3f (1.01f, 1.01f, 0.0f)); + boolean keyPressed = false; + if (Input.isKeyDown(GLFW.GLFW_KEY_S)) { + token.setTextureWrap(161,260,56,59, ObjectGl.STICK_BOTTOM); + keyPressed = true; + } else if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { +// token.translate(new Vector3f (0.0f, speed * 5.0f, 0.0f)); + keyPressed = true; } if (Input.isKeyDown(GLFW.GLFW_KEY_A)) { token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); - token.setTextureWrap(121,0,57,80); +// token.setTextureWrap(121,0,57,80, ObjectGl.STICK_TOP); + token.setTextureWrap(121,0,57,82, ObjectGl.STICK_TOP); + keyPressed = true; } - if (Input.isKeyDown(GLFW.GLFW_KEY_S)) { - token.setTextureWrap(161,260,56,59); - } - if (Input.isKeyDown(GLFW.GLFW_KEY_D)) { + else if (Input.isKeyDown(GLFW.GLFW_KEY_D)) { token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); - token.setTextureWrap(178,0,62,82); +// token.setTextureWrap(178,0,62,82, ObjectGl.STICK_TOP); + token.setTextureWrap(178,0,62,82, ObjectGl.STICK_TOP); + keyPressed = true; } +// if (!keyPressed) token.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); + if (!keyPressed) token.setTextureWrap(58,0,62,82, ObjectGl.STICK_TOP); } } diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 73f840a..4d7f212 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -8,6 +8,13 @@ import engine.math.*; */ public class ObjectGl { + /** + * STATE CONST DECLARATION + */ + public static final int DEFAULT = 0, STICK_BOTTOM = 1, STICK_TOP = 2; + + private int stick_state; + protected VertexArray vertexArray; protected Shader shader; protected Matrix4f transform; @@ -58,16 +65,17 @@ public class ObjectGl { this.scalingFactor = size; this.transform = Matrix4f.identity(); this.scale(new Vector3f(size, size,1f)); + this.stick_state = DEFAULT; // use different shader for each set of option if (tex == null && color == null){ this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); } else if (tex == null){ - this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); // TODO + this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); } else if (color == null){ this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); } else { - this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); // TODO + this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); } } @@ -138,8 +146,21 @@ public class ObjectGl { * @param w the length of the wrapping on the horizontal axis * @param h the length of the wrapping on the vertical axis */ - public void setTextureWrap(float x, float y, float w, float h){ + public void setTextureWrap(float x, float y, float w, float h, int sticky){ // TODO set sticky property + precision issue + if (this.stick_state != sticky){ // Check if we're using a new dimension + if (sticky == STICK_BOTTOM){ + this.stick_state = STICK_BOTTOM; + this.translate(new Vector3f(0.0f, (h - this.height)*this.scalingFactor, 0.0f)); + } else if (sticky == STICK_TOP){ + this.stick_state = STICK_TOP; + this.translate(new Vector3f(0.0f, (h - this.height)*this.scalingFactor, 0.0f)); + } else { + this.stick_state = DEFAULT; + } + } + this.height = h; + this.width = w; this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(this.zPos, w, h)); int texWidth = this.texture.getWidth(); int texHeight = this.texture.getHeight(); From 6262dd42d7ce5b65371006c26fb419b6fa7ad85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Mon, 24 May 2021 19:24:57 +0200 Subject: [PATCH 43/53] we don't call inputGamepad if there is no gamepad --- src/engine/Engine.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 65dbe19..26a719d 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -22,6 +22,8 @@ public class Engine { private static long window; private final List objectsGl; + + private static boolean present = glfwJoystickPresent(GLFW_JOYSTICK_1); private boolean running; @@ -56,7 +58,7 @@ public class Engine { this.setWindow(glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL)); assert getWindow() != NULL; - boolean present = glfwJoystickPresent(GLFW_JOYSTICK_1); + System.out.println(present); // On récupère les informations du moniteur principal @@ -176,8 +178,11 @@ public class Engine { while(engine.isRunning()){ lastFrame = System.currentTimeMillis(); // Game logic should fit here - -// gamepadInput(zangief, speed); + + if (present) { + gamepadInput(zangief, speed); + } + input(zangief, speed); // input(smiley2, speed); From 72c0098f9a7aaa71581fedd88c993f2b5c772d0c Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 24 May 2021 19:26:36 +0200 Subject: [PATCH 44/53] Pos zangief --- src/engine/Engine.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 26a719d..4a4957a 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -41,6 +41,7 @@ public class Engine { /** * Start the engine * Create the window + * Set the color of the background */ public void init() { glfwInit(); @@ -112,6 +113,10 @@ public class Engine { glfwSwapBuffers(getWindow()); //Envoie le buffer vers le moniteur } + /** + * Add obj to the render queue + * @param obj + */ public void add_objectGl(ObjectGl obj){ objectsGl.add(obj); } @@ -164,7 +169,7 @@ public class Engine { ObjectGl zangief = new ObjectGl(0f,60f,80f,10f, path, null); zangief.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); engine.add_objectGl(zangief); - zangief.translate(new Vector3f(-5000.0f,0.0f,10.0f)); + zangief.translate(new Vector3f(-5000.0f,500.0f,10.0f)); ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); engine.add_objectGl(smiley2); @@ -219,7 +224,7 @@ public class Engine { assert gamepadButton != null; String name = GLFW.glfwGetJoystickName(GLFW_JOYSTICK_1); - //System.out.println("GamePad Name :" + name); + System.out.println("GamePad Name :" + name); // for (int i =0 ; i < gamepadAxes.capacity(); i++) { // System.out.println(i + " :" + gamepadAxes.get(i)); // } From db4e2d0e869df0dfdfb866996f920b0e355aed5a Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 26 May 2021 17:23:13 +0200 Subject: [PATCH 45/53] Shaders modif --- shaders/ObjectGl/frag.glsl | 2 ++ shaders/ObjectGlColor/frag.glsl | 2 ++ shaders/ObjectGlTex/frag.glsl | 3 +- shaders/ObjectGlTex/vert.glsl | 2 +- shaders/ObjectGlTexColor/frag.glsl | 10 +++++-- shaders/ObjectGlTexColor/vert.glsl | 2 +- shaders/PowerUpShader/FragEnergyWave.glsl | 21 +++++++++++++ shaders/PowerUpShader/VertEnergyWave.glsl | 21 +++++++++++++ src/engine/Engine.java | 8 +++-- src/engine/object/ObjectGl.java | 36 +++++++++++++++++++++-- 10 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 shaders/PowerUpShader/FragEnergyWave.glsl create mode 100644 shaders/PowerUpShader/VertEnergyWave.glsl diff --git a/shaders/ObjectGl/frag.glsl b/shaders/ObjectGl/frag.glsl index 9713fc4..f76853f 100644 --- a/shaders/ObjectGl/frag.glsl +++ b/shaders/ObjectGl/frag.glsl @@ -2,6 +2,8 @@ out vec4 FragColor; +uniform float time; + void main() { FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/shaders/ObjectGlColor/frag.glsl b/shaders/ObjectGlColor/frag.glsl index 45e8b9c..3a1ca99 100644 --- a/shaders/ObjectGlColor/frag.glsl +++ b/shaders/ObjectGlColor/frag.glsl @@ -4,6 +4,8 @@ in vec3 color; out vec4 FragColor; +uniform float time; + void main() { FragColor = vec4(color, 1.0f); diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl index b3d6cfd..a13c996 100644 --- a/shaders/ObjectGlTex/frag.glsl +++ b/shaders/ObjectGlTex/frag.glsl @@ -1,10 +1,11 @@ -#version 330 core +#version 410 core out vec4 FragColor; in vec2 texCoord; uniform sampler2D texture1; +uniform float time; void main() { diff --git a/shaders/ObjectGlTex/vert.glsl b/shaders/ObjectGlTex/vert.glsl index b90059c..64f4b92 100644 --- a/shaders/ObjectGlTex/vert.glsl +++ b/shaders/ObjectGlTex/vert.glsl @@ -1,4 +1,4 @@ -#version 330 core +#version 410 core layout (location = 0) in vec3 aPos; layout (location = 2) in vec2 aTexCoord; diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl index 758da47..2f6e845 100644 --- a/shaders/ObjectGlTexColor/frag.glsl +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -1,4 +1,4 @@ -#version 330 core +#version 410 core out vec4 FragColor; @@ -6,8 +6,14 @@ in vec4 color; in vec2 texCoord; uniform sampler2D texture1; +uniform float time; void main() { - FragColor = mix(texture(texture1, texCoord), color, 0.5); + vec4 tex = texture(texture1, texCoord); + if (tex.a == 0.0){ + FragColor = tex; + } else{ + FragColor = mix(tex, color, 0.5); + } } \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/vert.glsl b/shaders/ObjectGlTexColor/vert.glsl index a591694..d1e7488 100644 --- a/shaders/ObjectGlTexColor/vert.glsl +++ b/shaders/ObjectGlTexColor/vert.glsl @@ -1,4 +1,4 @@ -#version 330 core +#version 410 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; diff --git a/shaders/PowerUpShader/FragEnergyWave.glsl b/shaders/PowerUpShader/FragEnergyWave.glsl new file mode 100644 index 0000000..a712d66 --- /dev/null +++ b/shaders/PowerUpShader/FragEnergyWave.glsl @@ -0,0 +1,21 @@ +#version 410 + +out vec4 FragColor; + +in vec4 color; +in vec4 position; +in vec2 texCoord; + +uniform sampler2D texture1; +uniform float time; + +void main() +{ // Pas fini c'est moche + vec4 colorPoweredUp = vec4(color.xyz * sin(position.y + time*40) * cos(position.x + time*40), 1.0); + vec4 tex = texture(texture1, texCoord); + if (tex.a == 0.0){ + FragColor = tex; + } else{ + FragColor = mix(tex, colorPoweredUp, 0.5); + } +} diff --git a/shaders/PowerUpShader/VertEnergyWave.glsl b/shaders/PowerUpShader/VertEnergyWave.glsl new file mode 100644 index 0000000..39a279a --- /dev/null +++ b/shaders/PowerUpShader/VertEnergyWave.glsl @@ -0,0 +1,21 @@ +#version 410 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; +layout (location = 2) in vec2 aTexCoord; + +out vec4 position; +out vec2 texCoord; +out vec4 color; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 transform; + +void main() +{ + gl_Position = projection * view * transform * vec4(aPos, 1.0); + color = vec4(aColor, 1.0f); + texCoord = aTexCoord; + position = vec4(aPos, 1.0); +} \ No newline at end of file diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 4a4957a..3c926f0 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -170,10 +170,12 @@ public class Engine { zangief.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); engine.add_objectGl(zangief); zangief.translate(new Vector3f(-5000.0f,500.0f,10.0f)); + zangief.setColor(new Vector3f(0.5f, 0.0f, 0.0f)); +// zangief.setShader("shaders/PowerUpShader/VertEnergyWave.glsl","shaders/PowerUpShader/FragEnergyWave.glsl"); - ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); - engine.add_objectGl(smiley2); - smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); +// ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); +// engine.add_objectGl(smiley2); +// smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); long timer = System.currentTimeMillis(); long lastFrame; diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java index 4d7f212..723cc2d 100644 --- a/src/engine/object/ObjectGl.java +++ b/src/engine/object/ObjectGl.java @@ -3,6 +3,8 @@ package engine.object; import engine.graphics.*; import engine.math.*; +import static org.lwjgl.glfw.GLFW.glfwGetTime; + /** * */ @@ -30,6 +32,8 @@ public class ObjectGl { private float height; private float scalingFactor; + public boolean useTime; + private Texture texture; /** @@ -57,15 +61,17 @@ public class ObjectGl { this.texture = new Texture(tex, 0); } - this.vertexArray = new VertexArray(Primitive.createRectangle(z, w, h), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); - this.zPos = z; this.height = h; this.width = w; + + this.vertexArray = new VertexArray(Primitive.createRectangle(this.zPos, this.width, this.height), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); + this.scalingFactor = size; this.transform = Matrix4f.identity(); this.scale(new Vector3f(size, size,1f)); this.stick_state = DEFAULT; + this.useTime = false; // use different shader for each set of option if (tex == null && color == null){ @@ -177,6 +183,29 @@ public class ObjectGl { this.setTextureWrap(result); } + /** + * Set a new shader to be used by this object + * @param vert path to glsl Vertex Shader + * @param frag path to glsl Fragment Shader + */ + public void setShader(String vert, String frag){ + this.shader = new Shader(vert, frag); + } + + /** + * Set a new Color for the object if the shader do not use the color attrib this will make no change. + * @param color Vector3f r,g,b format + */ + public void setColor(Vector3f color){ + float[] colorBuffer = new float[] { + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z + }; + this.vertexArray = new VertexArray(Primitive.createRectangle(this.zPos, this.width, this.height), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); + } + private void setTextureWrap(float[] texture){ this.vertexArray.swapTextureBufferObject(texture); } @@ -189,9 +218,12 @@ public class ObjectGl { * Do shader binding, texture binding and vertexArray drawing */ public void render(){ + this.shader.enable(); if (this.texture != null) this.texture.bind(); + if (this.useTime) this.shader.setUniform1f("time", (float) glfwGetTime()); + this.shader.setUniformMat4f("projection", projection); this.shader.setUniformMat4f("view", view); this.shader.setUniformMat4f("transform", this.transform); From dc2b696f7ea93773e7358c9cf746359ee158c83f Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 27 May 2021 00:14:28 +0200 Subject: [PATCH 46/53] Shaders modif --- .../VertEnergyWave.glsl => StylishShaders/BasicVert.glsl} | 4 ++-- .../FragEnergyWave.glsl => StylishShaders/EnergyWave.glsl} | 4 ++-- src/engine/Engine.java | 5 +++-- src/engine/graphics/VertexArray.java | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) rename shaders/{PowerUpShader/VertEnergyWave.glsl => StylishShaders/BasicVert.glsl} (88%) rename shaders/{PowerUpShader/FragEnergyWave.glsl => StylishShaders/EnergyWave.glsl} (72%) diff --git a/shaders/PowerUpShader/VertEnergyWave.glsl b/shaders/StylishShaders/BasicVert.glsl similarity index 88% rename from shaders/PowerUpShader/VertEnergyWave.glsl rename to shaders/StylishShaders/BasicVert.glsl index 39a279a..625b793 100644 --- a/shaders/PowerUpShader/VertEnergyWave.glsl +++ b/shaders/StylishShaders/BasicVert.glsl @@ -4,7 +4,7 @@ layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aTexCoord; -out vec4 position; +out vec2 fragCoord; out vec2 texCoord; out vec4 color; @@ -17,5 +17,5 @@ void main() gl_Position = projection * view * transform * vec4(aPos, 1.0); color = vec4(aColor, 1.0f); texCoord = aTexCoord; - position = vec4(aPos, 1.0); + fragCoord = aPos.xy; } \ No newline at end of file diff --git a/shaders/PowerUpShader/FragEnergyWave.glsl b/shaders/StylishShaders/EnergyWave.glsl similarity index 72% rename from shaders/PowerUpShader/FragEnergyWave.glsl rename to shaders/StylishShaders/EnergyWave.glsl index a712d66..a550c6b 100644 --- a/shaders/PowerUpShader/FragEnergyWave.glsl +++ b/shaders/StylishShaders/EnergyWave.glsl @@ -3,7 +3,7 @@ out vec4 FragColor; in vec4 color; -in vec4 position; +in vec2 fragCoord; in vec2 texCoord; uniform sampler2D texture1; @@ -11,7 +11,7 @@ uniform float time; void main() { // Pas fini c'est moche - vec4 colorPoweredUp = vec4(color.xyz * sin(position.y + time*40) * cos(position.x + time*40), 1.0); + vec4 colorPoweredUp = abs(vec4(color.xyz * sin(fragCoord.y + time*20), 1.0)); vec4 tex = texture(texture1, texCoord); if (tex.a == 0.0){ FragColor = tex; diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 3c926f0..505ac5a 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -170,8 +170,9 @@ public class Engine { zangief.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); engine.add_objectGl(zangief); zangief.translate(new Vector3f(-5000.0f,500.0f,10.0f)); - zangief.setColor(new Vector3f(0.5f, 0.0f, 0.0f)); -// zangief.setShader("shaders/PowerUpShader/VertEnergyWave.glsl","shaders/PowerUpShader/FragEnergyWave.glsl"); + zangief.setColor(new Vector3f(0.0f, 0.0f, 1.0f)); + zangief.useTime = true; + zangief.setShader("shaders/StylishShaders/BasicVert.glsl","shaders/StylishShaders/EnergyWave.glsl"); // ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); // engine.add_objectGl(smiley2); diff --git a/src/engine/graphics/VertexArray.java b/src/engine/graphics/VertexArray.java index c96feec..a8dc154 100644 --- a/src/engine/graphics/VertexArray.java +++ b/src/engine/graphics/VertexArray.java @@ -29,7 +29,6 @@ public class VertexArray { EBO = glGenBuffers(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); From 33a02891fccaffef672009756b35ee68bae058d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Autin?= Date: Thu, 27 May 2021 02:19:42 +0200 Subject: [PATCH 47/53] =?UTF-8?q?Ajout=20des=20d=C3=A9pendances=20=C3=A0?= =?UTF-8?q?=20JavaFX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 438626a..7f927f5 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,11 @@ lwjgl-stb ${lwjgl.natives} + + org.openjfx + javafx-controls + 11 + https://gitlab.istic.univ-rennes1.fr/fautin/jeu-de-combat From 75865640aa942b5ec07b46673b4f15477aa60d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Autin?= Date: Thu, 27 May 2021 03:02:36 +0200 Subject: [PATCH 48/53] =?UTF-8?q?Progr=C3=A8s=20ajout=20Launcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout de JavaFX.fxml aux dépendances, aménagement de l'architecture du launcher pour (launcher étend maintenant Application, Main lance correctement le Launcher) --- pom.xml | 8 +++++++- src/Main.java | 14 ++------------ src/launcher/Launcher.java | 17 +++++++++++++---- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 7f927f5..0d19601 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 3.2.3 natives-windows + 16 @@ -93,7 +94,12 @@ org.openjfx javafx-controls - 11 + ${javafx.version} + + + org.openjfx + javafx-fxml + ${javafx.version} diff --git a/src/Main.java b/src/Main.java index d4c19c8..7c9120e 100644 --- a/src/Main.java +++ b/src/Main.java @@ -9,25 +9,15 @@ import launcher.Launcher; import engine.Engine; +import javafx.application.Application; public class Main { - // Interface de configuration et lancement - static Launcher launcher = new Launcher(); - // Moteur de jeu static Engine game = new Engine(); public static void main(String[] args) { - - - // Lancement de l'interface de configuration du Jeu - try { - launcher.launch(); - } catch (Exception e) { - e.printStackTrace(); - } - + Application.launch(Launcher.class, args); } } diff --git a/src/launcher/Launcher.java b/src/launcher/Launcher.java index 9e34398..bf346f9 100644 --- a/src/launcher/Launcher.java +++ b/src/launcher/Launcher.java @@ -9,11 +9,20 @@ package launcher; -public class Launcher { +import javafx.application.Application; +import javafx.scene.Group; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.shape.Circle; +import javafx.stage.Stage; +import javafx.fxml.*; - public void launch() throws Exception { - - System.out.println("Hello world!"); +public class Launcher extends Application { + + Parent root = FXMLLoader.load("ui/main.fxml"); + Scene main = new Scene() + + public void start(Stage primaryStage) throws Exception { } From 9ced943c0a2333fea4c922094accc58e71702973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Autin?= Date: Thu, 27 May 2021 03:04:08 +0200 Subject: [PATCH 49/53] Ajout du fxml du launcher (initial) --- ui/launcher.fxml | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 ui/launcher.fxml diff --git a/ui/launcher.fxml b/ui/launcher.fxml new file mode 100644 index 0000000..f4b5d83 --- /dev/null +++ b/ui/launcher.fxml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + +