Szerkesztő:Kisteklak/próbalap

HEADER




#pragma once

// C++ includes

#include <memory>

// GLEW

#include <GL/glew.h>

// SDL

#include <SDL.h>

#include <SDL_opengl.h>

// GLM

#include <glm/glm.hpp>

#include <glm/gtc/matrix_transform.hpp>

#include <glm/gtx/transform2.hpp>

#include "includes/gCamera.h"

#include "includes/ProgramObject.h"

#include "includes/BufferObject.h"

#include "includes/VertexArrayObject.h"

#include "includes/TextureObject.h"

// mesh

#include "includes/ObjParser_OGL3.h"

class CMyApp

{

public:

   CMyApp();

   ~CMyApp();

   bool Init();

   void Clean();

   void Update();

   void Render();

   void KeyboardDown(SDL_KeyboardEvent&);

   void KeyboardUp(SDL_KeyboardEvent&);

   void MouseMove(SDL_MouseMotionEvent&);

   void MouseDown(SDL_MouseButtonEvent&);

   void MouseUp(SDL_MouseButtonEvent&);

   void MouseWheel(SDL_MouseWheelEvent&);

   void Resize(int, int);

protected:

   // shaderekhez szükséges változók

   ProgramObject        m_programGround;            // mesh shader

   ProgramObject        m_programSkybox;    // skybox shader

   ProgramObject        m_program;

   VertexArrayObject    m_CubeVao;            // VAO

   IndexBuffer            m_CubeIndices;        // index buffer

   ArrayBuffer            m_CubeVertexBuffer;    // VBO

   VertexArrayObject    m_SkyboxVao;

   IndexBuffer            m_SkyboxIndices;    

   ArrayBuffer            m_SkyboxPos;        

   VertexArrayObject    m_GroundVao;            // VAO

   IndexBuffer            m_GroundIndices;        // index buffer

   ArrayBuffer            m_GroundVertexBuffer;    // VBO

   VertexArrayObject    m_ForestVao;            // VAO

   IndexBuffer            m_ForestIndices;        // index buffer

   ArrayBuffer            m_ForestVertexBuffer;    // VBO

   VertexArrayObject    m_SphereVao;            // VAO

   IndexBuffer            m_SphereIndices;        // index buffer

   ArrayBuffer            m_SphereVertexBuffer;    // VBO

   gCamera                m_camera;

   Texture2D            m_woodTexture;

   Texture2D            m_suzanneTexture;

   Texture2D            m_forestTexture;

   Texture2D            m_leavesTexture;

   Texture2D            m_barkTexture;

   TextureCubeMap        m_skyboxTexture;

   int gridRes = 18;

   float trees[18][18];

   bool disableLine[18];

   float initialHeight = 0.05f;

   float cutHeight = 1.0f;

   float growTime = 10.0f;

   int cutTreeCount = 0;

   int lastTickCount = 0;

   glm::vec3 light_dir = { -1.0f, -1.0f, -1.0f };

   glm::vec3 winter_color = { 0.3, 0.6, 0.7 };

   glm::vec3 summer_color = { 0.7, 0.7, 0.6 };

   float yearTime = 0.0f;

   float yearLength = 20.0f;

   struct Vertex

   {

       glm::vec3 p;

       glm::vec3 n;

       glm::vec2 t;

   };

   // mesh adatok

   std::unique_ptr<Mesh> m_mesh;

   std::unique_ptr<Mesh> m_cylinder;

   void DrawTree(glm::mat4 viewProj, glm::mat4 world);

   // a jobb olvashatóság kedvéért

   void InitShaders();

   void InitCube();

   void InitSkyBox();

   void InitGround();

   void InitForest();

   void InitSphere();

   glm::vec3 GetSpherePos(float u, float v);

   glm::vec3 GetNorm(float u, float v);

   glm::vec2 GetTex(float u, float v);

   float height(glm::vec2 pos);

   void SpawnTrees();

   void DrawGui();

};

-------

MYAPP.CPP



#include "MyApp.h"

#include <math.h>

#include <vector>

#include <cstdlib>

#include <time.h>

#include <array>

#include <list>

#include <tuple>

#include <imgui/imgui.h>

#include "includes/GLUtils.hpp"

CMyApp::CMyApp(void)

{

   m_camera.SetView(glm::vec3(5, 5, 5), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));

   m_mesh = nullptr;

}

CMyApp::~CMyApp(void)

{

}

void CMyApp::InitCube()

