chipd8/source/gl.d

345 lines
7.8 KiB
D
Raw Normal View History

2018-10-03 23:36:40 +01:00
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