From f6cfbcc406875986c3f39314800f6c98200a07d9 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 14 Jan 2024 20:24:47 -0500 Subject: [PATCH] Add gltk --- Rsconscript | 1 + libs/gltk/gltk/array.d | 33 +++++++ libs/gltk/gltk/buffer.d | 48 +++++++++++ libs/gltk/gltk/package.d | 7 ++ libs/gltk/gltk/program.d | 182 +++++++++++++++++++++++++++++++++++++++ libs/gltk/gltk/shader.d | 81 +++++++++++++++++ libs/gltk/gltk/texture.d | 45 ++++++++++ src/app.d | 3 +- src/gl.d | 3 + 9 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 libs/gltk/gltk/array.d create mode 100644 libs/gltk/gltk/buffer.d create mode 100644 libs/gltk/gltk/package.d create mode 100644 libs/gltk/gltk/program.d create mode 100644 libs/gltk/gltk/shader.d create mode 100644 libs/gltk/gltk/texture.d create mode 100644 src/gl.d diff --git a/Rsconscript b/Rsconscript index 04185ba..576eba1 100644 --- a/Rsconscript +++ b/Rsconscript @@ -8,6 +8,7 @@ env "app" do |env| "libs/bindbc-common/source", "libs/bindbc-sdl/source", "libs/glad", + "libs/gltk", ] sources = dirs.reduce([]) do |result, dir| result + glob("#{dir}/**/*.{d,c}") diff --git a/libs/gltk/gltk/array.d b/libs/gltk/gltk/array.d new file mode 100644 index 0000000..be36f1d --- /dev/null +++ b/libs/gltk/gltk/array.d @@ -0,0 +1,33 @@ +module gltk.array; + +import gl; + +class Array +{ + private GLuint m_id; + + this() + { + m_id = 0u; + glGenVertexArrays(1, &m_id); + if (m_id == 0u) + { + throw new Exception("Failed to allocate an OpenGL array"); + } + } + + ~this() + { + glDeleteVertexArrays(1, &m_id); + } + + void bind() + { + glBindVertexArray(m_id); + } + + @property GLuint id() + { + return m_id; + } +} diff --git a/libs/gltk/gltk/buffer.d b/libs/gltk/gltk/buffer.d new file mode 100644 index 0000000..83d4cfb --- /dev/null +++ b/libs/gltk/gltk/buffer.d @@ -0,0 +1,48 @@ +module gltk.buffer; + +import gl; + +class Buffer +{ + private GLuint m_id; + + private GLenum m_target; + + this(GLenum target) + { + m_id = 0u; + glGenBuffers(1, &m_id); + if (m_id == 0u) + { + throw new Exception("Failed to allocate an OpenGL buffer"); + } + m_target = target; + } + + ~this() + { + glDeleteBuffers(1, &m_id); + } + + void bind() + { + glBindBuffer(m_target, m_id); + } + + @property GLuint id() + { + return m_id; + } + + void set_buffer_data(GLenum usage, const void * ptr, size_t size) + { + bind(); + glBufferData(m_target, size, ptr, usage); + } + + void set_buffer_data(T)(GLenum usage, T[] arr) + { + bind(); + glBufferData(m_target, arr.length * arr[0].sizeof, arr.ptr, usage); + } +} diff --git a/libs/gltk/gltk/package.d b/libs/gltk/gltk/package.d new file mode 100644 index 0000000..13ba04f --- /dev/null +++ b/libs/gltk/gltk/package.d @@ -0,0 +1,7 @@ +module gltk; + +public import gltk.array; +public import gltk.buffer; +public import gltk.program; +public import gltk.shader; +public import gltk.texture; diff --git a/libs/gltk/gltk/program.d b/libs/gltk/gltk/program.d new file mode 100644 index 0000000..670bacd --- /dev/null +++ b/libs/gltk/gltk/program.d @@ -0,0 +1,182 @@ +module gltk.program; + +import gltk.shader; +import std.string; +import gl; + +class Program(uniforms...) +{ + /* The number of template parameters must be a multiple of 2. */ + static assert((uniforms.length % 2) == 0); + + private GLuint m_id; + + static foreach (i, v; uniforms) + { + static if ((i % 2) == 0) + { + mixin("private GLint m_uniform_" ~ v ~ ";"); + } + } + + this(Args...)(Args args) + { + m_id = glCreateProgram(); + if (m_id == 0u) + { + throw new Exception("Failed to allocate an OpenGL program"); + } + static if (args.length > 0u) + { + build(args); + } + } + + ~this() + { + glDeleteProgram(m_id); + } + + void attach_shader(Shader shader) const + { + glAttachShader(m_id, shader.id); + } + + void bind_attrib_location(string name, uint index) const + { + glBindAttribLocation(m_id, index, name.toStringz()); + } + + void link() + { + glLinkProgram(m_id); + + GLint link_status; + glGetProgramiv(m_id, GL_LINK_STATUS, &link_status); + if (link_status != GL_TRUE) + { + string message = "Failed to link program"; + GLint log_length = 0; + glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) + { + char[] log = new char[log_length]; + glGetProgramInfoLog(m_id, log_length, &log_length, log.ptr); + message ~= "\n"; + message ~= log; + message ~= "\n"; + } + throw new Exception(message); + } + + static foreach(i, v; uniforms) + { + static if ((i % 2) == 0) + { + mixin("m_uniform_" ~ v ~ " = glGetUniformLocation(m_id, \"" ~ v ~ "\");"); + } + } + } + + GLint get_uniform_location(string uniform_name) const + { + return glGetUniformLocation(m_id, uniform_name.toStringz()); + } + + void get_uniform_locations(Args...)(string uniform_name, GLint * uniform_location, Args args) const + { + *uniform_location = get_uniform_location(uniform_name); + static if (args.length > 0u) + { + get_uniform_locations(args); + } + } + + @property GLuint id() const + { + return m_id; + } + + void use() const + { + glUseProgram(m_id); + } + + private static string uniform_param_decl_list(string spec) + { + int count = spec[0] - '0'; + string type; + if (spec[1] == 'i') + { + type = "GLint"; + } + else if (spec[1..2] == "ui") + { + type = "GLuint"; + } + else if (spec[1] == 'f') + { + type = "GLfloat"; + } + assert(type != ""); + if (spec[$ - 1] == 'v') + { + return "uint count, const " ~ type ~ " * v"; + } + else + { + string[] decls; + for (int i = 0; i < count; i++) + { + decls ~= type ~ " v" ~ cast(char)('0' + i); + } + return decls.join(", "); + } + } + + private static string uniform_param_list(string spec) + { + int count = spec[0] - '0'; + if (spec[$ - 1] == 'v') + { + return "count, v"; + } + else + { + string[] vees; + for (int i = 0; i < count; i++) + { + vees ~= "v" ~ cast(char)('0' + i); + } + return vees.join(", "); + } + } + + static foreach (i, v; uniforms) + { + static if ((i % 2) == 0) + { + mixin("void set_" ~ v ~ "(" ~ uniform_param_decl_list(uniforms[i + 1]) ~ ")" ~ + "{" ~ + " glUniform" ~ uniforms[i + 1] ~ "(m_uniform_" ~ v ~ ", " ~ uniform_param_list(uniforms[i + 1]) ~ ");" ~ + "}"); + } + } + + private void build(Args...)(Shader s, Args args) + { + attach_shader(s); + build(args); + } + + private void build(Args...)(string attrib_name, uint index, Args args) + { + bind_attrib_location(attrib_name, index); + build(args); + } + + private void build() + { + link(); + } +} diff --git a/libs/gltk/gltk/shader.d b/libs/gltk/gltk/shader.d new file mode 100644 index 0000000..30fc62d --- /dev/null +++ b/libs/gltk/gltk/shader.d @@ -0,0 +1,81 @@ +module gltk.shader; + +import gl; +static import std.file; + +class Shader +{ + private GLuint m_id; + + private GLenum m_shader_type; + + this(GLenum shader_type) + { + m_shader_type = shader_type; + m_id = glCreateShader(shader_type); + if (m_id == 0u) + { + throw new Exception("Failed to allocate an OpenGL shader"); + } + } + + ~this() + { + glDeleteShader(m_id); + } + + void set_source(const char * source, int length = -1) + { + GLint status; + + GLint gllength = length; + glShaderSource(m_id, 1, &source, &gllength); + + glCompileShader(m_id); + + glGetShaderiv(m_id, GL_COMPILE_STATUS, &status); + if (status == GL_TRUE) + { + return; + } + + string message = "Error compiling "; + switch (m_shader_type) + { + case GL_VERTEX_SHADER: + message ~= "vertex"; + break; + case GL_FRAGMENT_SHADER: + message ~= "fragment"; + break; + default: + message ~= "unknown"; + break; + } + message ~= " shader"; + + GLint log_length; + glGetShaderiv(m_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) + { + char[] log = new char[log_length]; + glGetShaderInfoLog(m_id, log_length, &log_length, log.ptr); + message ~= "\nShader Log:\n"; + message ~= log; + message ~= "\n"; + } + glDeleteShader(m_id); + throw new Exception(message); + } + + void set_source_from_file(string filename) + { + const(char)[] file_data = cast(const(char)[])std.file.read(filename); + set_source(file_data.ptr, cast(int)file_data.length); + } + + @property GLuint id() + { + return m_id; + } +} diff --git a/libs/gltk/gltk/texture.d b/libs/gltk/gltk/texture.d new file mode 100644 index 0000000..73a9b4d --- /dev/null +++ b/libs/gltk/gltk/texture.d @@ -0,0 +1,45 @@ +module gltk.texture; + +import gl; + +class Texture +{ + private GLuint m_id; + + this() + { + m_id = 0u; + glGenTextures(1, &m_id); + if (m_id == 0u) + { + throw new Exception("Failed to allocate an OpenGL texture"); + } + } + + ~this() + { + glDeleteTextures(1, &m_id); + } + + void bind(GLenum target) const + { + glBindTexture(target, m_id); + } + + @property GLuint id() + { + return m_id; + } + + static uint next_power_of_2(uint n) + { + n--; + n |= n >> 1u; + n |= n >> 2u; + n |= n >> 4u; + n |= n >> 8u; + n |= n >> 16u; + n++; + return n; + } +}; diff --git a/src/app.d b/src/app.d index 912a81a..87cb9d2 100644 --- a/src/app.d +++ b/src/app.d @@ -1,7 +1,8 @@ import std.stdio; import sdl; -import glad.gl.all; +import gl; import glad.gl.loader; +import gltk; enum int WIDTH = 800; enum int HEIGHT = 600; diff --git a/src/gl.d b/src/gl.d new file mode 100644 index 0000000..069c86b --- /dev/null +++ b/src/gl.d @@ -0,0 +1,3 @@ +module gl; + +public import glad.gl.all;