{

   //struct Vertex{ glm::vec3 position; glm::vec3 normals; glm::vec2 texture; };

   std::vector<Vertex>vertices;

   

   //front                                    

   vertices.push_back({ glm::vec3(-0.5, -0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(+0.5, -0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(1, 1) });

   //back

   vertices.push_back({ glm::vec3(+0.5, -0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(-0.5, -0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(1, 1) });

   //right                                    

   vertices.push_back({ glm::vec3(+0.5, -0.5, +0.5), glm::vec3(1, 0, 0), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(+0.5, -0.5, -0.5), glm::vec3(1, 0, 0), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, +0.5), glm::vec3(1, 0, 0), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, -0.5), glm::vec3(1, 0, 0), glm::vec2(1, 1) });

   //left                                    

   vertices.push_back({ glm::vec3(-0.5, -0.5, -0.5), glm::vec3(-1, 0, 0), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(-0.5, -0.5, +0.5), glm::vec3(-1, 0, 0), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, -0.5), glm::vec3(-1, 0, 0), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, +0.5), glm::vec3(-1, 0, 0), glm::vec2(1, 1) });

   //top                                    

   vertices.push_back({ glm::vec3(-0.5, +0.5, +0.5), glm::vec3(0, 1, 0), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, +0.5), glm::vec3(0, 1, 0), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, -0.5), glm::vec3(0, 1, 0), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, -0.5), glm::vec3(0, 1, 0), glm::vec2(1, 1) });

   //bottom                                

   vertices.push_back({ glm::vec3(-0.5, -0.5, -0.5), glm::vec3(0, -1, 0), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(+0.5, -0.5, -0.5), glm::vec3(0, -1, 0), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(-0.5, -0.5, +0.5), glm::vec3(0, -1, 0), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(+0.5, -0.5, +0.5), glm::vec3(0, -1, 0), glm::vec2(1, 1) });

   std::vector<int> indices(36);

   int index = 0;

   for (int i = 0; i < 6 * 4; i += 4)

   {

       indices[index + 0] = i + 0;

       indices[index + 1] = i + 1;

       indices[index + 2] = i + 2;

       indices[index + 3] = i + 1;

       indices[index + 4] = i + 3;

       indices[index + 5] = i + 2;

       index += 6;

   }

   //

   // geometria definiálása (std::vector<...>) és GPU pufferekbe való feltöltése BufferData-val

   //

   // vertexek pozíciói:

   /*

   Az m_CubeVertexBuffer konstruktora már létrehozott egy GPU puffer azonosítót és a most következő BufferData hívás ezt

   1. bind-olni fogja GL_ARRAY_BUFFER target-re (hiszen m_CubeVertexBuffer típusa ArrayBuffer) és

   2. glBufferData segítségével áttölti a GPU-ra az argumentumban adott tároló értékeit

   */

   m_CubeVertexBuffer.BufferData(vertices);

   // és a primitíveket alkotó csúcspontok indexei (az előző tömbökből) - triangle list-el való kirajzolásra felkészülve

   m_CubeIndices.BufferData(indices);

   // geometria VAO-ban való regisztrálása

   m_CubeVao.Init(

       {

           // 0-ás attribútum "lényegében" glm::vec3-ak sorozata és az adatok az m_CubeVertexBuffer GPU pufferben vannak

           { CreateAttribute<        0,                        // attribútum: 0

                                   glm::vec3,                // CPU oldali adattípus amit a 0-ás attribútum meghatározására használtunk <- az eljárás a glm::vec3-ból kikövetkezteti, hogy 3 darab float-ból áll a 0-ás attribútum

                                   0,                        // offset: az attribútum tároló elejétől vett offset-je, byte-ban

                                   sizeof(Vertex)            // stride: a következő csúcspont ezen attribútuma hány byte-ra van az aktuálistól

                               >, m_CubeVertexBuffer },

           { CreateAttribute<1, glm::vec3, (sizeof(glm::vec3)), sizeof(Vertex)>, m_CubeVertexBuffer },

           { CreateAttribute<2, glm::vec2, (2 * sizeof(glm::vec3)), sizeof(Vertex)>, m_CubeVertexBuffer },

       },

       m_CubeIndices

   );

}

void CMyApp::InitSkyBox()

