443 lines
14 KiB
C++
443 lines
14 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_timer.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
|
|
|
|
#define min(a, b) (a < b ? a : b)
|
|
#define max(a, b) (a > b ? a : b)
|
|
#define clamp(v, a, b) (min(max(v, a), b))
|
|
|
|
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,
|
|
};
|
|
|
|
class Shader {
|
|
public:
|
|
Shader(const std::string &vert_file, const std::string &frag_file);
|
|
~Shader();
|
|
void activate();
|
|
void set_float(const char *name, float value);
|
|
void set_vec3(const char *name, glm::vec3 vector);
|
|
void set_mat3(const char *name, glm::mat3 matrix);
|
|
void set_mat4(const char *name, glm::mat4 matrix);
|
|
GLuint program;
|
|
private:
|
|
void link_program(GLuint vert, GLuint frag);
|
|
GLuint load_and_compile_shader(const std::string &filepath, GLenum shader_type);
|
|
std::string load_shader_from_file(const std::string &filepath);
|
|
static const char *get_shader_type_string(GLenum shader_type);
|
|
};
|
|
|
|
int main() {
|
|
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
|
|
return EXIT_CODE_SDL_INIT_FAILED;
|
|
}
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 24);
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
|
SDL_Window *window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
|
if (!window) {
|
|
return EXIT_CODE_WINDOW_CREATION_FAILED;
|
|
}
|
|
|
|
SDL_GLContext context = SDL_GL_CreateContext(window);
|
|
if (!context) {
|
|
return EXIT_CODE_OPENGL_CONTEXT_FAILED;
|
|
}
|
|
|
|
if (gladLoadGLLoader(SDL_GL_GetProcAddress) == 0) {
|
|
return EXIT_CODE_GLAD_LOADER_FAILED;
|
|
}
|
|
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
SDL_WarpMouseInWindow(window, WINDOW_HALF_WIDTH, WINDOW_HALF_HEIGHT);
|
|
|
|
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
std::vector<GLfloat> vertices = {
|
|
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal
|
|
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal
|
|
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // position, normal
|
|
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal
|
|
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, // position, normal
|
|
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal
|
|
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal
|
|
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, // position, normal
|
|
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, // position, normal
|
|
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // position, normal
|
|
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal
|
|
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal
|
|
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // position, normal
|
|
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, // position, normal
|
|
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, // position, normal
|
|
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal
|
|
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, // position, normal
|
|
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // position, normal
|
|
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, // position, normal
|
|
};
|
|
|
|
std::vector<GLuint> indices = {
|
|
20, 11, 0,
|
|
0, 6, 20,
|
|
7, 1, 3,
|
|
3, 16, 7,
|
|
24, 14, 21,
|
|
21, 4, 24,
|
|
2, 12, 10,
|
|
10, 22, 2,
|
|
8, 9, 19,
|
|
19, 18, 8,
|
|
13, 5, 17,
|
|
17, 23, 15
|
|
};
|
|
|
|
GLuint vao = 0;
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
|
|
GLuint ebo = 0;
|
|
glGenBuffers(1, &ebo);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
|
|
|
|
GLuint vbo = 0;
|
|
glGenBuffers(1, &vbo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW);
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void *)0);
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat)));
|
|
glEnableVertexAttribArray(1);
|
|
|
|
GLuint light_vao = 0;
|
|
glGenVertexArrays(1, &light_vao);
|
|
glBindVertexArray(light_vao);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void *)0);
|
|
glEnableVertexAttribArray(0);
|
|
|
|
glBindVertexArray(0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
Shader main_shader {"shaders/vert.glsl", "shaders/frag.glsl"};
|
|
Shader light_shader {"shaders/vert.glsl", "shaders/light_frag.glsl"};
|
|
|
|
const float camera_speed = 25.0f;
|
|
glm::vec3 camera_position = glm::vec3(-2.0f, 0.0f, 6.0f);
|
|
glm::vec3 camera_forward = glm::vec3(0.0f);
|
|
glm::vec3 world_up = glm::vec3(0.0f, 1.0f, 0.0f);
|
|
glm::vec3 light_position = glm::vec3(1.0f, 0.5f, 0.0f);
|
|
glm::vec3 light_diffuse = glm::vec3(0.5f, 0.5f, 0.5f);
|
|
|
|
float yaw = -70.0f;
|
|
float pitch = 0.0f;
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
glm::mat4 view = glm::mat4(1.0f);
|
|
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f);
|
|
glm::mat3 normal_mat = glm::mat3(1.0f);
|
|
|
|
main_shader.set_mat4 ("projection", projection);
|
|
main_shader.set_vec3 ("material.ambient", glm::vec3(1.0f, 0.5f, 0.31f));
|
|
main_shader.set_vec3 ("material.diffuse", glm::vec3(1.0f, 0.5f, 0.31f));
|
|
main_shader.set_vec3 ("material.specular", glm::vec3(0.5f, 0.5f, 0.5f));
|
|
main_shader.set_float("material.shininess", 32.0f);
|
|
main_shader.set_vec3 ("light.ambient", glm::vec3(0.2f, 0.2f, 0.2f));
|
|
main_shader.set_vec3 ("light.diffuse", light_diffuse);
|
|
main_shader.set_vec3 ("light.specular", glm::vec3(1.0f, 1.0f, 1.0f));
|
|
light_shader.set_mat4("projection", projection);
|
|
|
|
const float light_radius = 3.0f;
|
|
const float sensitivity = 0.1f;
|
|
int last_mouse_x = WINDOW_HALF_WIDTH;
|
|
int last_mouse_y = WINDOW_HALF_HEIGHT;
|
|
uint32_t last_frame = SDL_GetTicks();
|
|
float delta = 0.0f;
|
|
bool running = true;
|
|
SDL_Event event = {};
|
|
|
|
while (running) {
|
|
uint32_t ticks = SDL_GetTicks();
|
|
float seconds = ticks * 0.001f;
|
|
delta = (ticks - last_frame) * 0.001f;
|
|
light_position = glm::vec3(sin(seconds) * light_radius, light_position.y, cos(seconds) * light_radius);
|
|
light_diffuse = glm::vec3(
|
|
clamp(sin(seconds * 3.0f) + 1.0f, 0.0f, 1.0f),
|
|
clamp(cos(seconds * 8.0f) + 1.0f, 0.0f, 1.0f),
|
|
clamp(tan(seconds * 5.0f) + 3.0f, 0.0f, 1.0f)
|
|
);
|
|
last_frame = ticks;
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
switch (event.type) {
|
|
case SDL_QUIT:
|
|
running = false;
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
if (event.key.keysym.sym == SDLK_ESCAPE) {
|
|
running = false;
|
|
} else if (event.key.keysym.sym == SDLK_w) {
|
|
camera_position += camera_speed * delta * camera_forward;
|
|
} else if (event.key.keysym.sym == SDLK_s) {
|
|
camera_position -= camera_speed * delta * camera_forward;
|
|
} else if (event.key.keysym.sym == SDLK_d) {
|
|
camera_position += camera_speed * delta * glm::normalize(glm::cross(camera_forward, world_up));
|
|
} else if (event.key.keysym.sym == SDLK_a) {
|
|
camera_position -= camera_speed * delta * glm::normalize(glm::cross(camera_forward, world_up));
|
|
}
|
|
break;
|
|
case SDL_MOUSEMOTION: {
|
|
float x_offset = event.motion.xrel;
|
|
float y_offset = -event.motion.yrel;
|
|
|
|
last_mouse_x = last_mouse_x + event.motion.xrel;
|
|
last_mouse_y = last_mouse_y + event.motion.yrel;
|
|
|
|
x_offset *= sensitivity;
|
|
y_offset *= sensitivity;
|
|
|
|
yaw += x_offset;
|
|
pitch += y_offset;
|
|
|
|
if(pitch > 89.0f) {
|
|
pitch = 89.0f;
|
|
}
|
|
|
|
if(pitch < -89.0f) {
|
|
pitch = -89.0f;
|
|
}
|
|
}
|
|
break;
|
|
case SDL_WINDOWEVENT:
|
|
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
SDL_Window *wnd = SDL_GetWindowFromID(event.window.windowID);
|
|
if (!wnd) {
|
|
continue;
|
|
}
|
|
int w, h;
|
|
SDL_GL_GetDrawableSize(wnd, &w, &h);
|
|
glViewport(0, 0, w, h);
|
|
SDL_WarpMouseInWindow(wnd, (int)(w * 0.5f), (int)(h * 0.5f));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
camera_forward.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
|
|
camera_forward.y = sin(glm::radians(pitch));
|
|
camera_forward.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
|
|
camera_forward = glm::normalize(camera_forward);
|
|
|
|
view = glm::lookAt(camera_position, camera_position + camera_forward, world_up);
|
|
main_shader.set_vec3("camera_position", camera_position);
|
|
main_shader.set_mat4("view", view);
|
|
light_shader.set_mat4("view", view);
|
|
|
|
glClearColor(0.04f, 0.08f, 0.08f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
|
|
normal_mat = glm::transpose(glm::inverse(model));
|
|
main_shader.activate();
|
|
main_shader.set_vec3("light.position", light_position);
|
|
main_shader.set_vec3("light.diffuse", light_diffuse);
|
|
main_shader.set_mat4("model", model);
|
|
main_shader.set_mat3("normal_mat", normal_mat);
|
|
glBindVertexArray(vao);
|
|
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (void *)0);
|
|
|
|
model = glm::translate(glm::mat4(1.0f), light_position);
|
|
model = glm::scale(model, glm::vec3(0.2f));
|
|
light_shader.activate();
|
|
light_shader.set_mat4("model", model);
|
|
light_shader.set_vec3("light_diffuse", light_diffuse);
|
|
glBindVertexArray(light_vao);
|
|
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (void *)0);
|
|
|
|
SDL_GL_SwapWindow(window);
|
|
}
|
|
|
|
SDL_GL_DeleteContext(context);
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
|
|
return EXIT_CODE_SUCCESS;
|
|
}
|
|
|
|
|
|
Shader::Shader(const std::string &vert_file, const std::string &frag_file) {
|
|
GLuint vert = load_and_compile_shader(vert_file, GL_VERTEX_SHADER);
|
|
GLuint frag = load_and_compile_shader(frag_file, GL_FRAGMENT_SHADER);
|
|
link_program(vert, frag);
|
|
glDeleteShader(vert);
|
|
glDeleteShader(frag);
|
|
}
|
|
|
|
Shader::~Shader() {
|
|
if (program > 0) {
|
|
glDeleteProgram(program);
|
|
}
|
|
}
|
|
|
|
void Shader::activate() {
|
|
if (program > 0) {
|
|
glUseProgram(program);
|
|
}
|
|
}
|
|
|
|
void Shader::set_float(const char *name, float value) {
|
|
activate();
|
|
glUniform1f(glGetUniformLocation(program, name), value);
|
|
}
|
|
|
|
void Shader::set_vec3(const char *name, glm::vec3 vector) {
|
|
activate();
|
|
glUniform3f(glGetUniformLocation(program, name), vector.x, vector.y, vector.z);
|
|
}
|
|
|
|
void Shader::set_mat3(const char *name, glm::mat3 matrix) {
|
|
activate();
|
|
glUniformMatrix3fv(glGetUniformLocation(program, name), 1, GL_FALSE, glm::value_ptr(matrix));
|
|
}
|
|
|
|
void Shader::set_mat4(const char *name, glm::mat4 matrix) {
|
|
activate();
|
|
glUniformMatrix4fv(glGetUniformLocation(program, name), 1, GL_FALSE, glm::value_ptr(matrix));
|
|
}
|
|
|
|
void Shader::link_program(GLuint vert, GLuint frag) {
|
|
program = glCreateProgram();
|
|
glAttachShader(program, vert);
|
|
glAttachShader(program, frag);
|
|
|
|
glLinkProgram(program);
|
|
GLint program_linked;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &program_linked);
|
|
if (program_linked != GL_TRUE)
|
|
{
|
|
GLsizei log_length = 0;
|
|
GLchar message[1024];
|
|
glGetProgramInfoLog(program, 1024, &log_length, message);
|
|
printf("Failed to link program: %s\n", message);
|
|
program = 0;
|
|
}
|
|
}
|
|
|
|
GLuint Shader::load_and_compile_shader(const std::string &filepath, GLenum shader_type) {
|
|
std::string src = load_shader_from_file(filepath);
|
|
const char *shader_src = src.c_str();
|
|
|
|
GLuint shader = glCreateShader(shader_type);
|
|
glShaderSource(shader, 1, &shader_src, NULL);
|
|
glCompileShader(shader);
|
|
|
|
GLint shader_compiled;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_compiled);
|
|
if (shader_compiled != GL_TRUE)
|
|
{
|
|
GLsizei log_length = 0;
|
|
GLchar message[1024];
|
|
glGetShaderInfoLog(shader, 1024, &log_length, message);
|
|
printf("Failed to compile %s shader: %s\n", get_shader_type_string(shader_type), message);
|
|
return 0;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
std::string Shader::load_shader_from_file(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;
|
|
}
|
|
|
|
const char *Shader::get_shader_type_string(GLenum shader_type) {
|
|
const char *output;
|
|
|
|
switch (shader_type) {
|
|
case GL_COMPUTE_SHADER:
|
|
output = "compute";
|
|
break;
|
|
case GL_VERTEX_SHADER:
|
|
output = "vertex";
|
|
break;
|
|
case GL_TESS_CONTROL_SHADER:
|
|
output = "tess_control";
|
|
break;
|
|
case GL_TESS_EVALUATION_SHADER:
|
|
output = "tess_evaluation";
|
|
break;
|
|
case GL_GEOMETRY_SHADER:
|
|
output = "geometry";
|
|
break;
|
|
case GL_FRAGMENT_SHADER:
|
|
output = "fragment";
|
|
break;
|
|
default:
|
|
output = "UNKNOWN";
|
|
break;
|
|
}
|
|
|
|
return output;
|
|
}
|