544 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			544 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // GLAD
 | |
| #include "glad/glad.h"
 | |
| 
 | |
| // GLM
 | |
| #define GLM_ENABLE_EXPERIMENTAL
 | |
| #include "glm/ext/matrix_transform.hpp"
 | |
| #include "glm/ext/vector_float3.hpp"
 | |
| #include "glm/gtc/type_ptr.hpp"
 | |
| #include "glm/trigonometric.hpp"
 | |
| #include "glm/gtx/rotate_vector.hpp"
 | |
| #include "glm/gtx/string_cast.hpp"
 | |
| 
 | |
| // SDL
 | |
| #include <SDL2/SDL.h>
 | |
| #include <SDL2/SDL_stdinc.h>
 | |
| #include <SDL2/SDL_error.h>
 | |
| #include <SDL2/SDL_video.h>
 | |
| #include <SDL2/SDL_events.h>
 | |
| #include <SDL2/SDL_mouse.h>
 | |
| #include <SDL2/SDL_keycode.h>
 | |
| 
 | |
| // STDLIB
 | |
| #include <cstdint>
 | |
| #include <cstdio>
 | |
| #include <cmath>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #define WINDOW_WIDTH       1280
 | |
| #define WINDOW_HEIGHT      720
 | |
| #define WINDOW_HALF_WIDTH  640
 | |
| #define WINDOW_HALF_HEIGHT 360
 | |
| 
 | |
| enum exit_codes : int {
 | |
|   EXIT_CODE_SUCCESS,
 | |
|   EXIT_CODE_SDL_INIT_FAILED,
 | |
|   EXIT_CODE_WINDOW_CREATION_FAILED,
 | |
|   EXIT_CODE_OPENGL_CONTEXT_FAILED,
 | |
|   EXIT_CODE_GLAD_LOADER_FAILED,
 | |
| };
 | |
| 
 | |
| struct Camera {
 | |
|   // Point in world coordinates where the camera is located
 | |
|   glm::vec3 position;
 | |
| 
 | |
|   // view_direction is an actual direction vector. When used with glm::lookAt, it gets added to the
 | |
|   // position to calculate the target point the camera is looking at
 | |
|   glm::vec3 view_direction;
 | |
| 
 | |
|   // The up vector in world coordinates
 | |
|   glm::vec3 up;
 | |
| 
 | |
|   // pitch, yaw
 | |
|   glm::vec2 rotation;
 | |
| 
 | |
|   // View matrix to be sent as uniform to the vertex shader
 | |
|   glm::mat4     view_mat;
 | |
| 
 | |
|   // Projection matrix to be sent as uniform to the vertex shader
 | |
|   glm::mat4     projection_mat;
 | |
| };
 | |
| 
 | |
| struct Transform {
 | |
|   glm::mat4     translation;
 | |
|   glm::mat4     rotation;
 | |
|   glm::mat4     scale;
 | |
| };
 | |
| 
 | |
| struct Mesh {
 | |
|   // VAO is an object that stores the state needed to supply the GPU with the vertex data.
 | |
|   // Think of it as a specification or a C struct that defines the types of data stored for the
 | |
|   // vertex array including the attributes for each vertex as well the indices buffer if it exists.
 | |
|   GLuint vao;
 | |
| 
 | |
|   // The buffer that contains all the vertex data
 | |
|   // e.g. assume 3 vertices with position, colour and uv, the buffer would look like this
 | |
|   //  -----------------------------------------------------------------------------------
 | |
|   // | position1 | colour1 | uv1 | position2 | colour2 | uv2 | position3 | colour3 | uv3 |
 | |
|   //  -----------------------------------------------------------------------------------
 | |
|   GLuint vbo;
 | |
| 
 | |
|   // Holds the indices of the vertices that draw each triangle. Each triangle constitutes what
 | |
|   // OpenGL refers to as element, hence the name Element Buffer Object
 | |
|   GLuint ebo;
 | |
| 
 | |
|   GLuint shader_program;
 | |
|   GLuint vertices_count;
 | |
| };
 | |
| 
 | |
| struct Model {
 | |
|   Transform transform;
 | |
|   Mesh      mesh;
 | |
| 
 | |
|   // Model matrix to be sent as uniform to the vertex shader
 | |
|   glm::mat4 model_mat;
 | |
| 
 | |
|   GLint     u_model_idx;
 | |
|   GLint     u_view_idx;
 | |
|   GLint     u_projection_idx;
 | |
| };
 | |