{

   m_SkyboxPos.BufferData(

       std::vector<glm::vec3>{

       // hátsó lap

       glm::vec3(-1, -1, -1),

       glm::vec3(1, -1, -1),

       glm::vec3(1, 1, -1),

       glm::vec3(-1, 1, -1),

       // elülső lap

       glm::vec3(-1, -1, 1),

       glm::vec3(1, -1, 1),

       glm::vec3(1, 1, 1),

       glm::vec3(-1, 1, 1),

   }

   );

   // és a primitíveket alkotó csúcspontok indexei (az előző tömbökből) - triangle list-el való kirajzolásra felkészülve

   m_SkyboxIndices.BufferData(

       std::vector<int>{

           // hátsó lap

           0, 1, 2,

           2, 3, 0,

           // elülső lap

           4, 6, 5,

           6, 4, 7,

           // bal

           0, 3, 4,

           4, 3, 7,

           // jobb

           1, 5, 2,

           5, 6, 2,

           // alsó

           1, 0, 4,

           1, 4, 5,

           // felső

           3, 2, 6,

           3, 6, 7,

   }

   );

   // geometria VAO-ban való regisztrálása

   m_SkyboxVao.Init(

       {

           { CreateAttribute<0, glm::vec3, 0, sizeof(glm::vec3)>, m_SkyboxPos },

       }, m_SkyboxIndices

   );

   // skybox texture

   glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

   m_skyboxTexture.AttachFromFile("assets/xpos.png", false, GL_TEXTURE_CUBE_MAP_POSITIVE_X);

   m_skyboxTexture.AttachFromFile("assets/xneg.png", false, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);

   m_skyboxTexture.AttachFromFile("assets/ypos.png", false, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);

   m_skyboxTexture.AttachFromFile("assets/yneg.png", false, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);

   m_skyboxTexture.AttachFromFile("assets/zpos.png", false, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);

   m_skyboxTexture.AttachFromFile("assets/zneg.png", true, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);

   // a GL_TEXTURE_MAG_FILTER-t és a GL_TEXTURE_MIN_FILTER-t beállítja az AttachFromFile

   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

   glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

}

void CMyApp::InitGround()

{

   std::vector<Vertex>vertices;

   for (int x = 0; x < 20; ++x) {

       for (int z = 0; z < 20; ++z) {

           float u = x / (float)19;

           float v = z / (float)19;

           vertices.push_back(

               {

                   glm::vec3(u,0,v),

                   glm::vec3(0,1,0),

                   glm::vec2(u,v)

               }

           );

       }

   }

   std::vector<int> indices;

   for (int x = 0; x <= 18; ++x) {

       for (int z = 0; z <= 18; ++z) {

           indices.push_back(x * 20 + z);

           indices.push_back((x+1) * 20 + z+1);

           indices.push_back((x+1) * 20 + z);

           indices.push_back((x + 1) * 20 + z + 1);

           indices.push_back(x * 20 + z);

           indices.push_back(x * 20 + z+1);

       }

   }

   m_GroundVertexBuffer.BufferData(vertices);

   m_GroundIndices.BufferData(indices);

   m_GroundVao.Init(

       {

           // 0-ás attribútum "lényegében" glm::vec3-ak sorozata és az adatok az m_CubeVertexBuffer GPU pufferben vannak

           { CreateAttribute<        0,                        // attribútum: 0

                                   glm::vec3,                // CPU oldali adattípus amit a 0-ás attribútum meghatározására használtunk <- az eljárás a glm::vec3-ból kikövetkezteti, hogy 3 darab float-ból áll a 0-ás attribútum

                                   0,                        // offset: az attribútum tároló elejétől vett offset-je, byte-ban

                                   sizeof(Vertex)            // stride: a következő csúcspont ezen attribútuma hány byte-ra van az aktuálistól

                               >, m_GroundVertexBuffer },

           { CreateAttribute<1, glm::vec3, (sizeof(glm::vec3)), sizeof(Vertex)>, m_GroundVertexBuffer },

           { CreateAttribute<2, glm::vec2, (2 * sizeof(glm::vec3)), sizeof(Vertex)>, m_GroundVertexBuffer },

       },

       m_GroundIndices

   );

}

void CMyApp::InitForest()

