From 76c7e1808c0c635da0d32dd323cf0677098aa3d3 Mon Sep 17 00:00:00 2001 From: kacperks Date: Wed, 1 Mar 2023 07:22:50 +0100 Subject: [PATCH] CMake build added. Basic OpenGL 3.3 Mesh Rendering --- CMakeLists.txt | 19 ++ GUI.cpp | 51 ----- XtreemNodes.depend | 134 ------------ XtreemNodes.layout | 114 ---------- XtreemNodesLinux.cbp | 59 ----- XtreemNodesWindows.cbp | 64 ------ XtreemNodesWindows.depend | 71 ------ XtreemNodesWindows.layout | 30 --- data/img/apple.png | Bin 0 -> 231 bytes data/img/awesomeface.png | Bin 0 -> 44004 bytes data/img/cobble.png | Bin 0 -> 225 bytes data/img/gold.png | Bin 0 -> 401 bytes data/img/grass.png | Bin 778 -> 423 bytes data/img/grass_side.png | Bin 0 -> 857 bytes data/img/iron.png | Bin 0 -> 289 bytes data/img/maple.png | Bin 0 -> 991 bytes data/img/oak.png | Bin 0 -> 877 bytes data/img/test.png | Bin 0 -> 347 bytes data/img/texturemap.png | Bin 5415 -> 0 bytes data/shader/MeshF.shader | 159 ++++++++++++++ data/shader/MeshV.shader | 21 ++ include/GUI.h | 26 --- include/NodeRenderer.h | 144 ------------- main.cpp | 196 ----------------- {include => src}/Base.h | 5 + src/Camera.h | 113 ++++++++++ {include => src}/FastNoiseLite.h | 0 src/GLMaterial.h | 42 ++++ src/GLMesh.h | 53 +++++ src/GLShader.cpp | 115 ++++++++++ src/GLShader.h | 65 ++++++ src/GLVertex.h | 19 ++ src/GLVertexArray.h | 70 ++++++ LevelGenerator.cpp => src/LevelGenerator.cpp | 26 +-- {include => src}/LevelGenerator.h | 3 +- {include => src}/Logger.h | 0 MapBlock.cpp => src/MapBlock.cpp | 3 +- {include => src}/MapBlock.h | 3 +- src/NodeRenderer.h | 34 +++ {include => src}/Nodes.h | 0 {include => src}/TextureHandler.h | 15 +- {include => src}/Utilities.h | 0 src/XtreemNodes.h | 0 src/main.cpp | 216 +++++++++++++++++++ src/stb_image.cpp | 2 + {include => src}/stb_image.h | 0 46 files changed, 960 insertions(+), 912 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 GUI.cpp delete mode 100644 XtreemNodes.depend delete mode 100644 XtreemNodes.layout delete mode 100644 XtreemNodesLinux.cbp delete mode 100644 XtreemNodesWindows.cbp delete mode 100644 XtreemNodesWindows.depend delete mode 100644 XtreemNodesWindows.layout create mode 100644 data/img/apple.png create mode 100644 data/img/awesomeface.png create mode 100644 data/img/cobble.png create mode 100644 data/img/gold.png create mode 100644 data/img/grass_side.png create mode 100644 data/img/iron.png create mode 100644 data/img/maple.png create mode 100644 data/img/oak.png create mode 100644 data/img/test.png delete mode 100644 data/img/texturemap.png create mode 100644 data/shader/MeshF.shader create mode 100644 data/shader/MeshV.shader delete mode 100644 include/GUI.h delete mode 100644 include/NodeRenderer.h delete mode 100644 main.cpp rename {include => src}/Base.h (81%) create mode 100644 src/Camera.h rename {include => src}/FastNoiseLite.h (100%) create mode 100644 src/GLMaterial.h create mode 100644 src/GLMesh.h create mode 100644 src/GLShader.cpp create mode 100644 src/GLShader.h create mode 100644 src/GLVertex.h create mode 100644 src/GLVertexArray.h rename LevelGenerator.cpp => src/LevelGenerator.cpp (64%) rename {include => src}/LevelGenerator.h (90%) rename {include => src}/Logger.h (100%) rename MapBlock.cpp => src/MapBlock.cpp (97%) rename {include => src}/MapBlock.h (96%) create mode 100644 src/NodeRenderer.h rename {include => src}/Nodes.h (100%) rename {include => src}/TextureHandler.h (89%) rename {include => src}/Utilities.h (100%) create mode 100644 src/XtreemNodes.h create mode 100644 src/main.cpp create mode 100644 src/stb_image.cpp rename {include => src}/stb_image.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..85a7f5b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) + +project(XtreemNodes) + +include_directories("include/") + +file(GLOB_RECURSE Sources "src/**.cpp") + +add_executable(${PROJECT_NAME} ${Sources}) + +if(UNIX AND NOT APPLE) # linux +target_link_libraries(${PROJECT_NAME} -lGL) +target_link_libraries(${PROJECT_NAME} -lGLEW) +target_link_libraries(${PROJECT_NAME} -lglfw) +#target_link_libraries(${PROJECT_NAME} -lsfml-opengl) +endif() + +if (WIN32) # windows +endif(WIN32) \ No newline at end of file diff --git a/GUI.cpp b/GUI.cpp deleted file mode 100644 index c22cb47..0000000 --- a/GUI.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "GUI.h" - -// Code contributed by Kacperks -// COMMENTED OUT BECAUSE IT DOESN'T WORK! -/* -Button::Button(sf::Image* normal, sf::Image* clicked, std::string words, Position2D location) { - this->normal.SetImage(*normal); - this->clicked.SetImage(*clicked); - this->currentSpr=&this->normal; - current =false; - this->normal.SetPosition(location); - this->clicked.SetPosition(location); - String.SetText(words); - String.SetPosition(location.x+3,location.y+3); - String.SetSize(14); -} -void Button::checkClick (sf::Vector2f mousePos) -{ - if (mousePos.x>currentSpr->GetPosition().x && mousePos.x<(currentSpr->GetPosition().x + currentSpr->GetSize().x)) { - if(mousePos.y>currentSpr->GetPosition().y && mousePos.y<(currentSpr->GetPosition().y + currentSpr->GetSize().y)) { - setState(!current); - } - } -} - -void Button::setState(bool which) -{ - current = which; - if (current) { - currentSpr=&clicked; - return; - } - currentSpr=&normal; -} -void Button::setText(std::string words) -{ - String.SetText(words); -} -bool Button::getVar() -{ - return current; -} -sf::Sprite* Button::getSprite() -{ - return currentSpr; -} - -sf::String * Button::getText() -{ - return &String; -}*/ diff --git a/XtreemNodes.depend b/XtreemNodes.depend deleted file mode 100644 index 820c498..0000000 --- a/XtreemNodes.depend +++ /dev/null @@ -1,134 +0,0 @@ -# depslib dependency file v1.0 -<<<<<<< HEAD -1672107417 source:c:\development\xtreemminer\main.cpp -======= -1672097148 source:c:\development\xtreemminer\main.cpp ->>>>>>> dcac2713f6b72f4d2252632a179b30a33fd827a8 - - - "Utilities.h" - "MapBlock.h" - "Base.h" - "NodeRenderer.h" - "TextureHandler.h" - - - - - -<<<<<<< HEAD -1672201712 c:\development\xtreemminer\include\noderenderer.h -======= -1667160597 c:\development\xtreemminer\include\noderenderer.h ->>>>>>> dcac2713f6b72f4d2252632a179b30a33fd827a8 - "Base.h" - "MapBlock.h" - - -1667164077 c:\development\xtreemminer\include\mapblock.h - "Base.h" - - - -1667065840 c:\development\xtreemminer\include\base.h - -1666531672 source:c:\development\xtreemminer\base.cpp - "MapBlock.h" - "Base.h" - -<<<<<<< HEAD -1672341127 c:\development\xtreemminer\include\texturehandler.h -======= -1672105046 c:\development\xtreemminer\include\texturehandler.h ->>>>>>> dcac2713f6b72f4d2252632a179b30a33fd827a8 - "stb_image.h" - "Base.h" - -1666445333 c:\development\xtreemminer\include\stb_image.h - "stb_image.h" - - - - - - - - - - - - - - - -1667065840 c:\development\xtreemminer\include\utilities.h - -1667075984 c:\development\xtreemminer\include\fastnoiselite.h - - -1667350805 source:c:\development\xtreemminer\mapblock.cpp - "MapBlock.h" - -1667294728 source:/home/kacperks/projects/XtreemNodes/MapBlock.cpp - "MapBlock.h" - -1667294728 /home/kacperks/projects/XtreemNodes/include/MapBlock.h - "Base.h" - - - -1667294728 /home/kacperks/projects/XtreemNodes/include/Base.h - -1667294728 source:/home/kacperks/projects/XtreemNodes/main.cpp - - - "Utilities.h" - "MapBlock.h" - "Base.h" - "NodeRenderer.h" - "TextureHandler.h" - - - "FastNoiseLite.h" - - - -1667294728 /home/kacperks/projects/XtreemNodes/include/Utilities.h - -1667294728 /home/kacperks/projects/XtreemNodes/include/NodeRenderer.h - "Base.h" - "MapBlock.h" - - -1667294728 /home/kacperks/projects/XtreemNodes/include/TextureHandler.h - "stb_image.h" - "Base.h" - -1667088826 /home/kacperks/projects/XtreemNodes/include/stb_image.h - "stb_image.h" - - - - - - - - - - - - - - - -1667294728 /home/kacperks/projects/XtreemNodes/include/FastNoiseLite.h - - -1667347897 source:c:\development\xtreemminer\gui.cpp - "GUI.h" - -1667333037 c:\development\xtreemminer\include\gui.h - "Base.h" - - - diff --git a/XtreemNodes.layout b/XtreemNodes.layout deleted file mode 100644 index bd1173a..0000000 --- a/XtreemNodes.layout +++ /dev/null @@ -1,114 +0,0 @@ - - - - -<<<<<<< HEAD - - - -======= - - - ->>>>>>> dcac2713f6b72f4d2252632a179b30a33fd827a8 - - - - - - - -<<<<<<< HEAD - - - - - - - - - - - - - - - - - - - - - -======= - ->>>>>>> dcac2713f6b72f4d2252632a179b30a33fd827a8 - - - - -<<<<<<< HEAD - -======= - - - - - - - - - - - - - - - - - - - - - ->>>>>>> dcac2713f6b72f4d2252632a179b30a33fd827a8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/XtreemNodesLinux.cbp b/XtreemNodesLinux.cbp deleted file mode 100644 index b7c8c00..0000000 --- a/XtreemNodesLinux.cbp +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - diff --git a/XtreemNodesWindows.cbp b/XtreemNodesWindows.cbp deleted file mode 100644 index 1f7feae..0000000 --- a/XtreemNodesWindows.cbp +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - diff --git a/XtreemNodesWindows.depend b/XtreemNodesWindows.depend deleted file mode 100644 index 09f58a1..0000000 --- a/XtreemNodesWindows.depend +++ /dev/null @@ -1,71 +0,0 @@ -# depslib dependency file v1.0 -1667347897 source:c:\development\xtreemminer\gui.cpp - "GUI.h" - -1667333037 c:\development\xtreemminer\include\gui.h - "Base.h" - - - -1667065840 c:\development\xtreemminer\include\base.h - -1672106999 source:c:\development\xtreemminer\main.cpp - - - "Utilities.h" - "MapBlock.h" - "Base.h" - "NodeRenderer.h" - "TextureHandler.h" - "LevelGenerator.h" - - - - - -1667065840 c:\development\xtreemminer\include\utilities.h - -1667164077 c:\development\xtreemminer\include\mapblock.h - "Base.h" - - - -1667160597 c:\development\xtreemminer\include\noderenderer.h - "Base.h" - "MapBlock.h" - - -1672105046 c:\development\xtreemminer\include\texturehandler.h - "stb_image.h" - "Base.h" - -1666445333 c:\development\xtreemminer\include\stb_image.h - "stb_image.h" - - - - - - - - - - - - - - - -1667350805 source:c:\development\xtreemminer\mapblock.cpp - "MapBlock.h" - -1672106616 source:c:\development\xtreemminer\levelgenerator.cpp - "LevelGenerator.h" - -1672106654 c:\development\xtreemminer\include\levelgenerator.h - "FastNoiseLite.h" - "MapBlock.h" - -1667075984 c:\development\xtreemminer\include\fastnoiselite.h - - diff --git a/XtreemNodesWindows.layout b/XtreemNodesWindows.layout deleted file mode 100644 index 9a0d84b..0000000 --- a/XtreemNodesWindows.layout +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/img/apple.png b/data/img/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..750557e631a3a21d7ad06ce765edc26bfcdef3c1 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFUwskHdqW;=tl*U~G@^Omtp>?yQ!=*_4XnB}sdXpc&E`&9ewQ%fzp-|f62 YKRsVW-*aNQE6@cDp00i_>zopr00wzc3jhEB literal 0 HcmV?d00001 diff --git a/data/img/awesomeface.png b/data/img/awesomeface.png new file mode 100644 index 0000000000000000000000000000000000000000..23c778d7a295984701047685137f9e2bbf9d2ea8 GIT binary patch literal 44004 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 b^Z)(@wwV$W5r>vk00000NkvXXu0mjf3vHsc delta 765 zcmVbDiz;A4;(x@JBaNid9QH!li@xpt z4_}Mle*ZPs2SexsuJ_nYNmwFbjsWOF;m3s(GrCY9Auf>=68rlta_T(-$^Ocp(Htte&ja)8g02+Z=YjskA+;m{)s;aTMVKS~(IKT0 z2ql;1+SZRRzH2+nO*(>UOPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2j&S2 z5hNf1y1!-s00P-bL_t(I%WacOZyR+K#eXyNpU%TGb{tP8b&Q;}H8iaPs8LpFM3G>H zgoG4z#im~b35l=5mNomV5aLmUXeml@Ax@of#;13Tk`@y)dk@o0ie<_h==JKP-z%z@;8__F@Oy4;nN7)HP?RJ zE7@q-e0(jT>H>gxm?A3@^|SGs=-zK*)b=JX^AVHUgj!1rL6WIXS!!z{6iM z0G=$GbXfA%8G{+OmyMUo|)O^fcuCF5+>W|(ByrUrm__AR!lvE@1h`HbDY zI`WQF1t1DDl5~#e9up-Qwx+Sa<8t)=dswwPEvLqQ+vR+aVi_8DTJO-i3|3pPO^v&E zb^#du`#rMI2(ygK$rL-u2um6J|3V$t;P)4QW0@L;ED*Y!69doxdWEj46l4ku%x$%Lw@p{N$8j}AzdD*Np%X8Dq-KcnT;*xRnJW))||qdDf$ zV@7F;T+vm|2MMJBQ&}*LXPiDd;OuFa^PZ3U@JkBSpim9Gu%O-EL6kDt&)+c@3#6}3 z56dvg=rkMjmm$?c2SDFc@x2K0mF8RR763o|aly7Nb8>i}^X|p!lx3}gH;s@ZiC|U` zB^k|%OFR>JVL>`uFi#>{P7PDZ@xp>>JR{2Hh-UL9{r(kUlCe>5Ko;RR4pExHJZ0Nf z(W@G-gMi+T6Kqq%2D%rQt7i(Y#z?Zn2M0|IC8trX5Cu~__n7TX3rlDW$0b+8C6bV^ jY8qRH#dQ$V_XGX|vfwv~A~-O}00000NkvXXu0mjf*oKCW literal 0 HcmV?d00001 diff --git a/data/img/iron.png b/data/img/iron.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e6e847bfb0842b2768171ac1522200c9d5d190 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPF7fcO5*u-^Z_N zCdi;9Z+91l4pvzYAZL=Ni(?4K_2eJ_|NplZNT?`&cJAix<=^L|?)3|0sg%+UHdaKt z!q|lgDlXC<#-&<;jcGhgNKEBn0wEy=Xpf)*g9)k91Y#;MAs%=@LIP2aC7<8<&iDHt z={)$AUbo3oNeaU-w%w{vpgkV0eiCKYi0wifK0nbM##Yz(yBJ1X?R6#_xdfI^;6n*A zI~d6i;`u>f3=##DNqi`Yk}#4WpTY}CU?#~Tk||)M2qQ%rDPSTW9{UQZh=~l37txUi z1)ePMz~l*&zsEw72Sq+&2*3)VuON^%PnLMxMl%9g6!4-zm_no|kd}a30%3&!Y=N-R zqyS8jG(}Jp2?tdo?x0pAZ4ob{Rs;_6BI$_0N~14gr$H%AI%&c~RmlD2G=OPfOA&V) zuq4Wn2wUoRBv6tl7o|)(66HyxBLiEeU}&Oc86}cYPsTiSMUJ{MaAX8@Sw<4LaukM1 zxiaI+z(Z9AdD@jZCqsEM1JQMvg=juQL78zgw3ngEO23z3VaOcJFfhZqO4QHLu0oYn z;3|x#P+pj^>p*@xIRN7TJs75`Ng(@tBkL* zuEqu`<7!-hq((uFff@qN4e=X@Py+}F84 zX9Jys*;rMN1zEO|<$~-$H5(6dT#$>`a%^?`Ky7=xnjh%Gt(}^7VAPx*8+xQ`%2 z+NnR6eeEp4u#`3X| z^G~-{o@u{%{DHTg9ew*m=fcUci>JDmp6fN2R?6W|JU!a1V=qU97CJI zd-&S^nc16;B>nk!^HTiCsqxh8$gxV~lN)P)^zQt8UYq)D$NDn<()W0GfE4e4?=9fJiM*eYlXtYQ_Y9Z|paSkxx zfcZYlfD43OaL^4xZaC()MBT`sr#sXShjDWV2VopUaY*ck<2V$@K^!*%8YMuKFpMQ} zBtbxe*Bm8*OjtxOB6&f=iwYzpc@cqx1riBS+Y=-#k+4Xa1qvidC}}K{kU~N-i6kf_ zK?8*X5)H`|CNfZxhEp`8(1^q!GJ|AkkYa$speY7UGa$o2vJXkHaGtTqEF`mN)(0{^ zEawABKbB#UEDNXDp0ppz89OY<_zk03Ka%I*3=dLVZ-IlQK(ESKay+K_F_l9KJdzI} zDv$06bg2QL@rKa~4|0Rn{D5s|5H5s3DTtLquokxN3_H}YLyy2}#Gyu<)d(m?oW-b9 z>(Hk~UB#Ha8pE`hyA*SkY+CTK{QBG+m^Ow#jGg}Fy0kC2wRY&G-aGV>R`c-Sx$vj2jpMp5 zJ=;5{L{I#)b(?HZpFc0W)9N|?!HDgZZ@=8XH@p&8YL?u>;evN%y4-d?_1?C3&q{y) z?mV(~`y#UNbD%cezVpZOllvAn=65f?_0fgRTNkFpzMFg7{8jC%o2mQK6ASk1-Mcpz zU%mHd`}L1+EH0TY9k_cUCeF=XE>8Wm`CoRzcVnXK`^lfiSFa(st@U)d<^Q_ge&*P7 cYqRwSsN+=Y{1=n^?Z%6l^kQ9|FSHK*4{<*0+yDRo literal 0 HcmV?d00001 diff --git a/data/img/test.png b/data/img/test.png new file mode 100644 index 0000000000000000000000000000000000000000..8ace58ba76c59f958b4736be6997a05575f98f23 GIT binary patch literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~`v9L1R|bav z>o^$x11X08)wNZ14YjT9EgfC$z5P8CCr_9*W9sa=v*s_Hw{Y=-#Y-2hShal3y44#u zZvd*^y?58a!}~ikzD)p`o8;~8!qCAg>jC6&7I;J!Gca%qgD@k*tT_@uLG}_)Usv{L zTwSkKL-j;^>lFzk+`he$IW*{frItnwXPbL+k5{7{@Z@lbUJH(kJ^J9ahAKuZ@U57=1<6EWJJ1f6mt$ kMvJ}=iODN6?ASlBTL*K=nm_b21Uitx)78&qol`;+06x5eo&W#< literal 0 HcmV?d00001 diff --git a/data/img/texturemap.png b/data/img/texturemap.png deleted file mode 100644 index b9facd7e630c25b43ae6f9f9303b03f6df669655..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5415 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 MAX_LIGHTS) ? MAX_LIGHTS : pointLightCount; + for (int i = 0; i < count; i++) { + PointLight light = pointLights[i]; + vec3 L = (light.Position - FragPosition); + Luminance += ProcessLight(N, V, L, light.Color, F0, light.Intensity, roughness, metallic, albedo); + } + + // Directional lights + count = (directLightCount > MAX_LIGHTS) ? MAX_LIGHTS : directLightCount; + for (int i = 0; i < count; i++) { + DirectLight light = directLights[i]; + Luminance += ProcessLight(N, V, light.Direction, light.Color, F0, light.Intensity, roughness, metallic, albedo); + } + + // ambient + vec3 ambient = vec3(0.1f) * albedo * ao; + + vec3 color = ambient + Luminance; + // HDR correction + color = color / (color + vec3(1.0)); + // Gamma correction + color = pow(color, vec3(1.0 / 3.0)); + + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/data/shader/MeshV.shader b/data/shader/MeshV.shader new file mode 100644 index 0000000..107706e --- /dev/null +++ b/data/shader/MeshV.shader @@ -0,0 +1,21 @@ +#version 330 core +#extension GL_ARB_explicit_attrib_location : enable + +layout(location = 0) in vec3 aPos; +layout(location = 1) in vec3 aNormal; +layout(location = 2) in vec2 aTexCoords; + +out vec3 Normal; +out vec2 TexCoords; +out vec3 FragPosition; + +uniform mat4 View; +uniform mat4 Model; +uniform mat4 Projection; + +void main() { + TexCoords = aTexCoords; + FragPosition = vec3(Model * vec4(aPos, 1.0)); + Normal = transpose(inverse(mat3(Model))) * aNormal; + gl_Position = Projection * View * vec4(FragPosition, 1.0); +} diff --git a/include/GUI.h b/include/GUI.h deleted file mode 100644 index 4d49831..0000000 --- a/include/GUI.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef GUI -#define GUI -#include "Base.h" -#include -#include - -class Button -{ - public: - Button(sf::Image* normal, sf::Image* clicked, std::string text, Position2D location); - void checkClick(Position2D); - void setState(bool state); - void setText(std::string); - bool getVar(); - sf::Sprite* getSprite(); - sf::String* getText(); - - private: - sf::Sprite normal; - sf::Sprite clicked; - sf::Sprite* currentSpr; - sf::String String; - bool current; -}; - -#endif diff --git a/include/NodeRenderer.h b/include/NodeRenderer.h deleted file mode 100644 index cf3f259..0000000 --- a/include/NodeRenderer.h +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef NODERENDERER_H -#define NODERENDERER_H -#include "Base.h" -#include "MapBlock.h" -#include - -class NodeRenderer -{ - public: - NodeRenderer() - { - - } - - virtual ~NodeRenderer() - { - - } - - int renderNode(int x, int y, int z) - { - Position2D block = BlockUtilities::getBlockFromNodeCoordinates(x, z); // The block the node at (x, y, z) is in - - glColor3f(1.0F, 1.0F, 1.0F); - - glBegin(GL_QUADS); - // Front - if(blockManager.isAir(x, y, z - 1)) - { - glTexCoord2f(.0F, .0F); - glVertex3f(x + .0F, y + 1.0F, z + .0F); - - glTexCoord2f(1.0F, .0F); - glVertex3f(x + 1.0F, y + 1.0F, z + .0F); - - glColor3f(.6F, .6F, .6F); // Bottom vertices - glTexCoord2f(1.0F, 1.0F); - glVertex3f(x + 1.0F, y + .0F, z + .0F); - - glTexCoord2f(.0F, 1.0F); - glVertex3f(x + .0F, y + .0F, z + .0F); - glColor3f(1.0F, 1.0F, 1.0F); - } - - // Back - if(blockManager.isAir(x, y, z + 1)) - { - - glTexCoord2f(.0F, .0F); - glVertex3f(x + .0F, y + 1.0F, z + 1.0F); - - glTexCoord2f(1.0F, .0F); - glVertex3f(x + 1.0F, y + 1.0F, z + 1.0F); - - glColor3f(.6F, .6F, .6F); - glTexCoord2f(1.0F, 1.0F); - glVertex3f(x + 1.0F, y + .0F, z + 1.0F); - - glTexCoord2f(.0F, 1.0F); - glVertex3f(x + .0F, y + .0F, z + 1.0F); - glColor3f(1.0F, 1.0F, 1.0F); - } - - // Right - if(blockManager.isAir(x + 1, y, z)) - { - glTexCoord2f(1.0F, .0F); - glVertex3f(x + 1.0F, y + 1.0F, z + .0F); - - glColor3f(.6F, .6F, .6F); - glTexCoord2f(1.0F, 1.0F); - glVertex3f(x + 1.0F, y + .0F, z + .0F); - - glTexCoord2f(.0F, 1.0F); - glVertex3f(x + 1.0F, y + .0F, z + 1.0F); - glColor3f(1.0F, 1.0F, 1.0F); - - glTexCoord2f(.0F, .0F); - glVertex3f(x + 1.0F, y + 1.0F, z + 1.0F); - } - - // Left - if(blockManager.isAir(x - 1, y, z)) - { - glTexCoord2f(1.0F, .0F); - glVertex3f(x + .0F, y + 1.0F, z + .0F); - - glColor3f(.6F, .6F, .6F); - glTexCoord2f(1.0F, 1.0F); - glVertex3f(x + .0F, y + .0F, z + .0F); - - glTexCoord2f(.0F, 1.0F); - glVertex3f(x + .0F, y + .0F, z + 1.0F); - glColor3f(1.0F, 1.0F, 1.0F); - - glTexCoord2f(.0F, .0F); - glVertex3f(x + .0F, y + 1.0F, z + 1.0F); - } - - // Bottom - //printf("\n\nx: %i, y: %i, z: %i, VALUE: %s", x, y, z, blockManager.isAir(x, y - 1, z) ? "true" : "false"); - if(blockManager.isAir(x, y - 1, z)) - { - //printf("\nWUT? x: %i, y: %i, z: %i, VALUE: %s", x, y, z, blockManager.isAir(x, y - 1, z) ? "true" : "false"); - glTexCoord2f(.0F, .0F); - glVertex3f(x + 1.0F, y + .0F, z + .0F); - - glTexCoord2f(1.0F, .0F); - glVertex3f(x + .0F, y + .0F, z + .0F); - - glTexCoord2f(1.0F, 1.0F); - glVertex3f(x + .0F, y + .0F, z + 1.0F); - - glTexCoord2f(.0F, 1.0F); - glVertex3f(x + 1.0F, y + .0F, z + 1.0F); - } - - // Top - if(blockManager.isAir(x, y + 1, z)) - { - glTexCoord2f(.0F, .0F); - glVertex3f(x + 1.0F, y + 1.0F, z + .0F); - - glTexCoord2f(1.0F, .0F); - glVertex3f(x + .0F, y + 1.0F, z + .0F); - - glTexCoord2f(1.0F, 1.0F); - glVertex3f(x + .0F, y + 1.0F, z + 1.0F); - - glTexCoord2f(.0F, 1.0F); - glVertex3f(x + 1.0F, y + 1.0F, z + 1.0F); - } - - glEnd(); - return 1; - } - - protected: - - private: - -}; - -#endif diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 37de996..0000000 --- a/main.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include "Utilities.h" -#include "MapBlock.h" -#include "Base.h" -#include "NodeRenderer.h" -#include "TextureHandler.h" -#include -#include -#include -#include - -NodeRenderer renderer; - -BlockManager blockManager; -TextureHandler textureHandler; - -GLfloat playerX = 0; -GLfloat playerY = -30; -GLfloat playerZ = -100; -GLfloat playerRotX = 0; -GLfloat playerRotY = 0; - - - -void display() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - - - glRotatef(playerRotX, 1.0F, .0F, .0F); - glRotatef(playerRotY, .0F, 1.0F, .0F); - glTranslatef(playerX, playerY, playerZ); - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - - glBegin(GL_QUADS); - - - for(int x = 0; x < 100; x++) - { - for(int y = 0; y < 64; y++) - { - for(int z = 0; z < 100; z++) - { - if(blockManager.getNodeAt(x, y, z) > 0) - { - textureHandler.getTextureForNode(x, y, z); - renderer.renderNode(x, y, z); - } - } - } - } - - glEnd(); - - glFlush(); - glutSwapBuffers(); -/* - sf::Vector2i lastMousePos = sf::Vector2i(0, 0); - const sf::Window& window = nullptr; - sf::Vector2i mouseDelta = sf::Mouse::getPosition(window) - lastMousePos; - lastMousePos = sf::Mouse::getPosition(window);*/ - //glutPostRedisplay(); - -} - - -void reshape(int width, int height) -{ - glMatrixMode(GL_PROJECTION); - - glLoadIdentity(); - gluPerspective(30, width / (float) height, 5, 512.0F); - glViewport(0, 0, width, height); - - glMatrixMode(GL_MODELVIEW); - glutPostRedisplay(); -} - -void updateTimer(int time) -{ - // Movement - if(sf::Keyboard::isKeyPressed(sf::Keyboard::W)) - { - playerZ += .8; - } - - else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S)) - { - playerZ -= .8; - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::A)) - { - playerX += .8; - } - - else if(sf::Keyboard::isKeyPressed(sf::Keyboard::D)) - { - playerX -= .8; - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) - { - playerY += .8; - } - - else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) - { - playerY -= .8; - } - - // Rotation - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) - { - playerRotY -= 1.8; - } - - else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) - { - playerRotY += 1.8; - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) - { - playerRotX -= 1.8; - } - - else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) - { - playerRotX += 1.8; - } - - if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) - { - exit(0); - } - - glutPostRedisplay(); - glutTimerFunc(30, &updateTimer, 0); -} - -int main(int argc, char **argv) -{ - glutInit(&argc, argv); - glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); - glutInitWindowSize(800, 600); - glutCreateWindow("XtreemNodes Engine - By MCL Software and Cube Software"); - - glClearColor(.4, .7, .8 , 255); - glEnable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); - //glEnable(GL_CULL_FACE); - //glCullFace(GL_FRONT); - //glFrontFace(GL_CCW); - - // Load textures - textureHandler.loadAllTextures(); - - - - - - - -/* - for(int y = 0; y < 16; y++) - { - for(int x = 0; x < 16; x++) - { - for(int z = 0; z < 16; z++) - { - Position2D block = BlockUtilities::getBlockFromNodeCoordinates(x, z); - printf("\n|x: %i |y: %i | z: %i | id: %i", x, y, z, blockManager.getNodeAt(x, y, z)); - - } - } - } - -*/ - - - - - updateTimer(0); - glutDisplayFunc(&display); - glutReshapeFunc(&reshape); - - glutMainLoop(); - - return 0; -} diff --git a/include/Base.h b/src/Base.h similarity index 81% rename from include/Base.h rename to src/Base.h index 20921d7..719c7cd 100644 --- a/include/Base.h +++ b/src/Base.h @@ -1,6 +1,11 @@ +#pragma once #ifndef BASE #define BASE +#include + +#include + //#include "NodeRenderer.h" //#include "MapBlock.h" diff --git a/src/Camera.h b/src/Camera.h new file mode 100644 index 0000000..b8c135b --- /dev/null +++ b/src/Camera.h @@ -0,0 +1,113 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include +#include + +#include + +// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods +enum Camera_Movement { + FORWARD, + BACKWARD, + LEFT, + RIGHT +}; + +const float YAW = -90.0f; +const float PITCH = 0.0f; +const float SPEED = 2.5f; +const float SENSITIVITY = 0.1f; +const float ZOOM = 45.0f; + + +class Camera +{ +public: + glm::vec3 Position; + glm::vec3 Front; + glm::vec3 Up; + glm::vec3 Right; + glm::vec3 WorldUp; + float Yaw; + float Pitch; + float MovementSpeed; + float MouseSensitivity; + float Zoom; + + Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM) + { + Position = position; + WorldUp = up; + Yaw = yaw; + Pitch = pitch; + updateCameraVectors(); + } + Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM) + { + Position = glm::vec3(posX, posY, posZ); + WorldUp = glm::vec3(upX, upY, upZ); + Yaw = yaw; + Pitch = pitch; + updateCameraVectors(); + } + + glm::mat4 GetViewMatrix() + { + return glm::lookAt(Position, Position + Front, Up); + } + + void ProcessKeyboard(Camera_Movement direction, float deltaTime) + { + float velocity = MovementSpeed * deltaTime; + if (direction == FORWARD) + Position += Front * velocity; + if (direction == BACKWARD) + Position -= Front * velocity; + if (direction == LEFT) + Position -= Right * velocity; + if (direction == RIGHT) + Position += Right * velocity; + } + + void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true) + { + xoffset *= MouseSensitivity; + yoffset *= MouseSensitivity; + + Yaw += xoffset; + Pitch += yoffset; + + if (constrainPitch) + { + if (Pitch > 89.0f) + Pitch = 89.0f; + if (Pitch < -89.0f) + Pitch = -89.0f; + } + + updateCameraVectors(); + } + + void ProcessMouseScroll(float yoffset) + { + Zoom -= (float)yoffset; + if (Zoom < 1.0f) + Zoom = 1.0f; + if (Zoom > 45.0f) + Zoom = 45.0f; + } + +private: + void updateCameraVectors() + { + glm::vec3 front; + front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch)); + front.y = sin(glm::radians(Pitch)); + front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch)); + Front = glm::normalize(front); + Up = glm::normalize(glm::cross(Right, Front)); + } +}; +#endif \ No newline at end of file diff --git a/include/FastNoiseLite.h b/src/FastNoiseLite.h similarity index 100% rename from include/FastNoiseLite.h rename to src/FastNoiseLite.h diff --git a/src/GLMaterial.h b/src/GLMaterial.h new file mode 100644 index 0000000..18989dc --- /dev/null +++ b/src/GLMaterial.h @@ -0,0 +1,42 @@ +#ifndef MATERIAL +#define MATERIAL + +#include "GLShader.h" + +struct Material { + Material() = default; + ~Material() = default; + Material(glm::vec3 albedo, GLfloat metallic, GLfloat roughness, GLfloat ao): + Albedo(albedo), Metallic(metallic), Roughness(roughness), Ao(ao) { } + + float Ao = 0.1f; + float Opacity = 1.0f; + float Metallic = 0.4f; + float Roughness = 0.2f; + float Shininess = 1.0f; + float Reflectivity = 0.0f; + glm::vec3 Albedo = glm::vec3(0.6f); + + GLuint AoMap = 0; + GLuint NormalMap = 0; + GLuint AlbedoMap = 0; + GLuint MetallicMap = 0; + GLuint RoughnessMap = 0; + GLuint SpecularBRDFMap = 0; + + void SetUniform(Shader& shader) { + shader.bind(); + shader.setuniform("material.Ao", Ao); + shader.setuniform("material.Albedo", Albedo); + shader.setuniform("material.Metallic", Metallic); + shader.setuniform("material.Roughness", Roughness); + + shader.setuniform("AoMap", AoMap, 0); + shader.setuniform("AlbedoMap", AlbedoMap, 1); + shader.setuniform("NormalMap", NormalMap, 2); + shader.setuniform("MetallicMap", MetallicMap, 3); + shader.setuniform("RoughnessMap", RoughnessMap, 4); + } +}; + +#endif \ No newline at end of file diff --git a/src/GLMesh.h b/src/GLMesh.h new file mode 100644 index 0000000..a728290 --- /dev/null +++ b/src/GLMesh.h @@ -0,0 +1,53 @@ +#ifndef GLMESH_H +#define GLMESH_H + +#include "GLVertexArray.h" +#include "GLMaterial.h" + +class GLMesh { + +public: + GLMesh() = default; + ~GLMesh() = default; + + GLMesh(Vertex* vertices, GLsizei vSize) { + vao = VertexArray(vertices, vSize); + } + + GLMesh(Vertex* vertices, GLsizei vSize, GLuint* indices, GLsizei iSize) { + vao = VertexArray(vertices, vSize, indices, iSize); + } + + GLMesh(Vertex* vertices, GLsizei vSize, GLuint* indices, GLsizei iSize, const Material& matrial): + material(matrial) { + vao = VertexArray(vertices, vSize, indices, iSize); + } + + GLMesh(const VertexArray& vao): vao(vao) {} + + void SetMaterial(const Material& matrial) { + material = matrial; + } + + Material& GetMaterial() { + return material; + } + + void Render(Shader& shader, GLenum mode = GL_TRIANGLE_STRIP) { + material.SetUniform(shader); + shader.bind(); + vao.DrawElements(mode); + } + + void DrawArrays(Shader& shader, GLenum mode = GL_TRIANGLES) { + material.SetUniform(shader); + shader.bind(); + vao.DrawArrays(mode); + } + +private: + VertexArray vao; + Material material; +}; + +#endif \ No newline at end of file diff --git a/src/GLShader.cpp b/src/GLShader.cpp new file mode 100644 index 0000000..4cd476a --- /dev/null +++ b/src/GLShader.cpp @@ -0,0 +1,115 @@ +#include "GLShader.h" +#include "Base.h" +#include +#include +#include +Shader::Shader(const char* vertexPath, const char* fragmentPath) +{ + // 1. retrieve the vertex/fragment source code from filePath + std::string vertexCode; + std::string fragmentCode; + std::ifstream vShaderFile; + std::ifstream fShaderFile; + // ensure ifstream objects can throw exceptions: + vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); + fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); + try + { + // open files + vShaderFile.open(vertexPath); + fShaderFile.open(fragmentPath); + std::stringstream vShaderStream, fShaderStream; + // read file's buffer contents into streams + vShaderStream << vShaderFile.rdbuf(); + fShaderStream << fShaderFile.rdbuf(); + // close file handlers + vShaderFile.close(); + fShaderFile.close(); + // convert stream into string + vertexCode = vShaderStream.str(); + fragmentCode = fShaderStream.str(); + } + catch (std::ifstream::failure& e) + { + std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ: " << e.what() << std::endl; + } + const char* vShaderCode = vertexCode.c_str(); + const char * fShaderCode = fragmentCode.c_str(); + // 2. compile shaders + unsigned int vertex, fragment; + // vertex shader + vertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex, 1, &vShaderCode, NULL); + glCompileShader(vertex); + checkCompileErrors(vertex, "VERTEX"); + // fragment Shader + fragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment, 1, &fShaderCode, NULL); + glCompileShader(fragment); + checkCompileErrors(fragment, "FRAGMENT"); + // shader Program + program = glCreateProgram(); + glAttachShader(program, vertex); + glAttachShader(program, fragment); + glLinkProgram(program); + checkCompileErrors(program, "PROGRAM"); + // delete the shaders as they're linked into our program now and no longer necessery + glDeleteShader(vertex); + glDeleteShader(fragment); + +} + +void Shader::bind(){ + GLCALL(glUseProgram(program)); +} + +void Shader::unbind(){ + GLCALL(glUseProgram(0)); +} + +void Shader::setuniform(const GLchar* uName, unsigned int value){ + GLCALL(glUniform1i(glGetUniformLocation(program, uName), value)); +} + + +void Shader::setuniform(const GLchar* uName, int value){ + GLCALL(glUniform1i(glGetUniformLocation(program, uName), value)); +} + +void Shader::setuniform(const GLchar* uName, GLfloat value){ + GLCALL(glUniform1i(glGetUniformLocation(program, uName), value)); +} + +void Shader::setuniform(const GLchar* uName, GLfloat x, GLfloat y){ + GLCALL(glUniform2f(glGetUniformLocation(program, uName), x, y)); +} + +void Shader::setuniform(const GLchar* uName, GLfloat x, GLfloat y, GLfloat z){ + GLCALL(glUniform3f(glGetUniformLocation(program, uName), x, y, z)); +} + +void Shader::setuniform(const GLchar* uName, glm::vec3 vector){ + GLCALL(glUniform3f(glGetUniformLocation(program, uName), vector.x, vector.y, vector.z)); +} + +void Shader::setuniform(const GLchar* uName, GLfloat x, GLfloat y, GLfloat z, GLfloat w){ + GLCALL(glUniform4f(glGetUniformLocation(program, uName), x, y, z, w)); +} + +void Shader::setuniform(const GLchar* name, const glm::mat4 &mat) +{ + glUniformMatrix4fv(glGetUniformLocation(program, name), 1, GL_FALSE, &mat[0][0]); +} + +void Shader::setuniform(const GLchar* uName, glm::vec2 vector){ + GLCALL(glUniform2f(glGetUniformLocation(program, uName), vector.x, vector.y)); +} + +void Shader::setuniform(const GLchar* uName, glm::vec4 vector){ + GLCALL(glUniform4f(glGetUniformLocation(program, uName), vector.x, vector.y, vector.z, vector.w)); +} +void Shader::setuniform(const GLchar* uName, GLuint tex2d, GLint unit){ // sample 2d + GLCALL(glActiveTexture(GL_TEXTURE0 + unit)); + GLCALL(glBindTexture(GL_TEXTURE_2D, tex2d)); + GLCALL(glUniform1i(glGetUniformLocation(program, uName), unit)); +} \ No newline at end of file diff --git a/src/GLShader.h b/src/GLShader.h new file mode 100644 index 0000000..d60e4d3 --- /dev/null +++ b/src/GLShader.h @@ -0,0 +1,65 @@ +#ifndef SHADER_H +#define SHADER_H + +#include +#include + +#include + + +#define GLCALL(func) func + +class Shader { + public: + Shader() : program(0) {} + Shader(const GLuint id) : program(id) {} + Shader(const char* vertexPath, const char* fragmentPath); + ~Shader() { glDeleteProgram(program); } + + void bind(); + void unbind(); + void setuniform(const GLchar* uName, unsigned int value); + void setuniform(const GLchar* uName, int value); + void setuniform(const GLchar* uName, GLfloat value); + void setuniform(const GLchar* uName, GLfloat x, GLfloat y); + void setuniform(const GLchar* uName, glm::vec2 vector); + void setuniform(const GLchar* uName, GLfloat x, GLfloat y, GLfloat z); + void setuniform(const GLchar* uName, glm::vec3 vector); + void setuniform(const GLchar* uName, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setuniform(const GLchar* uName, glm::vec4 vector); + void setuniform(const GLchar* uName, const glm::mat4& mtx); + void setuniform(const GLchar* uName, GLuint tex2d, GLint unit); // sample 2d + + GLuint getuniform(const char* name); + GLuint GetProgram() { return program; } + + private: + + void checkCompileErrors(GLuint shader, std::string type) + { + GLint success; + GLchar infoLog[1024]; + if(type != "PROGRAM") + { + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if(!success) + { + glGetShaderInfoLog(shader, 1024, NULL, infoLog); + std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; + } + } + else + { + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if(!success) + { + glGetProgramInfoLog(shader, 1024, NULL, infoLog); + std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; + } + } + } + + GLuint program; +}; + + #endif \ No newline at end of file diff --git a/src/GLVertex.h b/src/GLVertex.h new file mode 100644 index 0000000..025341a --- /dev/null +++ b/src/GLVertex.h @@ -0,0 +1,19 @@ +#ifndef GLVERTEX_H +#define GLVERTEX_H + +struct Vertex { + Vertex() = default; + Vertex(float x, float y, float z): Position(glm::vec3(x, y, z)){} + Vertex(glm::vec3 position): Position(position), Normal(0), TexCoords(0) { } + Vertex(glm::vec3 position, glm::vec3 normal): Position(position), Normal(normal), TexCoords(0) { } + Vertex(glm::vec3 position, glm::vec3 normal, glm::vec2 texcoord): Position(position), Normal(normal), TexCoords(texcoord) { } + + // position + glm::vec3 Position = glm::vec3(0.0f); + // normal + glm::vec3 Normal = glm::vec3(0.0f); + // coords + glm::vec2 TexCoords = glm::vec2(0.0f); +}; + +#endif \ No newline at end of file diff --git a/src/GLVertexArray.h b/src/GLVertexArray.h new file mode 100644 index 0000000..73adf81 --- /dev/null +++ b/src/GLVertexArray.h @@ -0,0 +1,70 @@ +#ifndef GLVERTEXARRAY_H +#define GLVERTEXARRAY_H + +#include "GLVertex.h" + +class VertexArray { +public: + ~VertexArray() = default; + + VertexArray(): VAO(0), EBO(0), VBO(0), numOfIndices(0), + numOfVertices(0) {} + + VertexArray(Vertex* vertices, GLsizei vSize, GLuint* indices=nullptr, GLsizei iSize=0): + numOfIndices(iSize), numOfVertices(vSize){ + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, numOfVertices * sizeof(Vertex), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, numOfIndices * sizeof(GLuint), indices, GL_STATIC_DRAW); + + // Positions + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0); + // Normals + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal)); + // Texcoords + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords)); + + glBindVertexArray(0); + numOfVertices /= sizeof(Vertex); + } + + void PushAttrib(GLuint index, GLint size, GLsizei stride, GLvoid* ptr) { + glEnableVertexAttribArray(index); + glVertexAttribPointer(index, size, GL_FLOAT, GL_FALSE, size, ptr); + } + + void Bind() { + GLCALL(glBindVertexArray(VAO)); + } + + void UnBind() { + GLCALL(glBindVertexArray(0)); + } + + void DrawElements(GLenum mode = GL_TRIANGLE_STRIP) { + GLCALL(glBindVertexArray(VAO)); + GLCALL(glDrawElements(mode, numOfIndices, GL_UNSIGNED_INT, 0)); + GLCALL(glBindVertexArray(0)); + } + + void DrawArrays(GLenum mode = GL_TRIANGLES) { + GLCALL(glBindVertexArray(VAO)); + GLCALL(glDrawArrays(mode, 0, numOfVertices)); + GLCALL(glBindVertexArray(0)); + } + +private: + GLuint VAO, VBO, EBO; + GLsizei numOfIndices, numOfVertices; +}; + +#endif \ No newline at end of file diff --git a/LevelGenerator.cpp b/src/LevelGenerator.cpp similarity index 64% rename from LevelGenerator.cpp rename to src/LevelGenerator.cpp index c8084b8..9c38092 100644 --- a/LevelGenerator.cpp +++ b/src/LevelGenerator.cpp @@ -1,23 +1,24 @@ +/*#include "MapBlock.h" +#include "FastNoiseLite.h" #include "LevelGenerator.h" -FastNoiseLite perlin, os, cellular; +//FastNoiseLite perlin, os, cellular; int seed = 138; -perlin.SetSeed(seed); -perlin.SetNoiseType(FastNoiseLite::NoiseType_Perlin); -perlin.SetFrequency(.01F); - -os.SetSeed(seed); -os.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2); -os.SetFrequency(.01F); - -cellular.SetSeed(seed); -cellular.SetNoiseType(FastNoiseLite::NoiseType_Cellular); -cellular.SetFrequency(.1F); LevelGenerator::LevelGenerator() { + perlin.SetSeed(seed); + perlin.SetNoiseType(FastNoiseLite::NoiseType_Perlin); + perlin.SetFrequency(.01F); + os.SetSeed(seed); + os.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2); + os.SetFrequency(.01F); + + cellular.SetSeed(seed); + cellular.SetNoiseType(FastNoiseLite::NoiseType_Cellular); + cellular.SetFrequency(.1F); } void LevelGenerator::generateBlock() @@ -41,3 +42,4 @@ void LevelGenerator::generateBlock() } } } +*/ \ No newline at end of file diff --git a/include/LevelGenerator.h b/src/LevelGenerator.h similarity index 90% rename from include/LevelGenerator.h rename to src/LevelGenerator.h index 8f64374..bbea41d 100644 --- a/include/LevelGenerator.h +++ b/src/LevelGenerator.h @@ -1,4 +1,4 @@ -#ifndef LEVELGENERATOR +/*#ifndef LEVELGENERATOR #define LEVELGENERATOR #include "FastNoiseLite.h" @@ -16,3 +16,4 @@ class LevelGenerator }; #endif +*/ \ No newline at end of file diff --git a/include/Logger.h b/src/Logger.h similarity index 100% rename from include/Logger.h rename to src/Logger.h diff --git a/MapBlock.cpp b/src/MapBlock.cpp similarity index 97% rename from MapBlock.cpp rename to src/MapBlock.cpp index d264165..d3594e9 100644 --- a/MapBlock.cpp +++ b/src/MapBlock.cpp @@ -1,4 +1,4 @@ -#include "MapBlock.h" +/*#include "MapBlock.h" @@ -65,3 +65,4 @@ Position2D BlockUtilities::getBlockFromNodeCoordinates(int x, int z) pos2d.z = floor(z / 16); return pos2d; } +*/ \ No newline at end of file diff --git a/include/MapBlock.h b/src/MapBlock.h similarity index 96% rename from include/MapBlock.h rename to src/MapBlock.h index 4ac6a78..a3b3851 100644 --- a/include/MapBlock.h +++ b/src/MapBlock.h @@ -1,4 +1,4 @@ -#ifndef MAPBLOCK +/*#ifndef MAPBLOCK #define MAPBLOCK #include "Base.h" #include @@ -31,3 +31,4 @@ class BlockUtilities }; #endif +*/ \ No newline at end of file diff --git a/src/NodeRenderer.h b/src/NodeRenderer.h new file mode 100644 index 0000000..7f52e24 --- /dev/null +++ b/src/NodeRenderer.h @@ -0,0 +1,34 @@ +#ifndef NODERENDERER_H +#define NODERENDERER_H +#include "Base.h" +#include "MapBlock.h" + +class NodeRenderer +{ + public: + NodeRenderer() + { + + } + + virtual ~NodeRenderer() + { + + } + + int renderNode(int x, int y, int z) + { + //Position2D block = BlockUtilities::getBlockFromNodeCoordinates(x, z); // The block the node at (x, y, z) is in + + // render block + + return 1; + } + + protected: + + private: + +}; + +#endif diff --git a/include/Nodes.h b/src/Nodes.h similarity index 100% rename from include/Nodes.h rename to src/Nodes.h diff --git a/include/TextureHandler.h b/src/TextureHandler.h similarity index 89% rename from include/TextureHandler.h rename to src/TextureHandler.h index 01ba231..c108126 100644 --- a/include/TextureHandler.h +++ b/src/TextureHandler.h @@ -4,8 +4,8 @@ #include "Base.h" -#ifndef TEXTURE_HANDLER -#define TEXTURE_HANDLER +//#ifndef TEXTURE_HANDLER +//#define TEXTURE_HANDLER class TextureHandler { private: @@ -24,7 +24,7 @@ class TextureHandler } void loadAllTextures() - { + {/* int textureIndex = 0; imageData = loadTexture("data/img/texturemap.png"); @@ -39,7 +39,6 @@ class TextureHandler glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData); - imageData1 = loadTexture("data/img/oak.png"); glGenTextures(1, &textures1); glBindTexture(GL_TEXTURE_2D, textures1); @@ -48,7 +47,7 @@ class TextureHandler glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData1);*/ } void getTextureForNode(int x, int y, int z) @@ -56,16 +55,16 @@ class TextureHandler //Position2D block = BlockUtilities::getBlockFromNodeCoordinates(x, z); if(blockManager.getNodeAt(x, y, z) == 1) { - glBindTexture(GL_TEXTURE_2D, textures); + //glBindTexture(GL_TEXTURE_2D, textures); } else if(blockManager.getNodeAt(x, y, z) == 2) { - glBindTexture(GL_TEXTURE_2D, textures1); + //glBindTexture(GL_TEXTURE_2D, textures1); } } }; -#endif +//#endif diff --git a/include/Utilities.h b/src/Utilities.h similarity index 100% rename from include/Utilities.h rename to src/Utilities.h diff --git a/src/XtreemNodes.h b/src/XtreemNodes.h new file mode 100644 index 0000000..e69de29 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e2c4ad8 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,216 @@ +#include +#include +#include "stb_image.h" + +#include +#include +#include + +#include "GLShader.h" +#include "GLMesh.h" +#include "Camera.h" + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); + +// settings +const unsigned int SCR_WIDTH = 800; +const unsigned int SCR_HEIGHT = 600; + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = SCR_WIDTH / 2.0f; +float lastY = SCR_HEIGHT / 2.0f; +bool firstMouse = true; + +// timing +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + // glew + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) + { + std::cout << "Failed to initialize GLEW" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + Shader sh = Shader("data/shader/MeshV.shader", "data/shader/MeshF.shader"); + + static Vertex cubeVertices[] = { + // positions // normals // texture coords + Vertex(glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec2(0.0f, 0.0f)), + Vertex(glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, 0.5f, -0.5f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec2(0.0f, 0.0f)), + + Vertex(glm::vec3(-0.5f, -0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(0.0f, 0.0f)), + Vertex(glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(0.0f, 0.0f)), + + Vertex(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(-0.5f, 0.5f, -0.5f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, 0.5f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 0.0f)), + Vertex(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + + Vertex(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 0.0f)), + Vertex(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + + Vertex(glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, 0.5f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec2(0.0f, 0.0f)), + Vertex(glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + + Vertex(glm::vec3(-0.5f, 0.5f, -0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + Vertex(glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 1.0f)), + Vertex(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), + Vertex(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 0.0f)), + Vertex(glm::vec3(-0.5f, 0.5f, -0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), + }; + + GLMesh mesh = GLMesh(cubeVertices, sizeof(cubeVertices)); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = static_cast(glfwGetTime()); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + mesh.DrawArrays(sh); + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) +{ + float xpos = static_cast(xposIn); + float ypos = static_cast(yposIn); + + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(static_cast(yoffset)); +} \ No newline at end of file diff --git a/src/stb_image.cpp b/src/stb_image.cpp new file mode 100644 index 0000000..badb3ef --- /dev/null +++ b/src/stb_image.cpp @@ -0,0 +1,2 @@ +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" \ No newline at end of file diff --git a/include/stb_image.h b/src/stb_image.h similarity index 100% rename from include/stb_image.h rename to src/stb_image.h