diff options
Diffstat (limited to 'src')
23 files changed, 1054 insertions, 2 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp index d98ae4f..92bf211 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1,5 +1,14 @@ #include "application.h" + +#include "rendering/renderer.h" +#include "rendering/vertex_array.h" +#include "rendering/vertex_buffer.h" +#include "rendering/index_buffer.h" +#include "rendering/shader.h" + #include <iostream> +#include <glm/glm.hpp> +#include <glm/gtc/matrix_transform.hpp> namespace Donut { @@ -73,8 +82,39 @@ namespace Donut }); } - void Application::on_init() { } - void Application::on_shutdown() { } + void Application::on_init() + { + Renderer::init(); + + float vertices[3 * 7] = + { + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f + }; + + uint32_t indices[3] = { 0, 1, 2 }; + + m_vertex_array = std::shared_ptr<VertexArray>(VertexArray::create()); + m_vertex_buffer = std::shared_ptr<VertexBuffer>(VertexBuffer::create(vertices, sizeof(vertices))); + + VertexBufferLayout layout; + layout.push<float>(3); + layout.push<float>(4); + m_vertex_buffer->set_layout(layout); + + m_vertex_array->add_vertex_buffer(m_vertex_buffer); + + m_index_buffer = std::shared_ptr<IndexBuffer>(IndexBuffer::create(indices, 3)); + m_vertex_array->set_index_buffer(m_index_buffer); + + m_shader = std::shared_ptr<Shader>(Shader::create("assets/basic.glsl")); + } + + void Application::on_shutdown() + { + Renderer::shutdown(); + } void Application::on_update() { @@ -82,5 +122,15 @@ namespace Donut void Application::on_render() { + Renderer::set_clear_color({ 0.1f, 0.1f, 0.1f, 1.0f }); + Renderer::clear(); + + Renderer::begin_scene(); + + glm::mat4 view_projection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + + Renderer::submit(m_shader, m_vertex_array, glm::mat4(1.0f)); + + Renderer::end_scene(); } } diff --git a/src/core/application.h b/src/core/application.h index d88eab2..a860e1c 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -7,6 +7,14 @@ namespace Donut { + class VertexArray; + class VertexBuffer; + class IndexBuffer; + class Shader; +}; + +namespace Donut +{ class Application { public: @@ -30,6 +38,11 @@ namespace Donut bool m_running; bool m_minimized; + std::shared_ptr<VertexArray> m_vertex_array; + std::shared_ptr<VertexBuffer> m_vertex_buffer; + std::shared_ptr<IndexBuffer> m_index_buffer; + std::shared_ptr<Shader> m_shader; + static Application* s_instance; }; } diff --git a/src/core/window.cpp b/src/core/window.cpp index ab5b977..77ef885 100644 --- a/src/core/window.cpp +++ b/src/core/window.cpp @@ -1,6 +1,7 @@ #include "window.h" #include <cstdint> #include <iostream> +#include <glad/glad.h> namespace Donut { @@ -42,6 +43,14 @@ namespace Donut } glfwMakeContextCurrent(m_window); + + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD!" << std::endl; + glfwTerminate(); + return; + } + glfwSetWindowUserPointer(m_window, this); glfwSetErrorCallback(glfw_error_callback); diff --git a/src/platform/opengl/opengl_index_buffer.cpp b/src/platform/opengl/opengl_index_buffer.cpp new file mode 100644 index 0000000..fac0585 --- /dev/null +++ b/src/platform/opengl/opengl_index_buffer.cpp @@ -0,0 +1,28 @@ +#include "opengl_index_buffer.h" +#include <glad/glad.h> + +namespace Donut +{ + OpenGLIndexBuffer::OpenGLIndexBuffer(const uint32_t* indices, uint32_t count) + : m_count(count) + { + glCreateBuffers(1, &m_renderer_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW); + } + + OpenGLIndexBuffer::~OpenGLIndexBuffer() + { + glDeleteBuffers(1, &m_renderer_id); + } + + void OpenGLIndexBuffer::bind() const + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id); + } + + void OpenGLIndexBuffer::unbind() const + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +}; diff --git a/src/platform/opengl/opengl_index_buffer.h b/src/platform/opengl/opengl_index_buffer.h new file mode 100644 index 0000000..b0498b4 --- /dev/null +++ b/src/platform/opengl/opengl_index_buffer.h @@ -0,0 +1,22 @@ +#pragma once + +#include "rendering/index_buffer.h" + +namespace Donut +{ + class OpenGLIndexBuffer + : public IndexBuffer + { + public: + OpenGLIndexBuffer(const uint32_t* indices, uint32_t count); + virtual ~OpenGLIndexBuffer(); + + virtual void bind() const override; + virtual void unbind() const override; + virtual uint32_t get_count() const override { return m_count; } + + private: + uint32_t m_renderer_id; + uint32_t m_count; + }; +}; diff --git a/src/platform/opengl/opengl_renderer_api.cpp b/src/platform/opengl/opengl_renderer_api.cpp new file mode 100644 index 0000000..9fb911a --- /dev/null +++ b/src/platform/opengl/opengl_renderer_api.cpp @@ -0,0 +1,34 @@ +#include "opengl_renderer_api.h" +#include <glad/glad.h> + +namespace Donut +{ + void OpenGLRendererAPI::init() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); + } + + void OpenGLRendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + glViewport(x, y, width, height); + } + + void OpenGLRendererAPI::set_clear_color(const glm::vec4& color) + { + glClearColor(color.r, color.g, color.b, color.a); + } + + void OpenGLRendererAPI::clear() + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + void OpenGLRendererAPI::draw_indexed(const std::shared_ptr<VertexArray>& vertex_array, uint32_t index_count) + { + uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count(); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr); + glBindTexture(GL_TEXTURE_2D, 0); + } +}; diff --git a/src/platform/opengl/opengl_renderer_api.h b/src/platform/opengl/opengl_renderer_api.h new file mode 100644 index 0000000..26e779a --- /dev/null +++ b/src/platform/opengl/opengl_renderer_api.h @@ -0,0 +1,19 @@ +#pragma once + +#include "rendering/renderer.h" +#include <glad/glad.h> + +namespace Donut +{ + class OpenGLRendererAPI + : public RendererAPI + { + public: + virtual void init() override; + virtual void set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; + virtual void set_clear_color(const glm::vec4& color) override; + virtual void clear() override; + + virtual void draw_indexed(const std::shared_ptr<VertexArray>& vertex_array, uint32_t index_count = 0) override; + }; +}; diff --git a/src/platform/opengl/opengl_shader.cpp b/src/platform/opengl/opengl_shader.cpp new file mode 100644 index 0000000..e2e6279 --- /dev/null +++ b/src/platform/opengl/opengl_shader.cpp @@ -0,0 +1,224 @@ +#include "opengl_shader.h" +#include <glad/glad.h> +#include <glm/gtc/type_ptr.hpp> +#include <fstream> +#include <iostream> + +namespace Donut +{ + static uint32_t ShaderTypeFromString(const std::string& type) + { + if (type == "vertex") + return GL_VERTEX_SHADER; + if (type == "fragment" || type == "pixel") + return GL_FRAGMENT_SHADER; + return 0; + } + + OpenGLShader::OpenGLShader(const std::string& filepath) + { + std::string source = read_file(filepath); + auto shader_sources = pre_process(source); + compile(shader_sources); + + auto last_slash = filepath.find_last_of("/\\"); + last_slash = last_slash == std::string::npos ? 0 : last_slash + 1; + auto last_dot = filepath.rfind('.'); + auto count = last_dot == std::string::npos ? filepath.size() - last_slash : last_dot - last_slash; + m_name = filepath.substr(last_slash, count); + } + + OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) + : m_name(name) + { + std::unordered_map<uint32_t, std::string> sources; + sources[GL_VERTEX_SHADER] = vertex_src; + sources[GL_FRAGMENT_SHADER] = fragment_src; + compile(sources); + } + + OpenGLShader::~OpenGLShader() + { + glDeleteProgram(m_renderer_id); + } + + std::string OpenGLShader::read_file(const std::string& filepath) + { + std::string result; + std::ifstream in(filepath, std::ios::in | std::ios::binary); + if (in) { + in.seekg(0, std::ios::end); + size_t size = in.tellg(); + if (size != -1) { + result.resize(size); + in.seekg(0, std::ios::beg); + in.read(&result[0], size); + } + } + return result; + } + + std::unordered_map<uint32_t, std::string> OpenGLShader::pre_process(const std::string& source) + { + std::unordered_map<uint32_t, std::string> shader_sources; + + const char* type_token = "#type"; + size_t type_token_length = strlen(type_token); + size_t pos = source.find(type_token, 0); + while (pos != std::string::npos) { + size_t eol = source.find_first_of("\r\n", pos); + size_t begin = pos + type_token_length + 1; + std::string type = source.substr(begin, eol - begin); + + size_t next_line_pos = source.find_first_not_of("\r\n", eol); + pos = source.find(type_token, next_line_pos); + shader_sources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(next_line_pos) : + source.substr(next_line_pos, pos - next_line_pos); + } + + return shader_sources; + } + + void OpenGLShader::compile(const std::unordered_map<uint32_t, std::string>& shader_sources) + { + uint32_t program = glCreateProgram(); + std::vector<uint32_t> glShaderIDs(shader_sources.size()); + for (auto& kv : shader_sources) + { + uint32_t type = kv.first; + const std::string& source = kv.second; + + uint32_t shader = glCreateShader(type); + const char* source_c_str = source.c_str(); + glShaderSource(shader, 1, &source_c_str, 0); + glCompileShader(shader); + + int is_compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &is_compiled); + if (is_compiled == GL_FALSE) + { + int max_length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length); + std::vector<char> info_log(max_length); + glGetShaderInfoLog(shader, max_length, &max_length, &info_log[0]); + glDeleteShader(shader); + std::cout << "Shader compilation failure!" << std::endl << info_log.data() << std::endl; + return; + } + glAttachShader(program, shader); + glShaderIDs.push_back(shader); + } + + m_renderer_id = program; + glLinkProgram(m_renderer_id); + + int is_linked = 0; + glGetProgramiv(m_renderer_id, GL_LINK_STATUS, (int*)&is_linked); + if (is_linked == GL_FALSE) + { + int max_length = 0; + glGetProgramiv(m_renderer_id, GL_INFO_LOG_LENGTH, &max_length); + std::vector<char> info_log(max_length); + glGetProgramInfoLog(m_renderer_id, max_length, &max_length, &info_log[0]); + glDeleteProgram(m_renderer_id); + for (auto id : glShaderIDs) + glDeleteShader(id); + std::cout << "Shader link failure!" << std::endl << info_log.data() << std::endl; + return; + } + + for (auto id : glShaderIDs) + { + glDetachShader(m_renderer_id, id); + glDeleteShader(id); + } + } + + void OpenGLShader::bind() const + { + glUseProgram(m_renderer_id); + } + + void OpenGLShader::unbind() const + { + glUseProgram(0); + } + + void OpenGLShader::set_int(const std::string& name, int value) + { + upload_uniform_int(name, value); + } + + void OpenGLShader::set_int_array(const std::string& name, int* values, uint32_t count) + { + upload_uniform_int_array(name, values, count); + } + + void OpenGLShader::set_float(const std::string& name, float value) + { + upload_uniform_float(name, value); + } + + void OpenGLShader::set_float3(const std::string& name, const glm::vec3& value) + { + upload_uniform_float3(name, value); + } + + void OpenGLShader::set_float4(const std::string& name, const glm::vec4& value) + { + upload_uniform_float4(name, value); + } + + void OpenGLShader::set_mat4(const std::string& name, const glm::mat4& value) + { + upload_uniform_mat4(name, value); + } + + void OpenGLShader::upload_uniform_int(const std::string& name, int value) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniform1i(location, value); + } + + void OpenGLShader::upload_uniform_int_array(const std::string& name, int* values, uint32_t count) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniform1iv(location, count, values); + } + + void OpenGLShader::upload_uniform_float(const std::string& name, float value) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniform1f(location, value); + } + + void OpenGLShader::upload_uniform_float2(const std::string& name, const glm::vec2& value) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniform2f(location, value.x, value.y); + } + + void OpenGLShader::upload_uniform_float3(const std::string& name, const glm::vec3& value) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniform3f(location, value.x, value.y, value.z); + } + + void OpenGLShader::upload_uniform_float4(const std::string& name, const glm::vec4& value) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniform4f(location, value.x, value.y, value.z, value.w); + } + + void OpenGLShader::upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); + } + + void OpenGLShader::upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) + { + int location = glGetUniformLocation(m_renderer_id, name.c_str()); + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); + } +}; diff --git a/src/platform/opengl/opengl_shader.h b/src/platform/opengl/opengl_shader.h new file mode 100644 index 0000000..e87c839 --- /dev/null +++ b/src/platform/opengl/opengl_shader.h @@ -0,0 +1,47 @@ +#pragma once + +#include "rendering/shader.h" +#include <unordered_map> +#include <glm/glm.hpp> + +namespace Donut +{ + class OpenGLShader + : public Shader + { + public: + OpenGLShader(const std::string& filepath); + OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src); + virtual ~OpenGLShader(); + + virtual void bind() const override; + virtual void unbind() const override; + + virtual void set_int(const std::string& name, int value) override; + virtual void set_int_array(const std::string& name, int* values, uint32_t count) override; + virtual void set_float(const std::string& name, float value) override; + virtual void set_float3(const std::string& name, const glm::vec3& value) override; + virtual void set_float4(const std::string& name, const glm::vec4& value) override; + virtual void set_mat4(const std::string& name, const glm::mat4& value) override; + + virtual const std::string& get_name() const override { return m_name; } + + void upload_uniform_int(const std::string& name, int value); + void upload_uniform_int_array(const std::string& name, int* values, uint32_t count); + void upload_uniform_float(const std::string& name, float value); + void upload_uniform_float2(const std::string& name, const glm::vec2& value); + void upload_uniform_float3(const std::string& name, const glm::vec3& value); + void upload_uniform_float4(const std::string& name, const glm::vec4& value); + void upload_uniform_mat3(const std::string& name, const glm::mat3& matrix); + void upload_uniform_mat4(const std::string& name, const glm::mat4& matrix); + + private: + std::string read_file(const std::string& filepath); + std::unordered_map<uint32_t, std::string> pre_process(const std::string& source); + void compile(const std::unordered_map<uint32_t, std::string>& shader_sources); + + private: + uint32_t m_renderer_id; + std::string m_name; + }; +}; diff --git a/src/platform/opengl/opengl_vertex_array.cpp b/src/platform/opengl/opengl_vertex_array.cpp new file mode 100644 index 0000000..14d4e63 --- /dev/null +++ b/src/platform/opengl/opengl_vertex_array.cpp @@ -0,0 +1,55 @@ +#include <glad/glad.h> +#include "opengl_vertex_array.h" +#include "rendering/vertex_buffer.h" +#include "rendering/index_buffer.h" + +namespace Donut +{ + OpenGLVertexArray::OpenGLVertexArray() + { + glCreateVertexArrays(1, &m_renderer_id); + } + + OpenGLVertexArray::~OpenGLVertexArray() + { + glDeleteVertexArrays(1, &m_renderer_id); + } + + void OpenGLVertexArray::bind() const + { + glBindVertexArray(m_renderer_id); + } + + void OpenGLVertexArray::unbind() const + { + glBindVertexArray(0); + } + + void OpenGLVertexArray::add_vertex_buffer(const std::shared_ptr<VertexBuffer>& vertex_buffer) + { + glBindVertexArray(m_renderer_id); + vertex_buffer->bind(); + + const auto& layout = vertex_buffer->get_layout(); + for (const auto& element : layout.get_elements()) + { + glEnableVertexAttribArray(m_vertex_buffer_index); + glVertexAttribPointer(m_vertex_buffer_index, + element.count, + element.type, + element.normalized ? GL_TRUE : GL_FALSE, + layout.get_stride(), + (const void*)element.offset); + m_vertex_buffer_index++; + } + + m_vertex_buffers.push_back(vertex_buffer); + } + + void OpenGLVertexArray::set_index_buffer(const std::shared_ptr<IndexBuffer>& index_buffer) + { + glBindVertexArray(m_renderer_id); + index_buffer->bind(); + m_index_buffer = index_buffer; + } +}; diff --git a/src/platform/opengl/opengl_vertex_array.h b/src/platform/opengl/opengl_vertex_array.h new file mode 100644 index 0000000..e1a02f6 --- /dev/null +++ b/src/platform/opengl/opengl_vertex_array.h @@ -0,0 +1,40 @@ +#pragma once + +#include "rendering/vertex_array.h" +#include <vector> +#include <memory> + +namespace Donut +{ + class VertexBuffer; + class IndexBuffer; + + class OpenGLVertexArray + : public VertexArray + { + public: + OpenGLVertexArray(); + virtual ~OpenGLVertexArray(); + + virtual void bind() const override; + virtual void unbind() const override; + + virtual void add_vertex_buffer(const std::shared_ptr<VertexBuffer>& vertex_buffer) override; + virtual void set_index_buffer(const std::shared_ptr<IndexBuffer>& index_buffer) override; + + virtual const std::vector<std::shared_ptr<VertexBuffer>>& get_vertex_buffers() const override + { + return m_vertex_buffers; + } + + virtual const std::shared_ptr<IndexBuffer>& get_index_buffer() const override + { + return m_index_buffer; + } + private: + uint32_t m_renderer_id; + uint32_t m_vertex_buffer_index = 0; + std::vector<std::shared_ptr<VertexBuffer>> m_vertex_buffers; + std::shared_ptr<IndexBuffer> m_index_buffer; + }; +}; diff --git a/src/platform/opengl/opengl_vertex_buffer.cpp b/src/platform/opengl/opengl_vertex_buffer.cpp new file mode 100644 index 0000000..76b620a --- /dev/null +++ b/src/platform/opengl/opengl_vertex_buffer.cpp @@ -0,0 +1,34 @@ +#include "opengl_vertex_buffer.h" +#include "rendering/vertex_buffer.h" +#include <glad/glad.h> + +namespace Donut +{ + OpenGLVertexBuffer::OpenGLVertexBuffer(const void* data, uint32_t size) + { + glCreateBuffers(1, &m_renderer_id); + glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id); + glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); + } + + OpenGLVertexBuffer::~OpenGLVertexBuffer() + { + glDeleteBuffers(1, &m_renderer_id); + } + + void OpenGLVertexBuffer::bind() const + { + glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id); + } + + void OpenGLVertexBuffer::unbind() const + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + void OpenGLVertexBuffer::set_data(const void* data, uint32_t size) + { + glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id); + glBufferSubData(GL_ARRAY_BUFFER, 0, size, data); + } +}; diff --git a/src/platform/opengl/opengl_vertex_buffer.h b/src/platform/opengl/opengl_vertex_buffer.h new file mode 100644 index 0000000..bdc0645 --- /dev/null +++ b/src/platform/opengl/opengl_vertex_buffer.h @@ -0,0 +1,25 @@ +#pragma once + +#include "rendering/vertex_buffer.h" + +namespace Donut +{ + class OpenGLVertexBuffer + : public VertexBuffer + { + public: + OpenGLVertexBuffer(const void* data, uint32_t size); + virtual ~OpenGLVertexBuffer(); + + virtual void bind() const override; + virtual void unbind() const override; + virtual void set_data(const void* data, uint32_t size) override; + + virtual const VertexBufferLayout& get_layout() const override { return m_layout; } + virtual void set_layout(const VertexBufferLayout& layout) override { m_layout = layout; } + + private: + uint32_t m_renderer_id; + VertexBufferLayout m_layout; + }; +}; diff --git a/src/rendering/index_buffer.cpp b/src/rendering/index_buffer.cpp new file mode 100644 index 0000000..d4bd9c0 --- /dev/null +++ b/src/rendering/index_buffer.cpp @@ -0,0 +1,17 @@ +#include "index_buffer.h" +#include "renderer.h" +#include "platform/opengl/opengl_index_buffer.h" + +namespace Donut +{ + IndexBuffer* IndexBuffer::create(const uint32_t* indices, uint32_t count) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return new OpenGLIndexBuffer(indices, count); + default: + return nullptr; + } + } +}; diff --git a/src/rendering/index_buffer.h b/src/rendering/index_buffer.h new file mode 100644 index 0000000..3003676 --- /dev/null +++ b/src/rendering/index_buffer.h @@ -0,0 +1,18 @@ +#pragma once + +#include <cstdint> + +namespace Donut +{ + class IndexBuffer + { + public: + virtual ~IndexBuffer() = default; + + virtual void bind() const = 0; + virtual void unbind() const = 0; + virtual uint32_t get_count() const = 0; + + static IndexBuffer* create(const uint32_t* indices, uint32_t count); + }; +}; diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp new file mode 100644 index 0000000..1da5034 --- /dev/null +++ b/src/rendering/renderer.cpp @@ -0,0 +1,61 @@ +#include "renderer.h" +#include "platform/opengl/opengl_renderer_api.h" +#include <memory> +#include <glm/gtc/matrix_transform.hpp> + +namespace Donut { + + Scope<RendererAPI> RendererAPI::create() { + switch (s_api) { + case API::OpenGL: + return std::make_unique<OpenGLRendererAPI>(); + default: + return nullptr; + } + } + + RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL; + + Renderer::SceneData* Renderer::s_scene_data = new Renderer::SceneData; + + void Renderer::init() { + RenderCommand::init(); + } + + void Renderer::shutdown() { + delete s_scene_data; + } + + void Renderer::on_window_resize(uint32_t width, uint32_t height) { + RenderCommand::set_viewport(0, 0, width, height); + } + + void Renderer::begin_scene() { + s_scene_data->ViewProjectionMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + } + + void Renderer::end_scene() { + } + + void Renderer::submit(const std::shared_ptr<Shader>& shader, + const std::shared_ptr<VertexArray>& vertex_array, + const glm::mat4& transform) { + shader->bind(); + shader->set_mat4("u_ViewProjection", s_scene_data->ViewProjectionMatrix); + shader->set_mat4("u_Transform", transform); + + vertex_array->bind(); + RenderCommand::draw_indexed(vertex_array); + } + + Scope<RendererAPI> RenderCommand::s_renderer_api = RendererAPI::create(); + + void Renderer::set_clear_color(const glm::vec4& color) { + RenderCommand::set_clear_color(color); + } + + void Renderer::clear() { + RenderCommand::clear(); + } + +} diff --git a/src/rendering/renderer.h b/src/rendering/renderer.h new file mode 100644 index 0000000..fd61914 --- /dev/null +++ b/src/rendering/renderer.h @@ -0,0 +1,100 @@ +#pragma once + +#include "vertex_array.h" +#include "shader.h" +#include <glm/glm.hpp> +#include <memory> + +namespace Donut +{ + template<typename T> + using Scope = std::unique_ptr<T>; + + template<typename T, typename ... Args> + constexpr Scope<T> create_scope(Args&& ... args) + { + return std::make_unique<T>(std::forward<Args>(args)...); + } + + class RendererAPI + { + public: + enum class API + { + None = 0, + OpenGL = 1, + Vulkan = 2, + }; + + public: + virtual ~RendererAPI() = default; + + virtual void init() = 0; + virtual void set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0; + virtual void set_clear_color(const glm::vec4& color) = 0; + virtual void clear() = 0; + + virtual void draw_indexed(const std::shared_ptr<VertexArray>& vertex_array, uint32_t index_count = 0) = 0; + + inline static API get_api() { return s_api; } + static Scope<RendererAPI> create(); + + private: + static API s_api; + }; + + class RenderCommand + { + public: + inline static void init() { + s_renderer_api->init(); + } + + inline static void set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + s_renderer_api->set_viewport(x, y, width, height); + } + + inline static void set_clear_color(const glm::vec4& color) { + s_renderer_api->set_clear_color(color); + } + + inline static void clear() { + s_renderer_api->clear(); + } + + inline static void draw_indexed(const std::shared_ptr<VertexArray>& vertex_array, uint32_t index_count = 0) { + s_renderer_api->draw_indexed(vertex_array, index_count); + } + + private: + static Scope<RendererAPI> s_renderer_api; + }; + + class Renderer + { + public: + static void init(); + static void shutdown(); + + static void on_window_resize(uint32_t width, uint32_t height); + + static void begin_scene(); + static void end_scene(); + + static void submit(const std::shared_ptr<Shader>& shader, + const std::shared_ptr<VertexArray>& vertex_array, + const glm::mat4& transform = glm::mat4(1.0f)); + + static void set_clear_color(const glm::vec4& color); + static void clear(); + + inline static RendererAPI::API get_api() { return RendererAPI::get_api(); } + private: + struct SceneData + { + glm::mat4 ViewProjectionMatrix; + }; + + static SceneData* s_scene_data; + }; +}; diff --git a/src/rendering/shader.cpp b/src/rendering/shader.cpp new file mode 100644 index 0000000..10f6c49 --- /dev/null +++ b/src/rendering/shader.cpp @@ -0,0 +1,66 @@ +#include "shader.h" +#include "renderer.h" +#include "platform/opengl/opengl_shader.h" +#include <memory> + +namespace Donut +{ + Shader* Shader::create(const std::string& filepath) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return new OpenGLShader(filepath); + default: + return nullptr; + } + } + + Shader* Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return new OpenGLShader(name, vertex_src, fragment_src); + default: + return nullptr; + } + } + + void ShaderLibrary::add(const std::shared_ptr<Shader>& shader) + { + auto& name = shader->get_name(); + add(name, shader); + } + + void ShaderLibrary::add(const std::string& name, const std::shared_ptr<Shader>& shader) + { + m_shaders[name] = shader; + } + + std::shared_ptr<Shader> ShaderLibrary::load(const std::string& filepath) + { + auto shader = std::shared_ptr<Shader>(Shader::create(filepath)); + add(shader); + return shader; + } + + std::shared_ptr<Shader> ShaderLibrary::load(const std::string& name, const std::string& filepath) + { + auto shader = std::shared_ptr<Shader>(Shader::create(filepath)); + add(name, shader); + return shader; + } + + std::shared_ptr<Shader> ShaderLibrary::get(const std::string& name) + { + if (exists(name)) + return m_shaders[name]; + return nullptr; + } + + bool ShaderLibrary::exists(const std::string& name) const + { + return m_shaders.find(name) != m_shaders.end(); + } +}; diff --git a/src/rendering/shader.h b/src/rendering/shader.h new file mode 100644 index 0000000..d9128f6 --- /dev/null +++ b/src/rendering/shader.h @@ -0,0 +1,46 @@ +#pragma once + +#include <string> +#include <unordered_map> +#include <memory> +#include <glm/glm.hpp> + +namespace Donut +{ + class Shader + { + public: + virtual ~Shader() = default; + + virtual void bind() const = 0; + virtual void unbind() const = 0; + + virtual void set_int(const std::string& name, int value) = 0; + virtual void set_int_array(const std::string& name, int* values, uint32_t count) = 0; + virtual void set_float(const std::string& name, float value) = 0; + virtual void set_float3(const std::string& name, const glm::vec3& value) = 0; + virtual void set_float4(const std::string& name, const glm::vec4& value) = 0; + virtual void set_mat4(const std::string& name, const glm::mat4& value) = 0; + + virtual const std::string& get_name() const = 0; + + static Shader* create(const std::string& filepath); + static Shader* create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src); + }; + + class ShaderLibrary + { + public: + void add(const std::shared_ptr<Shader>& shader); + void add(const std::string& name, const std::shared_ptr<Shader>& shader); + std::shared_ptr<Shader> load(const std::string& filepath); + std::shared_ptr<Shader> load(const std::string& name, const std::string& filepath); + + std::shared_ptr<Shader> get(const std::string& name); + + bool exists(const std::string& name) const; + + private: + std::unordered_map<std::string, std::shared_ptr<Shader>> m_shaders; + }; +}; diff --git a/src/rendering/vertex_array.cpp b/src/rendering/vertex_array.cpp new file mode 100644 index 0000000..48eeb5a --- /dev/null +++ b/src/rendering/vertex_array.cpp @@ -0,0 +1,28 @@ +#include "vertex_array.h" +#include "renderer.h" +#include "platform/opengl/opengl_vertex_array.h" + +namespace Donut +{ + uint32_t VertexBufferElement::get_size_of_type(uint32_t type) + { + switch (type) + { + case 0x1406: return 4; + case 0x1405: return 4; + case 0x1401: return 1; + default: return 0; + } + } + + VertexArray* VertexArray::create() + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return new OpenGLVertexArray(); + default: + return nullptr; + } + } +}; diff --git a/src/rendering/vertex_array.h b/src/rendering/vertex_array.h new file mode 100644 index 0000000..b51db84 --- /dev/null +++ b/src/rendering/vertex_array.h @@ -0,0 +1,26 @@ +#pragma once + +#include "vertex_buffer.h" +#include "index_buffer.h" +#include <memory> +#include <vector> + +namespace Donut +{ + class VertexArray + { + public: + virtual ~VertexArray() = default; + + virtual void bind() const = 0; + virtual void unbind() const = 0; + + virtual void add_vertex_buffer(const std::shared_ptr<VertexBuffer>& vertex_buffer) = 0; + virtual void set_index_buffer(const std::shared_ptr<IndexBuffer>& index_buffer) = 0; + + virtual const std::vector<std::shared_ptr<VertexBuffer>>& get_vertex_buffers() const = 0; + virtual const std::shared_ptr<IndexBuffer>& get_index_buffer() const = 0; + + static VertexArray* create(); + }; +}; diff --git a/src/rendering/vertex_buffer.cpp b/src/rendering/vertex_buffer.cpp new file mode 100644 index 0000000..a349e78 --- /dev/null +++ b/src/rendering/vertex_buffer.cpp @@ -0,0 +1,17 @@ +#include "vertex_buffer.h" +#include "renderer.h" +#include "platform/opengl/opengl_vertex_buffer.h" + +namespace Donut +{ + VertexBuffer* VertexBuffer::create(const void* data, uint32_t size) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return new OpenGLVertexBuffer(data, size); + default: + return nullptr; + } + } +}; diff --git a/src/rendering/vertex_buffer.h b/src/rendering/vertex_buffer.h new file mode 100644 index 0000000..1d0b621 --- /dev/null +++ b/src/rendering/vertex_buffer.h @@ -0,0 +1,73 @@ +#pragma once + +#include <cstdint> +#include <vector> + +namespace Donut +{ + struct VertexBufferElement + { + uint32_t type; + uint32_t count; + uint8_t normalized; + uint32_t offset; + + static uint32_t get_size_of_type(uint32_t type); + }; + + class VertexBufferLayout + { + public: + VertexBufferLayout() = default; + ~VertexBufferLayout() = default; + + template<typename T> + void push(uint32_t count) + { + static_assert(false); + } + + template<> + void push<float>(uint32_t count) + { + m_elements.push_back({ 0x1406, count, 0, m_stride }); + m_stride += count * VertexBufferElement::get_size_of_type(0x1406); + } + + template<> + void push<uint32_t>(uint32_t count) + { + m_elements.push_back({ 0x1405, count, 0, m_stride }); + m_stride += count * VertexBufferElement::get_size_of_type(0x1405); + } + + template<> + void push<uint8_t>(uint32_t count) + { + m_elements.push_back({ 0x1401, count, 1, m_stride }); + m_stride += count * VertexBufferElement::get_size_of_type(0x1401); + } + + inline const std::vector<VertexBufferElement>& get_elements() const { return m_elements; } + inline uint32_t get_stride() const { return m_stride; } + + private: + std::vector<VertexBufferElement> m_elements; + uint32_t m_stride = 0; + }; + + class VertexBuffer + { + public: + virtual ~VertexBuffer() = default; + + virtual void bind() const = 0; + virtual void unbind() const = 0; + virtual void set_data(const void* data, uint32_t size) = 0; + + virtual const VertexBufferLayout& get_layout() const = 0; + virtual void set_layout(const VertexBufferLayout& layout) = 0; + + static VertexBuffer* create(const void* data, uint32_t size); + }; +}; |