{

   std::vector<Vertex>vertices;

   //front                                    

   vertices.push_back({ glm::vec3(-0.5, -0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(+0.5, -0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, +0.5), glm::vec3(0, 0, 1), glm::vec2(1, 1) });

   //back

   vertices.push_back({ glm::vec3(+0.5, -0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(-0.5, -0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, -0.5), glm::vec3(0, 0, -1), glm::vec2(1, 1) });

   //right                                    

   vertices.push_back({ glm::vec3(+0.5, -0.5, +0.5), glm::vec3(1, 0, 0), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(+0.5, -0.5, -0.5), glm::vec3(1, 0, 0), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, +0.5), glm::vec3(1, 0, 0), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(+0.5, +0.5, -0.5), glm::vec3(1, 0, 0), glm::vec2(1, 1) });

   //left                                    

   vertices.push_back({ glm::vec3(-0.5, -0.5, -0.5), glm::vec3(-1, 0, 0), glm::vec2(0, 0) });

   vertices.push_back({ glm::vec3(-0.5, -0.5, +0.5), glm::vec3(-1, 0, 0), glm::vec2(1, 0) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, -0.5), glm::vec3(-1, 0, 0), glm::vec2(0, 1) });

   vertices.push_back({ glm::vec3(-0.5, +0.5, +0.5), glm::vec3(-1, 0, 0), glm::vec2(1, 1) });

   std::vector<int> indices(24);

   int index = 0;

   for (int i = 0; i < 4 * 4; i += 4)

   {

       indices[index + 0] = i + 0;

       indices[index + 1] = i + 2;

       indices[index + 2] = i + 1;

       indices[index + 3] = i + 1;

       indices[index + 4] = i + 2;

       indices[index + 5] = i + 3;

       index += 6;

   }

   m_ForestVertexBuffer.BufferData(vertices);

   // és a primitíveket alkotó csúcspontok indexei (az előző tömbökből) - triangle list-el való kirajzolásra felkészülve

   m_ForestIndices.BufferData(indices);

   // geometria VAO-ban való regisztrálása

   m_ForestVao.Init(

       {

           // 0-ás attribútum "lényegében" glm::vec3-ak sorozata és az adatok az m_CubeVertexBuffer GPU pufferben vannak

           { CreateAttribute<        0,                        // attribútum: 0

                                   glm::vec3,                // CPU oldali adattípus amit a 0-ás attribútum meghatározására használtunk <- az eljárás a glm::vec3-ból kikövetkezteti, hogy 3 darab float-ból áll a 0-ás attribútum

                                   0,                        // offset: az attribútum tároló elejétől vett offset-je, byte-ban

                                   sizeof(Vertex)            // stride: a következő csúcspont ezen attribútuma hány byte-ra van az aktuálistól

                               >, m_ForestVertexBuffer },

           { CreateAttribute<1, glm::vec3, (sizeof(glm::vec3)), sizeof(Vertex)>, m_ForestVertexBuffer },

           { CreateAttribute<2, glm::vec2, (2 * sizeof(glm::vec3)), sizeof(Vertex)>, m_ForestVertexBuffer },

       },

       m_ForestIndices

   );

}

glm::vec3 CMyApp::GetSpherePos(float u, float v)

{

   // origó középpontú, r sugarú gömb parametrikus alakja: http://hu.wikipedia.org/wiki/G%C3%B6mb#Egyenletek

   // figyeljünk:    matematikában sokszor a Z tengely mutat felfelé, de nálunk az Y, tehát a legtöbb képlethez képest nálunk

   //                az Y és Z koordináták felcserélve szerepelnek

   u *= float(2 * M_PI);

   v *= float(M_PI);

   float r = 2;

   return glm::vec3(r * sin(v) * cos(u),

       r * cos(v),

       r * sin(v) * sin(u));

}

//

// egy parametrikus felület (u,v) paraméterértékekhez tartozó normálvektorának

// kiszámítását végző függvény

//

glm::vec3 CMyApp::GetNorm(float u, float v)

{

   // Képlettel

   u *= float(2 * M_PI);

   v *= float(M_PI);

   return glm::vec3(sin(v) * cos(u), cos(v), sin(v) * sin(u));

   // Numerikusan (nem kell ismerni a képletet, elég a pozícióét)

   /*

   glm::vec3 du = GetPos(u+0.01, v)-GetPos(u-0.01, v);

   glm::vec3 dv = GetPos(u, v+0.01)-GetPos(u, v-0.01);

   return glm::normalize(glm::cross(du, dv));*/

}

glm::vec2 CMyApp::GetTex(float u, float v)

{

   return glm::vec2(1 - u, 1 - v);

}

void CMyApp::InitSphere()

