345 lines
7.8 KiB
D
345 lines
7.8 KiB
D
module gl;
|
|
|
|
import glad.gl.all;
|
|
import core.stdc.stdio;
|
|
|
|
bool checkShaderError(GLuint shader, GLuint flag, bool is_program, in char[] shader_path) nothrow {
|
|
|
|
GLint result;
|
|
|
|
(is_program) ? glGetProgramiv(shader, flag, &result)
|
|
: glGetShaderiv(shader, flag, &result);
|
|
|
|
if (result == GL_FALSE) {
|
|
|
|
GLchar[256] log; //FIXME this is potentially fatal
|
|
(is_program) ? glGetProgramInfoLog(shader, log.sizeof, null, log.ptr)
|
|
: glGetShaderInfoLog(shader, log.sizeof, null, log.ptr);
|
|
|
|
printf("[OpenGL] Error in %s: %s\n", shader_path.ptr, log.ptr);
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} // checkShaderError
|
|
|
|
struct PixelBuffer {
|
|
|
|
struct Shader {
|
|
|
|
private {
|
|
GLuint shader_prog;
|
|
}
|
|
|
|
@disable this(this);
|
|
|
|
void compile(const (GLchar*)* vs_source, const (GLchar*)* fs_source) {
|
|
|
|
import core.stdc.stdlib : exit;
|
|
|
|
GLuint new_vs_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
glShaderSource(new_vs_shader, 1, vs_source, null);
|
|
glCompileShader(new_vs_shader);
|
|
if (!checkShaderError(new_vs_shader, GL_COMPILE_STATUS, false, "vertex_shader")) {
|
|
exit(-1);
|
|
}
|
|
|
|
GLuint new_fs_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glShaderSource(new_fs_shader, 1, fs_source, null);
|
|
glCompileShader(new_fs_shader);
|
|
if (!checkShaderError(new_fs_shader, GL_COMPILE_STATUS, false, "fragment_shader")) {
|
|
exit(-1);
|
|
}
|
|
|
|
GLuint new_shader = glCreateProgram();
|
|
glAttachShader(new_shader, new_vs_shader);
|
|
glAttachShader(new_shader, new_fs_shader);
|
|
|
|
// glBindAttribLocation(new_shader, 0, "screen_size");
|
|
|
|
glLinkProgram(new_shader);
|
|
if (!checkShaderError(new_shader, GL_LINK_STATUS, true, "shader_program")) {
|
|
exit(-1);
|
|
}
|
|
|
|
glValidateProgram(new_shader);
|
|
if (!checkShaderError(new_shader, GL_VALIDATE_STATUS, true, "shader_program")) {
|
|
exit(-1);
|
|
}
|
|
|
|
shader_prog = new_shader;
|
|
|
|
} // compile
|
|
|
|
void setUniforms(int w, int h) {
|
|
auto attr_loc = glGetUniformLocation(shader_prog, "screen_size");
|
|
glUniform2f(attr_loc, cast(float)w, cast(float)h);
|
|
} // setUniforms
|
|
|
|
void bind() {
|
|
glUseProgram(shader_prog);
|
|
} // bind
|
|
|
|
void unbind() {
|
|
glUseProgram(0);
|
|
} // unbind
|
|
|
|
} // Shader
|
|
|
|
struct Texture {
|
|
|
|
import derelict.sdl2.sdl;
|
|
import derelict.sdl2.image;
|
|
|
|
private {
|
|
|
|
GLuint texture_; //OpenGL handle for texture
|
|
GLenum input_format_, output_format_, data_type_;
|
|
int width_, height_;
|
|
|
|
}
|
|
|
|
@property @nogc nothrow {
|
|
|
|
int width() const { return width_; }
|
|
int height() const { return height_; }
|
|
GLuint handle() { return texture_; }
|
|
|
|
}
|
|
|
|
@disable this(this);
|
|
|
|
nothrow @nogc
|
|
this(int width, int height, GLenum input_format, GLenum output_format, GLenum unpack_alignment) {
|
|
|
|
width_ = width;
|
|
height_ = height;
|
|
input_format_ = input_format;
|
|
output_format_ = output_format;
|
|
data_type_ = GL_UNSIGNED_BYTE;
|
|
|
|
glGenTextures(1, &texture_);
|
|
glBindTexture(GL_TEXTURE_2D, texture_);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, GL_UNSIGNED_BYTE, cast(void*)0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
} //this
|
|
|
|
nothrow @nogc
|
|
void create(void* pixels, int width, int height, GLenum input_format = GL_RGB, GLenum output_format = GL_RGB, GLenum data_type = GL_UNSIGNED_BYTE) {
|
|
|
|
width_ = width;
|
|
height_ = height;
|
|
input_format_ = input_format;
|
|
output_format_ = output_format;
|
|
data_type_ = data_type;
|
|
|
|
//generate single texture, put handle in texture
|
|
glGenTextures(1, &texture_);
|
|
|
|
//normal 2d texture, bind to our texture handle
|
|
glBindTexture(GL_TEXTURE_2D, texture_);
|
|
|
|
//set texture parameters in currently bound texture, controls texture wrapping (or GL_CLAMP?)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
//linearly interpolate between pixels, MIN if texture is too small for drawing area, MAG if drawing area is smaller than texture
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
//texture type, level, format to store as, width, height, border, format loaded in
|
|
glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, data_type_, pixels);
|
|
|
|
//UNBIND
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
} // this
|
|
|
|
~this() nothrow @nogc {
|
|
|
|
if (texture_ != 0) {
|
|
glDeleteTextures(1, &texture_);
|
|
}
|
|
|
|
} // ~this
|
|
|
|
/**
|
|
* Binds the texture handle, takes an argument for which texture unit to use.
|
|
*/
|
|
nothrow @nogc
|
|
void bind(int unit) {
|
|
|
|
assert(unit >= 0 && unit <= 31);
|
|
glActiveTexture(GL_TEXTURE0 + unit); //since this is sequential, this works
|
|
glBindTexture(GL_TEXTURE_2D, texture_);
|
|
|
|
} // bind
|
|
|
|
nothrow @nogc
|
|
void unbind() {
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
} // unbind
|
|
|
|
/**
|
|
* Updates the texture in place given the new texture buffer.
|
|
* Takes an optional offset to update only a part of the texture.
|
|
**/
|
|
nothrow @nogc
|
|
void update(void[] pixels, size_t offset = 0) {
|
|
|
|
bind(0);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, input_format_, data_type_, pixels.ptr);
|
|
unbind();
|
|
|
|
} // update
|
|
|
|
} // Texture
|
|
|
|
/**
|
|
* Generic VertexArray structure, used to upload data of any given vertex type to the GPU.
|
|
*/
|
|
struct VertexArray {
|
|
|
|
private {
|
|
|
|
GLuint vao_;
|
|
GLuint vbo_;
|
|
GLenum type_; // type of vertex data, GL_TRIANGLES etc
|
|
uint num_vertices_;
|
|
|
|
}
|
|
|
|
@disable this(this);
|
|
|
|
nothrow @nogc
|
|
void create(in float[2][] vertices, GLenum draw_type = GL_STATIC_DRAW, GLenum primitive = GL_TRIANGLES) {
|
|
|
|
this.num_vertices_ = cast(uint)vertices.length;
|
|
this.type_ = primitive;
|
|
|
|
glGenVertexArrays(1, &vao_);
|
|
glBindVertexArray(vao_);
|
|
|
|
glGenBuffers(1, &vbo_);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
|
glBufferData(GL_ARRAY_BUFFER, vertices.length * vertices[0].sizeof, vertices.ptr, draw_type);
|
|
|
|
// pos
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0,
|
|
2,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
float.sizeof * 2,
|
|
null
|
|
);
|
|
|
|
glBindVertexArray(0);
|
|
|
|
} // this
|
|
|
|
~this() nothrow @nogc {
|
|
if (vao_ != 0) {
|
|
glDeleteVertexArrays(1, &vao_);
|
|
}
|
|
} // ~this
|
|
|
|
void bind() nothrow @nogc {
|
|
glBindVertexArray(vao_);
|
|
} // bind
|
|
|
|
void draw() nothrow @nogc {
|
|
glDrawArrays(type_, 0, num_vertices_);
|
|
} // draw
|
|
|
|
void unbind() nothrow @nogc {
|
|
glBindVertexArray(0);
|
|
} // unbind
|
|
|
|
} // VertexArray
|
|
|
|
const char* vs_shader = q{
|
|
#version 330 core
|
|
|
|
uniform vec2 screen_size;
|
|
|
|
layout(location = 0) in vec2 pos;
|
|
|
|
out vec2 frag_uv;
|
|
|
|
void main() {
|
|
gl_Position = vec4(pos, 0.0, 1.0);
|
|
frag_uv = clamp(pos, vec2(0.0, 0.0), vec2(1.0, 1.0));
|
|
}
|
|
|
|
};
|
|
|
|
const char* fs_shader = q{
|
|
#version 330 core
|
|
|
|
uniform sampler2D tex;
|
|
|
|
in vec2 frag_uv;
|
|
|
|
out vec4 out_col;
|
|
|
|
void main() {
|
|
float v = texture(tex, frag_uv.st).r * 255;
|
|
out_col = vec4(v, v, v, 1.0);
|
|
}
|
|
|
|
};
|
|
|
|
Shader shader;
|
|
VertexArray vao;
|
|
Texture tex;
|
|
|
|
void create(void* pixels, int w, int h) {
|
|
|
|
float[2][6] rect = [
|
|
[-1.0f, -1.0f], // top left
|
|
[1.0f, -1.0f], // top right
|
|
[1.0f, 1.0f], // bottom right
|
|
|
|
[-1.0f, -1.0f], // top left
|
|
[-1.0f, 1.0f], // bottom left
|
|
[1.0f, 1.0f], // bottom right
|
|
];
|
|
|
|
vao.create(rect[]);
|
|
tex.create(pixels, w, h, GL_RED, GL_RED);
|
|
shader.compile(&vs_shader, &fs_shader);
|
|
|
|
} // create
|
|
|
|
void draw(int screen_w, int screen_h) {
|
|
|
|
vao.bind();
|
|
shader.bind();
|
|
shader.setUniforms(screen_w, screen_h);
|
|
tex.bind(0);
|
|
vao.draw();
|
|
|
|
} // draw
|
|
|
|
nothrow @nogc
|
|
void update(void[] pixels, size_t offset = 0) {
|
|
tex.update(pixels, offset);
|
|
} // update
|
|
|
|
} // PixelBuffer
|