wip: Basic spinning triangle demonstration

This commit is contained in:
Archie Hilton 2024-05-06 18:14:17 +01:00
parent 487dae631f
commit d7996a991e
6 changed files with 222 additions and 13 deletions

View File

@ -1,6 +1,12 @@
add_executable(Exponent
main.cc
GLProgramLoader.cc
)
target_link_libraries(Exponent PUBLIC glfw glm::glm)
target_include_directories(Exponent PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_include_directories(Exponent PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_compile_definitions(Exponent PRIVATE GLFW_INCLUDE_NONE)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/shaders/shader.frag" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/shaders/")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/shaders/shader.vert" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/shaders/")

81
src/GLProgramLoader.cc Normal file
View File

@ -0,0 +1,81 @@
#include "GLProgramLoader.hh"
#include <exception>
#include <format>
#include <fstream>
#include <sstream>
static GLuint compile_shader(GLenum shader_type, const std::string &path) {
if (shader_type != GL_VERTEX_SHADER && shader_type != GL_FRAGMENT_SHADER) {
throw GLProgramException(std::format("Unsupported shader type {} provided.", shader_type));
}
// Check that the path actually exists
std::ifstream shader_file_handle;
shader_file_handle.open(path);
if (!shader_file_handle.is_open()) {
throw GLProgramException(std::format("Failed to open path {}.", path));
}
// Read the data in.
std::stringstream shader_source_stream;
shader_source_stream << shader_file_handle.rdbuf();
std::string shader_source_str = shader_source_stream.str();
const GLchar *shader_source = shader_source_str.c_str();
GLuint shader_id = glCreateShader(shader_type);
if (shader_id == 0) {
throw GLProgramException(std::format("Error when creating shader type {}.", shader_type));
}
glShaderSource(shader_id, 1, &shader_source, NULL);
glCompileShader(shader_id);
GLint did_compile;
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &did_compile);
if(!did_compile) {
GLint len = 0;
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &len);
std::string error_log(len, ' ');
glGetShaderInfoLog(shader_id, len, &len, error_log.data());
glDeleteShader(shader_id);
throw GLProgramException(std::format("Shader {} compilation failed:\n{}", path, error_log));
}
return shader_id;
}
GLuint compile_and_link_program(const std::vector<std::pair<GLuint, std::string>> &shaders) {
// Firstly, compile all the shaders and get all of their ids.
// I would love to use a functional map here, but C++ doesn't really do that.
std::vector<GLuint> shader_ids = {};
for(auto &s:shaders) {
shader_ids.push_back(compile_shader(s.first, s.second));
}
GLuint program_id = glCreateProgram();
for (auto &s:shader_ids) {
glAttachShader(program_id, s);
}
glLinkProgram(program_id);
GLint link_worked;
glGetProgramiv(program_id, GL_LINK_STATUS, &link_worked);
if (!link_worked) {
GLint len = 0;
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &len);
std::string error_log(len, ' ');
glGetProgramInfoLog(program_id, len, &len, error_log.data());
glDeleteProgram(program_id);
throw GLProgramException(std::format("Program {} link failed:\n{}", program_id, error_log));
}
return program_id;
}

24
src/GLProgramLoader.hh Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "glad/gl.h"
#include <string>
#include <optional>
#include <vector>
class GLProgramException : public std::exception {
std::string m_what;
public:
GLProgramException(const char *str):m_what(str) {
}
GLProgramException(const std::string &str):m_what(str) {
}
virtual const char* what() const throw() {
return m_what.c_str();
}
};
GLuint compile_and_link_program(const std::vector<std::pair<GLuint, std::string>> &shaders);

View File

@ -1,9 +1,20 @@
#define GLAD_GL_IMPLEMENTATION
#include "glad/gl.h"
#define GLFW_INCLUDE_NONE
#include "GLFW/glfw3.h"
//http://seshbot.com/blog/2015/05/05/an-introduction-to-opengl-getting-started/
#include "GLProgramLoader.hh"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// This has to come last.
#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>
static void dbg_log(const char* str) {
std::cout << str << std::endl;
}
static void key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
@ -11,14 +22,74 @@ static void key_callback(GLFWwindow *w, int key, int scancode, int action, int m
}
}
#define VERTEX_SHADER_LOCATION "shaders/shader.vert"
#define FRAGMENT_SHADER_LOCATION "shaders/shader.frag"
GLuint g_program_id = 0;
static GLuint time_unif;
static void init_shaders() {
dbg_log("Initialising shaders");
const std::vector<std::pair<GLuint, std::string>> shaders = {
{GL_VERTEX_SHADER, VERTEX_SHADER_LOCATION},
{GL_FRAGMENT_SHADER, FRAGMENT_SHADER_LOCATION},
};
try {
g_program_id = compile_and_link_program(shaders);
} catch (GLProgramException e) {
std::cerr << e.what() << std::endl;
} catch (...) {
std::cerr << "Unexpected exception while compiling shaders." << std::endl;
}
glUseProgram(g_program_id);
glGetUniformLocation(g_program_id, "time");
}
static const GLfloat verts[6] = {
0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f
};
static GLuint vertex_buf;
static GLuint array_buf;
static const GLuint vertex_data_position = 0;
// Do the work of initialising opengl so we can draw stuff
static void init_opengl() {
dbg_log("Initialising opengl");
// Configure the context's capabilities
glClearColor(0.0, 0.0, 0.0, 1.0);
glGenVertexArrays(1, &array_buf);
glBindVertexArray(array_buf);
glGenBuffers(1, &vertex_buf);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
init_shaders();
glVertexAttribPointer(vertex_data_position, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(vertex_data_position);
}
int main() {
dbg_log("Initialising glfw");
// Initialise GLFW
if (!glfwInit()) {
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create our context
GLFWwindow *w = glfwCreateWindow(640, 480, "Test", NULL, NULL);
if (!w) {
@ -32,13 +103,7 @@ int main() {
// Set up the key handler
glfwSetKeyCallback(w, key_callback);
// Configure the context's capabilities
glClearColor(1.0, 0.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND); // Maybe this allows transparency?
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// TODO: Actually make some opengl shit happen
init_opengl();
// Main Loop
while(!glfwWindowShouldClose(w)) {
@ -50,6 +115,14 @@ int main() {
glClear(GL_COLOR_BUFFER_BIT);
*/
float time = glfwGetTime();
glClear(GL_COLOR_BUFFER_BIT);
glUniform1f(time_unif, time);
glBindVertexArray(array_buf);
glDrawArrays(GL_TRIANGLES, 0, 3);
// TODO Draw primitives
glfwSwapBuffers(w);

8
src/shaders/shader.frag Normal file
View File

@ -0,0 +1,8 @@
#version 430 core
out vec4 f_color;
// All fragments are the same colour for now.
void main() {
f_color = vec4(1.0, 1.0, 1.0, 1.0);
}

17
src/shaders/shader.vert Normal file
View File

@ -0,0 +1,17 @@
#version 430 core
layout (location = 0) in vec4 v_position;
uniform float time;
// Simple passthrough shader for now.
void main() {
vec2 new_xy = v_position.xy * mat2x2(
cos(time), -sin(time),
sin(time), cos(time)
);
vec4 new_position = vec4(new_xy.x, new_xy.y, 0.0, 1.0);
gl_Position = new_position;
}