| 
 | |
| struct App {
 | |
|   SDL_Window    *window;
 | |
|   SDL_GLContext context;
 | |
|   SDL_Event     event;
 | |
|   Model         plane;
 | |
|   Model         floor;
 | |
|   Camera        camera;
 | |
|   float         speed;
 | |
|   glm::vec2     prev_mouse;
 | |
|   bool          running;
 | |
| };
 | |
| 
 | |
| int         init                      (App &app);
 | |
| void        create_vertex_spec        (Model &model, const std::vector<GLfloat> &vertices, const std::vector<GLuint> &indices);
 | |
| void        create_graphics_pipeline  (Model &model, Camera &camera, const char *vs_file, const char *fs_file);
 | |
| void        run_main_loop             (App &app);
 | |
| void        cleanup                   (App &app);
 | |
| void        render_model              (const Model &model, const Camera &camera);
 | |
| GLuint      create_shader_program     (const std::string &vertex_shader_source, const std::string &fragment_shader_source);
 | |
| GLuint      compile_shader            (GLuint type, const std::string &source);
 | |
| std::string load_shader               (const std::string &filepath);
 | |
| void        handle_object_movement    (App &app);
 | |
| void        handle_camera_movement    (App &app);
 | |
| glm::vec3   rotation_to_view_direction(const Camera &camera);
 | |
| 
 | |
| int main() {
 | |
|   App app    = {};
 | |
|   int result = init(app);
 | |
|   if (result != EXIT_CODE_SUCCESS) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   const std::vector<GLfloat> plane_vertices = {
 | |
|     // vert0
 | |
|     -0.5f, -0.5f, 0.0f, // position
 | |
|      1.0f,  0.0f, 0.0f, // colour
 | |
|     // vert1
 | |
|      0.5f, -0.5f, 0.0f, // position
 | |
|      0.0f,  1.0f, 0.0f, // colour
 | |
|     // vert2
 | |
|     -0.5f,  0.5f, 0.0f, // position
 | |
|      0.0f,  0.0f, 1.0f, // colour
 | |
|     // vert3
 | |
|      0.5f,  0.5f, 0.0f, // position
 | |
|      1.0f,  1.0f, 0.0f, // colour
 | |
|   };
 | |
| 
 | |
|   const std::vector<GLuint> plane_indices = {
 | |
|     // first triangle
 | |
|     0, 1, 2,
 | |
|     // second triangle
 | |
|     2, 1, 3,
 | |
|   };
 | |
| 
 | |
|   const std::vector<GLfloat> floor_vertices = {
 | |
|     // vert0
 | |
|       -10.0f,     0.0f,    10.0f, // position
 | |
|     0.23529f, 0.43137f, 0.44313f, // colour
 | |
|     // vert1
 | |
|        10.0f,     0.0f,    10.0f, // position
 | |
|     0.23529f, 0.43137f, 0.44313f, // colour
 | |
|     // vert2
 | |
|       -10.0f,     0.0f,   -10.0f, // position
 | |
|     0.23529f, 0.43137f, 0.44313f, // colour
 | |
|     // vert3
 | |
|        10.0f,     0.0f,   -10.0f, // position
 | |
|     0.23529f, 0.43137f, 0.44313f, // colour
 | |
|   };
 | |
| 
 | |
|   const std::vector<GLuint> floor_indices = {
 | |
|     // first triangle
 | |
|     0, 1, 2,
 | |
|     // second triangle
 | |
|     2, 1, 3,
 | |
|   };
 | |
| 
 | |
|   // Setup the geometry
 | |
|   create_vertex_spec(app.plane, plane_vertices, plane_indices);
 | |
|   create_vertex_spec(app.floor, floor_vertices, floor_indices);
 | |
| 
 | |
|   // Setup graphics pipeline. At the very minimum creating vertex and fragment shaders
 | |
|   create_graphics_pipeline(app.plane, app.camera, "shaders/vert.glsl", "shaders/frag.glsl");
 | |
|   create_graphics_pipeline(app.floor, app.camera, "shaders/vert.glsl", "shaders/frag.glsl");
 | |
| 
 | |
|   run_main_loop(app);
 | |
| 
 | |
|   cleanup(app);
 | |
| 
 | |
|   return EXIT_CODE_SUCCESS;
 | |
| }
 | |
| 
 | |