{

   int N = 20;

   int M = 20;

   // NxM darab négyszöggel közelítjük a parametrikus felületünket => (N+1)x(M+1) pontban kell kiértékelni

   std::vector<Vertex> vertices;

   vertices.resize((N + 1) * (M + 1));

   for (int i = 0; i <= N; ++i)

       for (int j = 0; j <= M; ++j)

       {

           float u = i / (float)N;

           float v = j / (float)M;

           Vertex tmp;

           tmp.p = GetSpherePos(u, v);

           tmp.n = GetNorm(u, v);

           tmp.t = GetTex(u, v);

           vertices[i + j * (N + 1)] = tmp;

       }

   // indexpuffer adatai: NxM négyszög = 2xNxM háromszög = háromszöglista esetén 3x2xNxM index

   //GLushort indices[3 * 2 * (N) * (M)];

   std::vector<int> indices;

   for (int i = 0; i < N; ++i)

       for (int j = 0; j < M; ++j)

       {

           // minden négyszögre csináljunk kettő háromszöget, amelyek a következő

           // (i,j) indexeknél született (u_i, v_i) paraméterértékekhez tartozó

           // pontokat kötik össze:

           //

           //    (i,j+1)

           //          o-----o(i+1,j+1)

           //          |\    |            a = p(u_i, v_i)

           //          | \   |            b = p(u_{i+1}, v_i)

           //          |  \  |            c = p(u_i, v_{i+1})

           //          |   \ |            d = p(u_{i+1}, v_{i+1})

           //          |       \|

           //    (i,j) o-----o(i+1, j)

           //

           // - az (i,j)-hez tartózó 1D-s index a VBO-ban: i+j*(N+1)

           // - az (i,j)-hez tartózó 1D-s index az IB-ben: i*6+j*6*(N+1)

           //        (mert minden négyszöghöz 2db háromszög = 6 index tartozik)

           //

           indices.push_back((i)+(j) * (N + 1));

           indices.push_back((i + 1) + (j) * (N + 1));

           indices.push_back((i)+(j + 1) * (N + 1));

           indices.push_back((i + 1) + (j) * (N + 1));

           indices.push_back((i + 1) + (j + 1) * (N + 1));

           indices.push_back((i)+(j + 1) * (N + 1));

       }

   m_SphereVertexBuffer.BufferData(vertices);

   m_SphereIndices.BufferData(indices);

   m_SphereVao.Init(

       {

           // 0-ás attribútum "lényegében" glm::vec3-ak sorozata és az adatok az m_CubeVertexBuffer GPU pufferben vannak

           { CreateAttribute<        0,                        // attribútum: 0

                                   glm::vec3,                // CPU oldali adattípus amit a 0-ás attribútum meghatározására használtunk <- az eljárás a glm::vec3-ból kikövetkezteti, hogy 3 darab float-ból áll a 0-ás attribútum

                                   0,                        // offset: az attribútum tároló elejétől vett offset-je, byte-ban

                                   sizeof(Vertex)            // stride: a következő csúcspont ezen attribútuma hány byte-ra van az aktuálistól

                               >, m_SphereVertexBuffer },

           { CreateAttribute<1, glm::vec3, (sizeof(glm::vec3)), sizeof(Vertex)>, m_SphereVertexBuffer },

           { CreateAttribute<2, glm::vec2, (2 * sizeof(glm::vec3)), sizeof(Vertex)>, m_SphereVertexBuffer },

       },

       m_SphereIndices

   );

}

void CMyApp::InitShaders()

{

   // a shadereket tároló program létrehozása az OpenGL-hez hasonló módon:

   m_programGround.AttachShaders({

       { GL_VERTEX_SHADER, "myVert.vert"},

       { GL_FRAGMENT_SHADER, "frag.frag"}

   });

   // attributomok osszerendelese a VAO es shader kozt

   m_programGround.BindAttribLocations({

       { 0, "vs_in_pos" },                // VAO 0-as csatorna menjen a vs_in_pos-ba

       { 1, "vs_in_norm" },            // VAO 1-es csatorna menjen a vs_in_norm-ba

       { 2, "vs_in_tex" },                // VAO 2-es csatorna menjen a vs_in_tex-be

   });

   m_programGround.LinkProgram();

   // shader program rövid létrehozása, egyetlen függvényhívással a fenti három:

   m_programSkybox.Init(

       {

           { GL_VERTEX_SHADER, "skybox.vert" },

           { GL_FRAGMENT_SHADER, "skybox.frag" }

       },

       {

           { 0, "vs_in_pos" },                // VAO 0-as csatorna menjen a vs_in_pos-ba

       }

   );

   m_program.Init(

       {

           { GL_VERTEX_SHADER, "vert.vert" },

           { GL_FRAGMENT_SHADER, "frag.frag" }

       },

       {

           { 0, "vs_in_pos" },                // VAO 0-as csatorna menjen a vs_in_pos-ba

           { 1, "vs_in_norm" },            // VAO 1-es csatorna menjen a vs_in_norm-ba

           { 2, "vs_in_tex" },

       }

       );

}

bool CMyApp::Init()

{

   // törlési szín legyen kékes

   glClearColor(0.125f, 0.25f, 0.5f, 1.0f);

   glEnable(GL_CULL_FACE); // kapcsoljuk be a hatrafele nezo lapok eldobasat

   glEnable(GL_DEPTH_TEST); // mélységi teszt bekapcsolása (takarás)

   InitShaders();

   InitCube();

   InitSkyBox();

   InitGround();

   InitForest();

   InitSphere();

   // egyéb textúrák betöltése

   m_woodTexture.FromFile("assets/wood.jpg");

   m_suzanneTexture.FromFile("assets/grass.jpg");

   m_forestTexture.FromFile("assets/forest.jpg");

   m_leavesTexture.FromFile("assets/leaves.jpg");

   m_barkTexture.FromFile("assets/bark.jpg");

   // mesh betöltése

   m_mesh = std::unique_ptr<Mesh>(ObjParser::parse("assets/Suzanne.obj"));

   m_mesh->initBuffers();

   m_cylinder = std::unique_ptr<Mesh>(ObjParser::parse("assets/henger.obj"));

   m_cylinder->initBuffers();

   

   // kamera

   m_camera.SetProj(glm::radians(60.0f), 640.0f / 480.0f, 0.01f, 1000.0f);

   srand(time(nullptr));

   int count = (rand() % 51) + 50;

   for (int i = 0; i < gridRes; ++i) {

       for (int j = 0; j < gridRes; ++j) {

           trees[i][j] = -1;

       }

   }

   while (count > 0) {

       int x = rand() % (gridRes + 1);

       int y = rand() % (gridRes + 1);

       if (trees[x][y] < 0) {

           trees[x][y] = initialHeight;

           count--;

       }

   }

   for (int j = 0; j < gridRes; ++j) {

       disableLine[j] = false;

   }

   return true;

}

