From 741b0c5b07e802c57ed0c71181a85e2d14f2a734 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 21 Nov 2021 01:23:53 -0500 Subject: [PATCH] Add cmrc for resource compiling, add shaders, create a 2d triangle --- .gitmodules | 3 + CMakeLists.txt | 2 + cmrc | 1 + examples/dev_testing/CMakeLists.txt | 10 ++ .../resources/shaders/fragment_core.glsl | 11 ++ .../resources/shaders/vertex_core.glsl | 17 ++ examples/dev_testing/src/main.cpp | 34 +++- include/simpleengine/destructable.h | 24 +++ include/simpleengine/event/event.h | 28 +++ include/simpleengine/game.h | 18 +- include/simpleengine/renderable.h | 25 +++ include/simpleengine/shader.h | 165 ++++++++++++++++++ include/simpleengine/shader_program.h | 122 +++++++++++++ include/simpleengine/shapes/2d/triangle.h | 66 +++++++ src/game.cpp | 48 +++-- 15 files changed, 557 insertions(+), 17 deletions(-) create mode 100644 .gitmodules create mode 160000 cmrc create mode 100644 examples/dev_testing/resources/shaders/fragment_core.glsl create mode 100644 examples/dev_testing/resources/shaders/vertex_core.glsl create mode 100644 include/simpleengine/destructable.h create mode 100644 include/simpleengine/event/event.h create mode 100644 include/simpleengine/renderable.h create mode 100644 include/simpleengine/shader.h create mode 100644 include/simpleengine/shader_program.h create mode 100644 include/simpleengine/shapes/2d/triangle.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5307d83 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cmrc"] + path = cmrc + url = https://github.com/vector-of-bool/cmrc.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e799746..4f790aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required (VERSION 3.6) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") project(SimpleEngine) +include(cmrc/CMakeRC.cmake) + # Add some CMake options: option(SIMPLE_ENGINE_BUILD_EXAMPLES "Build example projects" ON) diff --git a/cmrc b/cmrc new file mode 160000 index 0000000..a64bea5 --- /dev/null +++ b/cmrc @@ -0,0 +1 @@ +Subproject commit a64bea50c05594c8e7cf1f08e441bb9507742e2e diff --git a/examples/dev_testing/CMakeLists.txt b/examples/dev_testing/CMakeLists.txt index b9d96e8..01a45e9 100644 --- a/examples/dev_testing/CMakeLists.txt +++ b/examples/dev_testing/CMakeLists.txt @@ -8,8 +8,18 @@ file(GLOB_RECURSE source_list src/*.cpp) target_sources(dev_testing PRIVATE ${source_list}) target_include_directories(dev_testing PUBLIC include) +# Embed shaders +file(GLOB_RECURSE shaders_list resources/shaders/*.glsl) +cmrc_add_resource_library( + resource_shaders + WHENCE resources/shaders + PREFIX shaders + ${shaders_list} +) + # Link simpleengine target_link_libraries(dev_testing PUBLIC simpleengine) +target_link_libraries(dev_testing PRIVATE resource_shaders) # Set standard to C++20 set_target_properties(dev_testing PROPERTIES CXX_STANDARD 20 CXX_EXTENSIONS OFF) \ No newline at end of file diff --git a/examples/dev_testing/resources/shaders/fragment_core.glsl b/examples/dev_testing/resources/shaders/fragment_core.glsl new file mode 100644 index 0000000..166b593 --- /dev/null +++ b/examples/dev_testing/resources/shaders/fragment_core.glsl @@ -0,0 +1,11 @@ +#version 440 + +in vec3 vs_position; +in vec3 vs_color; +in vec2 vs_texcoord; + +out vec4 fs_color; + +void main() { + fs_color = vec4(vs_color, 1.f); +} \ No newline at end of file diff --git a/examples/dev_testing/resources/shaders/vertex_core.glsl b/examples/dev_testing/resources/shaders/vertex_core.glsl new file mode 100644 index 0000000..0ef4feb --- /dev/null +++ b/examples/dev_testing/resources/shaders/vertex_core.glsl @@ -0,0 +1,17 @@ +#version 440 + +layout (location = 0) in vec3 vertex_position; +layout (location = 1) in vec3 vertex_color; +layout (location = 2) in vec2 vertex_texcoord; + +out vec3 vs_position; +out vec3 vs_color; +out vec2 vs_texcoord; + +void main() { + vs_position = vertex_position; + vs_color = vertex_color; + vs_texcoord = vec2(vertex_texcoord.x, vertex_texcoord.y); + + gl_Position = vec4(vertex_position, 1.f); +} \ No newline at end of file diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index fc8e014..6f879a5 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -3,13 +3,45 @@ // Github: https://github.com/SeanOMik // +#include +#include +#include +#include #include +#include #include #include +#include + +#include +#include +CMRC_DECLARE(resource_shaders); + +std::string read_resource_shader(const std::string& path) { + auto fs = cmrc::resource_shaders::get_filesystem(); + cmrc::file vertex_file = fs.open(path); + + return std::string(vertex_file.begin()); +} int main(int argc, char *argv[]) { - simpleengine::Game game(1280, 720, "SimpleEngine - Developer Testing", false); + simpleengine::Game game(1280, 720, "SimpleEngine 3D OpenGL - Developer Testing", false); + + // Load shaders + std::string vertex_core = read_resource_shader("shaders/vertex_core.glsl"); + std::string fragment_core = read_resource_shader("shaders/fragment_core.glsl"); + + // Create shader program + simpleengine::ShaderProgram shader_prog; + shader_prog.add_shader_from_source(simpleengine::ShaderType::Vertex, vertex_core); + shader_prog.add_shader_from_source(simpleengine::ShaderType::Fragment, fragment_core); + shader_prog.link(); + std::shared_ptr base_shader_program = shader_prog.program; + + // Create just a simple 2d triangle + std::shared_ptr tri(new simpleengine::shapes_2d::Triangle(base_shader_program)); + game.add_event(tri); return game.run(); } \ No newline at end of file diff --git a/include/simpleengine/destructable.h b/include/simpleengine/destructable.h new file mode 100644 index 0000000..868cd22 --- /dev/null +++ b/include/simpleengine/destructable.h @@ -0,0 +1,24 @@ +// +// Created by SeanOMik on 3/12/2021. +// Github: https://github.com/SeanOMik +// + +#ifndef SIMPLEENGINE_DESTRUCTABLE_H +#define SIMPLEENGINE_DESTRUCTABLE_H + +namespace simpleengine { + class Destructable { + public: + virtual void destroy() { + destroying = true; + } + + virtual const bool& is_destroying() const { + return destroying; + } + protected: + bool destroying = false; + }; +} + +#endif //SIMPLEENGINE_DESTRUCTABLE_H \ No newline at end of file diff --git a/include/simpleengine/event/event.h b/include/simpleengine/event/event.h new file mode 100644 index 0000000..fdeec72 --- /dev/null +++ b/include/simpleengine/event/event.h @@ -0,0 +1,28 @@ +// +// Created by SeanOMik on 7/2/2020. +// Github: https://github.com/SeanOMik +// + +#ifndef SIMPLEENGINE_EVENT_H +#define SIMPLEENGINE_EVENT_H + +#include "../destructable.h" + +#include + +#include + +namespace simpleengine { + class Event : public simpleengine::Destructable { + public: + explicit Event(std::shared_ptr window = nullptr) : window(window) {} + virtual ~Event() = default; + + virtual void update(const float& delta_time) = 0; + virtual void render(std::shared_ptr target) = 0; + protected: + std::shared_ptr window; + }; +} + +#endif //GAMEENGINE_EVENT_H \ No newline at end of file diff --git a/include/simpleengine/game.h b/include/simpleengine/game.h index 355fbb3..71ecb35 100644 --- a/include/simpleengine/game.h +++ b/include/simpleengine/game.h @@ -8,15 +8,18 @@ #include #include +#include #include #include +#include "event/event.h" + namespace simpleengine { class Game { + private: + using self = simpleengine::Game; public: - friend class CollisionHandler; - /** * @brief Construct a new Game object. Initializes GLEW and OpenGL * @@ -24,10 +27,14 @@ namespace simpleengine { * @param h Height of viewport * @param window_name The name of the window */ - Game(int w, int h, const std::string& window_name, const bool& resizeable = false); + Game(int w, int h, const std::string& window_name, const int& gl_profile = GLFW_OPENGL_CORE_PROFILE, const int& major_version = 4, + const int& minor_version = 4, const bool& resizeable = false, const int& forward_compat = GL_TRUE); virtual ~Game(); + void add_event(std::shared_ptr event); + void update(); + void handle_input(); void render_window(); void render_items(); void exit(); @@ -38,7 +45,12 @@ namespace simpleengine { private: static void framebuffer_resize_callback(GLFWwindow*, int fbW, int fbH); + void initialize(const int& gl_profile, const int& major_version, const int& minor_version, + const bool& resizeable, const int& forward_compat = GL_TRUE); + std::shared_ptr window; + std::vector> events; + const bool& window_resizeable; }; } diff --git a/include/simpleengine/renderable.h b/include/simpleengine/renderable.h new file mode 100644 index 0000000..7621bb1 --- /dev/null +++ b/include/simpleengine/renderable.h @@ -0,0 +1,25 @@ +// +// Created by SeanOMik on 7/2/2020. +// Github: https://github.com/SeanOMik +// + +#ifndef SIMPLEENGINE_RENDERABLE_H +#define SIMPLEENGINE_RENDERABLE_H + +#include "event/event.h" + +#include + +#include + +namespace simpleengine { + class Renderable : public simpleengine::Event { + private: + using super = simpleengine::Event; + public: + explicit Renderable(std::shared_ptr window = nullptr) : super(window) {} + virtual ~Renderable() = default; + }; +} + +#endif //SIMPLEENGINE_RENDERABLE_H \ No newline at end of file diff --git a/include/simpleengine/shader.h b/include/simpleengine/shader.h new file mode 100644 index 0000000..c82145a --- /dev/null +++ b/include/simpleengine/shader.h @@ -0,0 +1,165 @@ +// +// Created by SeanOMik on 7/2/2020. +// Github: https://github.com/SeanOMik +// + +#ifndef SIMPLEENGINE_SHADER_H +#define SIMPLEENGINE_SHADER_H + +#include +#include + +#include + +#include "event/event.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace simpleengine { + class ShaderException : public std::exception { + public: + explicit ShaderException(char const* const msg) : std::exception(msg) { + + } + }; + + enum ShaderType { + Vertex = GL_VERTEX_SHADER, + Fragment = GL_FRAGMENT_SHADER, + }; + + class Shader : public simpleengine::Event { + private: + using super = simpleengine::Event; + protected: + Shader() { + + } + public: + static Shader from_source(const ShaderType& type, std::string& shader_source) { + Shader shd = Shader::from_source(std::make_shared(glCreateProgram()), type, shader_source); + + shd.link(); + shd.delete_shader(); + + return shd; + } + + static Shader from_source(std::shared_ptr program, const ShaderType& type, std::string& shader_source) { + Shader shd; + shd.program = program; + shd.shader = glCreateShader(type); + + const GLchar* vert_src = shader_source.c_str(); + glShaderSource(shd.shader, 1, &vert_src, NULL); + glCompileShader(shd.shader); + + GLint success = false; + glGetShaderiv(shd.shader, GL_COMPILE_STATUS, &success); + + if (!success) { + char log[512]; + glGetShaderInfoLog(shd.shader, 512, NULL, log); + + std::cerr << "Failed to load shader from source:" << std::endl << log << std::endl; + throw ShaderException("Failed to compile shader!"); + } + + glAttachShader(*shd.program, shd.shader); + + return shd; + } + + /** + * @brief Load a shader from a filepath. + * + * @param type The type of the shader. + * @param shader_path The path of the shader source. + */ + static Shader from_filepath(const ShaderType& type, const std::string& shader_path) { + Shader shd = Shader::from_filepath(std::make_shared(glCreateProgram()), type, shader_path); + + shd.link(); + shd.delete_shader(); + + return shd; + } + + /** + * @brief Load a shader from a filepath. + * + * @param program The shader program that this shader will be attached to. + * @param type The type of the shader. + * @param shader_path The path of shader source. + */ + static Shader from_filepath(std::shared_ptr program, const ShaderType& type, + const std::string& shader_path) { + std::ifstream fstream(shader_path, std::ios::in); + + if (!fstream.is_open()) { + std::cerr << "Failed to open shader file: " << shader_path << std::endl; + throw ShaderException("Failed to open shader file!"); + } + + std::stringstream ss; + { + std::string str; + while (std::getline(fstream, str)) + { + ss << str << std::endl; + } + } + std::string ss_str = ss.str(); + + return Shader::from_source(type, ss_str); + } + + virtual ~Shader() { + delete_shader(); + } + + /** + * @brief Link the shader program and checks for errors. + * + */ + void link() { + glLinkProgram(*program); + + GLint success = false; + glGetProgramiv(*program, GL_LINK_STATUS, &success); + + if (!success) { + std::cerr << "Failed to link shader program!" << std::endl; + throw ShaderException("Failed to link shader program!"); + } + } + + /** + * @brief Delete the shader with glDeleteShader. Only do this after we've linked. + * + */ + void delete_shader() { + glDeleteShader(shader); + } + + virtual void update(const float& delta_time) override { + //super::update(delta_time); + } + + virtual void render(std::shared_ptr target) override { + //super::render(target); + } + + std::shared_ptr program; + private: + GLuint shader; + }; +} + +#endif //SIMPLEENGINE_SHADER_H \ No newline at end of file diff --git a/include/simpleengine/shader_program.h b/include/simpleengine/shader_program.h new file mode 100644 index 0000000..e99a864 --- /dev/null +++ b/include/simpleengine/shader_program.h @@ -0,0 +1,122 @@ +// +// Created by SeanOMik on 7/2/2020. +// Github: https://github.com/SeanOMik +// + +#ifndef SIMPLEENGINE_SHADER_PROGRAM_H +#define SIMPLEENGINE_SHADER_PROGRAM_H + +#include +#include + +#include + +#include "event/event.h" +#include "shader.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace simpleengine { + class ShaderProgram : public simpleengine::Event { + private: + using super = simpleengine::Event; + public: + ShaderProgram() : program(std::make_shared(glCreateProgram())) { + + } + + virtual ~ShaderProgram() { + glDeleteProgram(*program); + } + + /** + * @brief Add a shader to this shader program. Also checks that its using the same `program` as this. + * + * @see ShaderProgram::add_shader(const ShaderType& type, const std::string& shader_path) + * @param shader The shader to add. + * @return ShaderProgram& self + */ + ShaderProgram& add_shader(Shader& shader) { + if (shader.program != this->program) { + throw std::exception("The added shader does not have the same program as this shade program!"); + } + + shaders.push_back(shader); + + return *this; + } + + /** + * @brief Create and add a shader from a string to this shader program. + * + * @param type The type of the shader. + * @param shader_path The path of the shader. + * @return ShaderProgram& self + */ + ShaderProgram& add_shader_from_source(const ShaderType& type, std::string& shader_source) { + Shader shd = Shader::from_source(program, type, shader_source); + + shaders.emplace_back(shd); + + return *this; + } + + /** + * @brief Create and add a shader from a filepath to this shader program. + * + * @param type The type of the shader. + * @param shader_path The path of the shader. + * @return ShaderProgram& self + */ + ShaderProgram& add_shader_from_path(const ShaderType& type, const std::string& shader_path) { + Shader shd = Shader::from_filepath(program, type, shader_path); + + shaders.emplace_back(shd); + + return *this; + } + + /** + * @brief Link the shader program. Also removes unused shader resources. + * + */ + void link() { + if (shaders.empty()) { + throw std::exception("Shaders cannot be empty when running simpleengine::ShaderProgram::link()!"); + } + + glLinkProgram(*program); + + GLint success = false; + glGetProgramiv(*program, GL_LINK_STATUS, &success); + + if (!success) { + std::cerr << "Failed to link shader program!" << std::endl; + throw ShaderException("Failed to link shader program!"); + } + + for (Shader& shader : shaders) { + shader.delete_shader(); + } + } + + virtual void update(const float& delta_time) { + + } + + virtual void render(std::shared_ptr target) { + glUseProgram(*program); + } + + std::shared_ptr program; + std::vector shaders; + }; +} + +#endif //SIMPLEENGINE_SHADER_PROGRAM_H \ No newline at end of file diff --git a/include/simpleengine/shapes/2d/triangle.h b/include/simpleengine/shapes/2d/triangle.h new file mode 100644 index 0000000..b3a3a00 --- /dev/null +++ b/include/simpleengine/shapes/2d/triangle.h @@ -0,0 +1,66 @@ +// +// Created by SeanOMik on 7/2/2020. +// Github: https://github.com/SeanOMik +// + +#ifndef SIMPLEENGINE_TRIANGLE_H +#define SIMPLEENGINE_TRIANGLE_H + +#include +#include + +#include + +#include "../../renderable.h" + +namespace simpleengine::shapes_2d { + class Triangle : public simpleengine::Renderable { + private: + using super = simpleengine::Renderable; + private: + std::shared_ptr shader_program; + public: + float vertices[9]; + uint32_t vbo; + uint32_t vao; + + Triangle(std::shared_ptr shader_program) : super(nullptr), shader_program(shader_program), vertices{ + -0.5f, -0.5f, 0.0f, // left + 0.5f, -0.5f, 0.0f, // right + 0.0f, 0.5f, 0.0f // top + } { + + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other + // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary. + glBindVertexArray(0); + } + + virtual ~Triangle() = default; + + virtual void update(const float& delta_time) override { + + } + + virtual void render(std::shared_ptr target) override { + glUseProgram(*shader_program); + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + } + }; +} + +#endif //SIMPLEENGINE_TRIANGLE_H \ No newline at end of file diff --git a/src/game.cpp b/src/game.cpp index 7805eba..349fe30 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4,6 +4,7 @@ // #include "game.h" +#include "event/event.h" #include @@ -12,17 +13,11 @@ #include -simpleengine::Game::Game(int w, int h, const std::string& window_name, const bool& resizeable) { +simpleengine::Game::Game(int w, int h, const std::string& window_name, const int& gl_profile, const int& major_version, + const int& minor_version, const bool& resizeable, const int& forward_compat) : window_resizeable(resizeable) { + initialize(gl_profile, major_version, minor_version, window_resizeable, forward_compat); + // Create a window - glfwInit(); - - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); - glfwWindowHint(GLFW_RESIZABLE, resizeable); - - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - window = std::shared_ptr(glfwCreateWindow(w, h, window_name.c_str(), NULL, NULL)); // If we're not resizeable, we need to set the viewport size. @@ -45,22 +40,49 @@ simpleengine::Game::Game(int w, int h, const std::string& window_name, const boo } } +void simpleengine::Game::initialize(const int& gl_profile, const int& major_version, const int& minor_version, + const bool& resizeable, const int& forward_compat) { + glfwInit(); + + glfwWindowHint(GLFW_OPENGL_PROFILE, gl_profile); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major_version); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor_version); + glfwWindowHint(GLFW_RESIZABLE, window_resizeable); + + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, forward_compat); +} + simpleengine::Game::~Game() { } +void simpleengine::Game::add_event(std::shared_ptr event) { + events.push_back(event); +} + void simpleengine::Game::update() { - + handle_input(); + + // Update items + for (const std::shared_ptr& event : events) { + event->update(0.f); + } +} + +void simpleengine::Game::handle_input() { + } void simpleengine::Game::render_window() { - glClearColor(0.f, 0.f, 0.f, 1.f); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); render_items(); } void simpleengine::Game::render_items() { - + for (const std::shared_ptr& event : events) { + event->render(window); + } } int simpleengine::Game::run() {