| int init(App &app) {
 | |
|   if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
 | |
|     fprintf(stderr, "Failed to initialise SDL: %s", SDL_GetError());
 | |
|     return EXIT_CODE_SDL_INIT_FAILED;
 | |
|   }
 | |
| 
 | |
|   uint32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI;
 | |
|   app.window     = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
 | |
|                                     WINDOW_WIDTH, WINDOW_HEIGHT, flags);
 | |
|   if (!app.window) {
 | |
|     fprintf(stderr, "Failed to create window: %s", SDL_GetError());
 | |
|     return EXIT_CODE_WINDOW_CREATION_FAILED;
 | |
|   }
 | |
| 
 | |
|   SDL_WarpMouseInWindow(app.window, WINDOW_HALF_WIDTH, WINDOW_HALF_HEIGHT);
 | |
|   SDL_SetRelativeMouseMode(SDL_TRUE);
 | |
| 
 | |
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
 | |
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
 | |
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
 | |
|   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 | |
|   SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
 | |
| 
 | |
|   app.context = SDL_GL_CreateContext(app.window);
 | |
|   if (!app.context) {
 | |
|     fprintf(stderr, "Failed to create OpenGL context: %s", SDL_GetError());
 | |
|     return EXIT_CODE_OPENGL_CONTEXT_FAILED;
 | |
|   }
 | |
| 
 | |
|   // Init GLAD
 | |
|   if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) {
 | |
|     fprintf(stderr, "Failed to initialise glad");
 | |
|     return EXIT_CODE_GLAD_LOADER_FAILED;
 | |
|   }
 | |
| 
 | |
|   printf("%s\n%s\n%s\n%s\n",
 | |
|          glGetString(GL_VENDOR),
 | |
|          glGetString(GL_RENDERER),
 | |
|          glGetString(GL_VERSION),
 | |
|          glGetString(GL_SHADING_LANGUAGE_VERSION)
 | |
|   );
 | |
| 
 | |
|   app.speed                       = 0.04f;
 | |
|   app.plane.transform.translation = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.5f, -1.0f));
 | |
|   app.plane.transform.rotation    = glm::mat4(1.0f);
 | |
|   app.plane.transform.scale       = glm::mat4(1.0f);
 | |
|   app.plane.model_mat             = app.plane.transform.translation *
 | |
|                                     app.plane.transform.rotation * app.plane.transform.scale;
 | |
|   app.floor.transform.translation = glm::mat4(1.0f);
 | |
|   app.floor.transform.rotation    = glm::mat4(1.0f);
 | |
|   app.floor.transform.scale       = glm::mat4(1.0f);
 | |
|   app.floor.model_mat             = app.floor.transform.translation *
 | |
|                                     app.floor.transform.rotation * app.floor.transform.scale;
 | |
|   app.camera.rotation             = glm::vec2(-5.0f, -90.0f);
 | |
|   app.camera.position             = glm::vec3(0.0f, 0.5f,  3.0f);
 | |
|   app.camera.view_direction       = rotation_to_view_direction(app.camera);
 | |
|   app.camera.up                   = glm::vec3(0.0f, 1.0f,  0.0f);
 | |
|   app.camera.view_mat             = glm::lookAt(app.camera.position, app.camera.position + app.camera.view_direction, app.camera.up);
 | |
|   app.camera.projection_mat       = glm::perspective(glm::radians(60.0f),
 | |
|                                                      (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT,
 | |
|                                                      0.1f,
 | |
|                                                      20.0f);
 | |
|   app.prev_mouse                  = glm::vec2(WINDOW_HALF_WIDTH, WINDOW_HALF_HEIGHT);
 | |
| 
 | |
|   return EXIT_CODE_SUCCESS;
 | |
| }
 | |
| 
 | |
| void create_vertex_spec(Model &model, const std::vector<GLfloat> &vertices, const std::vector<GLuint> &indices) {
 | |
|   // Create and activate the VAO
 | |
|   glGenVertexArrays(1, &model.mesh.vao);
 | |
|   glBindVertexArray(model.mesh.vao);
 | |
| 
 | |
|   // Create and set up the VBO
 | |
|   glGenBuffers(1, &model.mesh.vbo);
 | |
|   glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vbo);
 | |
|   glBufferData(GL_ARRAY_BUFFER,
 | |
|                vertices.size() * sizeof(GLfloat),
 | |
|                (void *)vertices.data(),
 | |
|                GL_STATIC_DRAW
 | |
|   );
 | |