void CMyApp::Clean()

{

}

void CMyApp::Update()

{

   static Uint32 last_time = SDL_GetTicks();

   float delta_time = (SDL_GetTicks() - last_time) / 1000.0f;

   m_camera.Update(delta_time);

   last_time = SDL_GetTicks();

   int tickCount = SDL_GetTicks();

   if (tickCount / 2000 > lastTickCount / 2000) {

       SpawnTrees();

   }

   float growSpeed = (cutHeight - initialHeight) / growTime;

   for (int i = 0; i < gridRes; ++i) {

       for (int j = 0; j < gridRes; ++j) {

           if (trees[i][j] >= 0) {

               trees[i][j] += growSpeed * delta_time;

           }

           if (trees[i][j] > cutHeight) {

               trees[i][j] = -1;

               cutTreeCount++;

           }

       }

   }

   yearTime += delta_time;

   if (yearTime > yearLength) {

       yearTime -= yearLength;

   }

   lastTickCount = SDL_GetTicks();

}

void CMyApp::DrawTree(glm::mat4 viewProj, glm::mat4 world)

{

   glm::mat4 worldCylinder = world * glm::translate(glm::vec3(0, 2, 0));

   m_program.Use();

   m_program.SetTexture("texImage", 0, m_barkTexture);

   m_program.SetUniform("MVP", viewProj * worldCylinder);

   m_program.SetUniform("world", worldCylinder);

   m_program.SetUniform("worldIT", glm::inverse(glm::transpose(worldCylinder)));

   m_program.SetUniform("eye_pos", m_camera.GetEye());

   m_program.SetUniform("light_dir", light_dir);

   m_program.SetUniform("year_progress", yearTime / yearLength);

   m_program.SetUniform("winter_color", winter_color);

   m_program.SetUniform("summer_color", summer_color);

   m_cylinder->draw();

   glm::mat4 worldSphere = world * glm::translate(glm::vec3(0, 5, 0)) * glm::scale(glm::vec3(1, 1, 1));

   m_program.SetTexture("texImage", 0, m_leavesTexture);

   m_program.SetUniform("MVP", viewProj * worldSphere);

   m_program.SetUniform("world", worldSphere);

   m_program.SetUniform("worldIT", glm::inverse(glm::transpose(worldSphere)));

   m_SphereVao.Bind();

   glDrawElements(GL_TRIANGLES, 2400, GL_UNSIGNED_INT, nullptr);

}

void CMyApp::Render()

{

   // töröljük a frampuffert (GL_COLOR_BUFFER_BIT) és a mélységi Z puffert (GL_DEPTH_BUFFER_BIT)

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glm::mat4 viewProj = m_camera.GetViewProj();

   //glPolygonMode(GL_FRONT, GL_LINE);

   //Ground

   glm::mat4 groundWorld = glm::translate(glm::vec3(-10,0,-10)) * glm::scale(glm::vec3(20,1,20));

   m_programGround.Use();

   m_programGround.SetTexture("texImage", 0, m_suzanneTexture);

   m_programGround.SetUniform("MVP", viewProj * groundWorld);

   m_programGround.SetUniform("world", groundWorld);

   m_programGround.SetUniform("worldIT", glm::inverse(glm::transpose(groundWorld)));

   m_programGround.SetUniform("eye_pos", m_camera.GetEye());

   m_programGround.SetUniform("light_dir", light_dir);

   m_programGround.SetUniform("year_progress", yearTime / yearLength);

   m_programGround.SetUniform("winter_color", winter_color);

   m_programGround.SetUniform("summer_color", summer_color);

   //m_mesh->draw();

   m_GroundVao.Bind();

   glDrawElements(GL_TRIANGLES, 19 * 19 * 2 * 3, GL_UNSIGNED_INT, nullptr);

   glm::mat4 forestWorld = glm::translate(glm::vec3(0, 2.5-0.2, 0)) * glm::scale(glm::vec3(20, 5, 20));

   m_program.Use();

   m_program.SetTexture("texImage", 0, m_forestTexture);

   m_program.SetUniform("MVP", viewProj * forestWorld);

   m_program.SetUniform("world", forestWorld);

   m_program.SetUniform("worldIT", glm::inverse(glm::transpose(forestWorld)));

   m_program.SetUniform("eye_pos", m_camera.GetEye());

   m_program.SetUniform("light_dir", light_dir);

   m_program.SetUniform("year_progress", yearTime / yearLength);

   m_program.SetUniform("winter_color", winter_color);

   m_program.SetUniform("summer_color", summer_color);

   m_ForestVao.Bind();

   //m_SphereVao.Bind();

   glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_INT, nullptr);

   for (int x = 0; x < gridRes; x++) {

       for (int y = 0; y < gridRes; y++) {

           if (trees[x][y] >= 0) {

               glm::vec3 pos(x + 1, height(glm::vec2((x + 1) / 20.0f, (y + 1) / 20.0f)), y + 1);

               DrawTree(viewProj, glm::translate(pos + glm::vec3(-10,0,-10)) * glm::scale(glm::vec3(1,1,1)*trees[x][y]));

           }

       }

   }

   // skybox

   // mentsük el az előző Z-test eredményt, azaz azt a relációt, ami alapján update-eljük a pixelt.

   GLint prevDepthFnc;

   glGetIntegerv(GL_DEPTH_FUNC, &prevDepthFnc);

   // most kisebb-egyenlőt használjunk, mert mindent kitolunk a távoli vágósíkokra

   glDepthFunc(GL_LEQUAL);

   m_SkyboxVao.Bind();

   m_programSkybox.Use();

   m_programSkybox.SetUniform("MVP", viewProj * glm::translate( m_camera.GetEye()) );

   

   // cube map textúra beállítása 0-ás mintavételezőre és annak a shaderre beállítása

   glActiveTexture(GL_TEXTURE0);

   glBindTexture(GL_TEXTURE_CUBE_MAP, m_skyboxTexture);

   glUniform1i(m_programSkybox.GetLocation("skyboxTexture"), 0);

   // az előző három sor <=> m_programSkybox.SetCubeTexture("skyboxTexture", 0, m_skyboxTexture);

   glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr);

   m_programSkybox.Unuse();

   // végül állítsuk vissza

   glDepthFunc(prevDepthFnc);

   // 1. feladat: készíts egy vertex shader-fragment shader párt, ami tárolt geometria _nélkül_ kirajzol egy tetszőleges pozícióba egy XYZ tengely-hármast,

   //               ahol az X piros, az Y zöld a Z pedig kék!

   //ImGui Testwindow

   ImGui::ShowTestWindow();

   DrawGui();

}

void CMyApp::KeyboardDown(SDL_KeyboardEvent& key)

{

   m_camera.KeyboardDown(key);

}

void CMyApp::KeyboardUp(SDL_KeyboardEvent& key)

{

   m_camera.KeyboardUp(key);

}

void CMyApp::MouseMove(SDL_MouseMotionEvent& mouse)

{

   m_camera.MouseMove(mouse);

}

void CMyApp::MouseDown(SDL_MouseButtonEvent& mouse)

{

}

void CMyApp::MouseUp(SDL_MouseButtonEvent& mouse)

{

}

void CMyApp::MouseWheel(SDL_MouseWheelEvent& wheel)

{

}

// a két paraméterbe az új ablakméret szélessége (_w) és magassága (_h) található

void CMyApp::Resize(int _w, int _h)

{

   glViewport(0, 0, _w, _h );

   m_camera.Resize(_w, _h);

}

float CMyApp::height(glm::vec2 pos) {

   return sin(pos.x * 3.14 * 2 * 3) * 0.2f;

}

void CMyApp::SpawnTrees()

{

   for (int i = 0; i < gridRes; ++i) {

       for (int j = 0; j < gridRes; ++j) {

           if (trees[i][j] < 0 && !disableLine[i]) {

               if (rand() % 100 < 5) {

                   trees[i][j] = initialHeight;

               }

           }

       }

   }

}

void CMyApp::DrawGui()