| 
 | |
|   // Create and set up the EBO
 | |
|   glGenBuffers(1, &model.mesh.ebo);
 | |
|   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.mesh.ebo);
 | |
|   glBufferData(GL_ELEMENT_ARRAY_BUFFER,
 | |
|                indices.size() * sizeof(GLuint),
 | |
|                (void *)indices.data(),
 | |
|                GL_STATIC_DRAW
 | |
|   );
 | |
| 
 | |
|   GLsizei vertex_stride          = 6 * sizeof(GLfloat);
 | |
|   GLvoid  *position_start_offset = (void *)0;
 | |
|   GLvoid  *colour_start_offset   = (void *)(3 * sizeof(GLfloat));
 | |
| 
 | |
|   // Defines the vertex attributes and their indices. This defines the attribute for the currently
 | |
|   // bound VAO
 | |
|   glEnableVertexAttribArray(0);
 | |
|   glEnableVertexAttribArray(1);
 | |
| 
 | |
|   // Defines the number of components for the attribute, its type, the stride between the component
 | |
|   // for each vertex and the offset of the first instance of the component in the buffer
 | |
|   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_stride, position_start_offset);
 | |
|   glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_stride, colour_start_offset);
 | |
| 
 | |
|   // Cleanup.
 | |
|   // The order matters. Unbind VAO first then disable the attributes. If you do it the
 | |
|   // other way around, the VAO will store that the attributes are disabled and nothing will be
 | |
|   // drawn during the rendering stage
 | |
|   glBindVertexArray(0);
 | |
|   glBindBuffer(GL_ARRAY_BUFFER, 0);
 | |
|   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 | |
|   glDisableVertexAttribArray(0);
 | |
|   glDisableVertexAttribArray(1);
 | |
| 
 | |
|   model.mesh.vertices_count = indices.size();
 | |
| }
 | |
| 
 | |