{

   ImGui::Begin("Settings");

   ImGui::DragFloat3("ligth dir", &light_dir[0]);

   ImGui::InputFloat("grow duration", &growTime);

   ImGui::InputFloat("year length", &yearLength);

   ImGui::SliderFloat("cut height", &cutHeight, 0.5f, 1.2f);

   /*if (ImGui::InputFloat("cut height", &cutHeight)) {

       if (cutHeight < 0.5f)

           cutHeight = 0.5f;

   }*/

   std::string text = "# trees cut " + std::to_string(cutTreeCount);

   ImGui::Text(text.c_str());

   if (ImGui::Button("CUT!")) {

       for (int i = 0; i < gridRes; ++i) {

           for (int j = 0; j < gridRes; ++j) {

               if (trees[i][j] >= 0) {

                   cutTreeCount++;

                   trees[i][j] = -1;

               }

           }

       }

   }

   ImGui::BeginGroup();

   //ImGui::Text("%s", i == 0 ? "Top" : i == 1 ? "25%" : i == 2 ? "Center" : i == 3 ? "75%" : "Bottom");

   ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)0), ImVec2(ImGui::GetWindowWidth() * 0.8f, 100.0f), true);

   for (int line = 0; line < gridRes; line++)

   {

           std::string text = "disable line " + std::to_string(line);

           //ImGui::Text(text.c_str());

           ImGui::Checkbox(text.c_str(), &disableLine[line]);

   }

   ImGui::EndChild();

   ImGui::EndGroup();

   ImGui::ColorEdit3("winter color", &winter_color[0]);

   ImGui::ColorEdit3("summer color", &summer_color[0]);

   ImGui::End();

}


FRAG.frag




#version 330 core

// pipeline-ból bejövő per-fragment attribútumok

in vec3 vs_out_pos;

in vec3 vs_out_norm;

in vec2 vs_out_tex;

out vec4 fs_out_col;

// irány fényforrás: fény iránya

uniform vec3 light_dir = vec3(-1,-1,-1);

uniform vec3 eye_pos;

// fénytulajdonságok: ambiens, diffúz, ...

uniform vec3 La = vec3(0.4, 0.4, 0.4);

uniform vec3 Ld = vec3(0.6, 0.6, 0.6);

uniform vec3 Ls = vec3(0.6, 0.6, 0.6);

uniform vec3 winter_color = vec3(0.3,0.6,0.7);

uniform vec3 spring_color = vec3(0.5,0.7,0.6);

uniform vec3 summer_color = vec3(0.7,0.7,0.6);

uniform vec3 autumn_color = vec3(0.6,0.4,0.3);

uniform float year_progress = 0; // 0 január eleje, 1 december vége

uniform sampler2D texImage;

void main()

{

   vec3 light_col;

   if(year_progress < 0.25)

       light_col = mix(winter_color, spring_color, year_progress/0.25);

   else if(year_progress < 0.5)

       light_col = mix(spring_color, summer_color, (year_progress-0.25)/0.25);

   else if(year_progress < 0.75)

       light_col = mix(summer_color, autumn_color, (year_progress-0.5)/0.25);

   else

       light_col = mix(autumn_color, winter_color, (year_progress-0.75)/0.25);

   vec3 ambient = La;

   vec3 normal = normalize(vs_out_norm);

   vec3 to_light = normalize(-light_dir);

   

   float cosa = clamp(dot(normal, to_light), 0, 1);

   vec3 diffuse = cosa*light_col;

   

   vec3 reflected_dir = reflect(normalize(light_dir), normal);

   vec3 to_eye = normalize(eye_pos - vs_out_pos);

   float cos2 = pow(clamp(dot(to_eye, reflected_dir), 0, 1), 16);

   vec3 specular = light_col * cos2;

   fs_out_col = vec4(ambient + diffuse + specular, 1) * texture(texImage, vs_out_tex);

}


MyVert.vert




#version 330 core

// VBO-ból érkező változók

in vec3 vs_in_pos;

in vec3 vs_in_norm;

in vec2 vs_in_tex;

// a pipeline-ban tovább adandó értékek

out vec3 vs_out_pos;

out vec3 vs_out_norm;

out vec2 vs_out_tex;

// shader külső paraméterei

uniform mat4 MVP;

uniform mat4 world;

uniform mat4 worldIT;

float height(vec2 pos) {

   return sin(pos.x*3.14*2*3)*0.2;

}

vec3 getPos(vec2 uv) {

   return vec3(

       uv.x,

       height(uv),

       uv.y

   );

}

void main()

{

   vec3 pos = getPos(vs_in_pos.xz);

   float eps = 0.01;

   //vec3 dx = (height(pos+vec3(eps,0,0)) - height(pos-vec3(eps,0,0)))/(2*eps);

   vec3 dx = getPos(vs_in_pos.xz + vec2(eps,0)) - getPos(vs_in_pos.xz - vec2(eps,0));

   vec3 dz = getPos(vs_in_pos.xz + vec2(0,eps)) - getPos(vs_in_pos.xz - vec2(0,eps));

   vec3 n = normalize(cross(dz,dx));

   gl_Position = MVP * vec4( pos, 1 );

   

   vs_out_pos = (world * vec4(pos, 1)).xyz;

   vs_out_norm = (worldIT * vec4(n, 0)).xyz;

   vs_out_tex = vs_in_tex;

}