| void create_graphics_pipeline(Model &model, Camera &camera, const char *vs_file, const char *fs_file) {
 | |
|   const std::string vs_source = load_shader(vs_file);
 | |
|   const std::string fs_source = load_shader(fs_file);
 | |
| 
 | |
|   model.mesh.shader_program = create_shader_program(vs_source, fs_source);
 | |
| 
 | |
|   const char *u_model = "u_model";
 | |
|   model.u_model_idx = glGetUniformLocation(model.mesh.shader_program, u_model);
 | |
|   if (model.u_model_idx < 0) {
 | |
|     printf("Failed to find uniform %s\n", u_model);
 | |
|   }
 | |
| 
 | |
|   const char *u_view = "u_view";
 | |
|   model.u_view_idx = glGetUniformLocation(model.mesh.shader_program, u_view);
 | |
|   if (model.u_view_idx < 0) {
 | |
|     printf("Failed to find uniform %s\n", u_view);
 | |
|   }
 | |
| 
 | |
|   const char *u_projection = "u_projection";
 | |
|   model.u_projection_idx = glGetUniformLocation(model.mesh.shader_program, u_projection);
 | |
|   if (model.u_projection_idx < 0) {
 | |
|     printf("Failed to find uniform %s\n", u_projection);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void run_main_loop(App &app) {
 | |
|   app.running = true;
 | |
| 
 | |
|   while (app.running) {
 | |
|     while (SDL_PollEvent(&app.event)) {
 | |
|       switch (app.event.type) {
 | |
|       case SDL_QUIT:
 | |
|         app.running = false;
 | |
|         break;
 | |
|       case SDL_KEYDOWN:
 | |
|         if (app.event.key.keysym.sym == SDLK_ESCAPE) {
 | |
|           app.running = false;
 | |
|         } else {
 | |
|           handle_camera_movement(app);
 | |
|         }
 | |
| 
 | |
|         break;
 | |
|       case SDL_MOUSEMOTION:
 | |
|         handle_camera_movement(app);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Pre draw setup
 | |
|     glDisable(GL_DEPTH_TEST);
 | |
|     glDisable(GL_CULL_FACE);
 | |
| 
 | |
|     glViewport  (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
 | |
|     glClearColor(0.36f, 0.34f, 0.42f, 1.0f);
 | |
|     glClear     (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
 | |
| 
 | |
|     // Render models
 | |
|     render_model(app.floor, app.camera);
 | |
|     render_model(app.plane, app.camera);
 | |
| 
 | |
|     // Not necessary if we only have one shader program
 | |
|     glUseProgram(0);
 | |
| 
 | |
|     SDL_GL_SwapWindow(app.window);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void cleanup(App &app) {
 | |
|   SDL_GL_DeleteContext(app.context);
 | |
|   SDL_DestroyWindow(app.window);
 | |
|   SDL_Quit();
 | |
| }
 | |
| 
 | |
| void render_model(const Model &model, const Camera &camera) {
 | |
|   glUseProgram(model.mesh.shader_program);
 | |
|   if (model.u_model_idx >= 0) {
 | |
|     glUniformMatrix4fv(model.u_model_idx, 1, GL_FALSE, glm::value_ptr(model.model_mat));
 | |
|   }
 | |
|   if (model.u_view_idx >= 0) {
 | |
|     glUniformMatrix4fv(model.u_view_idx, 1, GL_FALSE, glm::value_ptr(camera.view_mat));
 | |
|   }
 | |
|   if (model.u_projection_idx >= 0) {
 | |
|     glUniformMatrix4fv(model.u_projection_idx, 1, GL_FALSE, glm::value_ptr(camera.projection_mat));
 | |
|   }
 | |
| 
 | |
|   glBindVertexArray(model.mesh.vao);
 | |
|   glDrawElements   (GL_TRIANGLES, model.mesh.vertices_count, GL_UNSIGNED_INT, (void *)0);
 | |
| }
 | |
| 
 | |
| GLuint create_shader_program(const std::string &vertex_shader_source, const std::string &fragment_shader_source) {
 | |
|   GLuint shader_program  = glCreateProgram();
 | |
|   GLuint vertex_shader   = compile_shader(GL_VERTEX_SHADER, vertex_shader_source);
 | |
|   GLuint fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_source);
 | |
| 
 | |
|   glAttachShader(shader_program, vertex_shader);
 | |
|   glAttachShader(shader_program, fragment_shader);
 | |
|   glLinkProgram (shader_program);
 | |
| 
 | |
|   GLint link_status;
 | |
|   glGetProgramiv(shader_program, GL_LINK_STATUS, &link_status);
 | |
|   if (link_status != GL_TRUE) {
 | |
|     GLsizei log_length = 0;
 | |
|     GLchar message[1024];
 | |
|     glGetProgramInfoLog(shader_program, 1024, &log_length, message);
 | |
|     fprintf(stderr, "Failed to link program: %s\n", message);
 | |
|   }
 | |
| 
 | |
|   // Validate the shader_program
 | |
|   glValidateProgram(shader_program);
 | |
|   GLint validation_status;
 | |
|   glGetProgramiv(shader_program, GL_VALIDATE_STATUS, &validation_status);
 | |
|   if (validation_status != GL_TRUE) {
 | |
|     GLsizei log_length = 0;
 | |
|     GLchar message[1024];
 | |
|     glGetProgramInfoLog(shader_program, 1024, &log_length, message);
 | |
|     fprintf(stderr, "Failed to validate program: %s\n", message);
 | |
|   }
 | |
| 
 | |
|   return shader_program;
 | |
| }
 | |
| 
 | |
| GLuint compile_shader(GLuint type, const std::string &source) {
 | |
|   GLuint shader             = glCreateShader(type);
 | |
|   const char *shader_source = source.c_str();
 | |
| 
 | |
|   glShaderSource (shader, 1, &shader_source, NULL);
 | |
|   glCompileShader(shader);
 | |
| 
 | |
|   GLint compile_status;
 | |
|   glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
 | |
|   if (compile_status != GL_TRUE) {
 | |
|     GLsizei log_length = 0;
 | |
|     GLchar message[1024];
 | |
|     glGetShaderInfoLog(shader, 1024, &log_length, message);
 | |
|     fprintf(stderr, "Failed to compile shader: %s\n", message);
 | |
|   }
 | |
| 
 | |
|   return shader;
 | |
| }
 | |
| 
 | |
| std::string load_shader(const std::string &filepath) {
 | |
|   FILE *fp = fopen(filepath.c_str(), "r");
 | |
|   if (!fp) {
 | |
|     return "";
 | |
|   }
 | |
| 
 | |
|   std::string output = {};
 | |
| 
 | |
|   char buf[1024] = {0};
 | |
|   while (fgets(buf, sizeof(buf), fp)) {
 | |
|     output += buf;
 | |
|   }
 | |
| 
 | |
|   return output;
 | |
| }
 | |
| 
 | |
| // Example of moving an object
 | |
| void handle_object_movement(App &app) {
 | |
|   switch (app.event.key.keysym.sym) {
 | |
|     case SDLK_w:
 | |
|         app.plane.transform.translation = glm::translate(app.plane.transform.translation,
 | |
|                                                          glm::vec3(0.0f, 0.0f, app.speed));
 | |
|         break;
 | |
|     case SDLK_s:
 | |
|         app.plane.transform.translation = glm::translate(app.plane.transform.translation,
 | |
|                                                          glm::vec3(0.0f, 0.0f, -app.speed));
 | |
|         break;
 | |
|     case SDLK_d:
 | |
|         app.plane.transform.translation = glm::translate(app.plane.transform.translation,
 | |
|                                                          glm::vec3(app.speed, 0.0f, 0.0f));
 | |
|         break;
 | |
|     case SDLK_a:
 | |
|         app.plane.transform.translation = glm::translate(app.plane.transform.translation,
 | |
|                                                          glm::vec3(-app.speed, 0.0f, 0.0f));
 | |
|         break;
 | |
|     case SDLK_RIGHT:
 | |
|         app.plane.transform.rotation = glm::rotate(app.plane.transform.rotation,
 | |
|                                                    glm::radians(10.0f), glm::vec3(0.0f, 1.0f, 0.0f));
 | |
|         break;
 | |
|     case SDLK_LEFT:
 | |
|         app.plane.transform.rotation = glm::rotate(app.plane.transform.rotation,
 | |
|                                                    glm::radians(-10.0f), glm::vec3(0.0f, 1.0f, 0.0f));
 | |
|         break;
 | |
|     case SDLK_r:
 | |
|         app.plane.transform.scale = glm::scale(app.plane.transform.scale, glm::vec3(1.2f));
 | |
|         break;
 | |
|     case SDLK_e:
 | |
|         app.plane.transform.scale = glm::scale(app.plane.transform.scale, glm::vec3(0.8f));
 | |
|         break;
 | |
|     default:
 | |
|         return;
 | |
|   }
 | |
| 
 | |
|   app.plane.model_mat = app.plane.transform.translation * app.plane.transform.rotation * app.plane.transform.scale;
 | |
|   app.floor.model_mat = app.floor.transform.translation * app.floor.transform.rotation * app.floor.transform.scale;
 | |
| }
 | |
| 
 | |
| void handle_camera_movement(App &app) {
 | |
|   if (app.event.type == SDL_MOUSEMOTION) {
 | |
|     glm::vec2 offset          = glm::vec2(-app.event.motion.yrel, app.event.motion.xrel) * 0.1f;
 | |
|     app.prev_mouse           += offset;
 | |
|     app.camera.rotation      += offset;
 | |
|     app.camera.rotation.x     = glm::clamp(app.camera.rotation.x, -89.0f, 89.0f);
 | |
|     app.camera.view_direction = rotation_to_view_direction(app.camera);
 | |
|   } else {
 | |
|     switch (app.event.key.keysym.sym) {
 | |
|       case SDLK_w:
 | |
|           app.camera.position += app.camera.view_direction * app.speed;
 | |
|           break;
 | |
|       case SDLK_s:
 | |
|           app.camera.position -= app.camera.view_direction * app.speed;
 | |
|           break;
 | |
|       case SDLK_d:
 | |
|           app.camera.position += glm::normalize(glm::cross(app.camera.view_direction, app.camera.up)) * app.speed; 
 | |
|           break;
 | |
|       case SDLK_a:
 | |
|           app.camera.position -= glm::normalize(glm::cross(app.camera.view_direction, app.camera.up)) * app.speed;
 | |
|           break;
 | |
|       default:
 | |
|           return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   app.camera.view_mat = glm::lookAt(app.camera.position, app.camera.position + app.camera.view_direction, app.camera.up);
 | |
| }
 | |
| 
 | |
| glm::vec3 rotation_to_view_direction(const Camera &camera) {
 | |
|   glm::vec3 direction;
 | |
|   direction.x = cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x));
 | |
|   direction.y = sin(glm::radians(camera.rotation.x));
 | |
|   direction.z = sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x));
 | |
| 
 | |
|   return glm::normalize(direction);
 | |
| }
 |