Start reworking Program class template
This commit is contained in:
parent
1aa3650ee3
commit
2ec33ff1c0
@ -4,21 +4,187 @@ import gltk.shader;
|
||||
import std.string;
|
||||
import gl;
|
||||
|
||||
class Program(uniforms...)
|
||||
/**
|
||||
* OpenGL program object.
|
||||
*
|
||||
* This template allows creating a program class with specific uniforms and
|
||||
* attributes. Uniforms specified via the uniforms template argument will
|
||||
* generate setter functions to set the uniform values. Attributes specified
|
||||
* via the attributes template argument will bind attribute locations after
|
||||
* the program is linked.
|
||||
*
|
||||
* Uniforms are specified as an associative array with uniform names as keys
|
||||
* and uniform types as values. The uniform types are the strings following
|
||||
* the "glProgramUniform" prefix (for example, "1i", "3f", "Matrix4fv", etc.).
|
||||
*
|
||||
* Attributes are specified as an associative array with attribute names as
|
||||
* keys and attribute locations as values.
|
||||
*/
|
||||
class Program(string[string] uniforms = [], int[string] attributes = [])
|
||||
{
|
||||
/* The number of template parameters must be a multiple of 2. */
|
||||
static assert((uniforms.length % 2) == 0);
|
||||
|
||||
/** Program ID. */
|
||||
private GLuint m_id;
|
||||
|
||||
static foreach (i, v; uniforms)
|
||||
/**
|
||||
* Uniform type attributes.
|
||||
*/
|
||||
private struct UniformTypeInfo
|
||||
{
|
||||
static if ((i % 2) == 0)
|
||||
ubyte count;
|
||||
ubyte count2;
|
||||
char type;
|
||||
bool v;
|
||||
|
||||
public @property bool matrix() const
|
||||
{
|
||||
mixin("private GLint m_uniform_" ~ v ~ ";");
|
||||
return count2 != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uniform type attributes from uniform type string.
|
||||
*/
|
||||
private static UniformTypeInfo uniform_type_info(string uniform_type)
|
||||
{
|
||||
switch (uniform_type)
|
||||
{
|
||||
case "1f": return UniformTypeInfo(1, 0, 'f', false);
|
||||
case "2f": return UniformTypeInfo(2, 0, 'f', false);
|
||||
case "3f": return UniformTypeInfo(3, 0, 'f', false);
|
||||
case "4f": return UniformTypeInfo(4, 0, 'f', false);
|
||||
|
||||
case "1i": return UniformTypeInfo(1, 0, 'i', false);
|
||||
case "2i": return UniformTypeInfo(2, 0, 'i', false);
|
||||
case "3i": return UniformTypeInfo(3, 0, 'i', false);
|
||||
case "4i": return UniformTypeInfo(4, 0, 'i', false);
|
||||
|
||||
case "1ui": return UniformTypeInfo(1, 0, 'u', false);
|
||||
case "2ui": return UniformTypeInfo(2, 0, 'u', false);
|
||||
case "3ui": return UniformTypeInfo(3, 0, 'u', false);
|
||||
case "4ui": return UniformTypeInfo(4, 0, 'u', false);
|
||||
|
||||
case "1fv": return UniformTypeInfo(1, 0, 'f', true);
|
||||
case "2fv": return UniformTypeInfo(2, 0, 'f', true);
|
||||
case "3fv": return UniformTypeInfo(3, 0, 'f', true);
|
||||
case "4fv": return UniformTypeInfo(4, 0, 'f', true);
|
||||
|
||||
case "1iv": return UniformTypeInfo(1, 0, 'i', true);
|
||||
case "2iv": return UniformTypeInfo(2, 0, 'i', true);
|
||||
case "3iv": return UniformTypeInfo(3, 0, 'i', true);
|
||||
case "4iv": return UniformTypeInfo(4, 0, 'i', true);
|
||||
|
||||
case "1uiv": return UniformTypeInfo(1, 0, 'u', true);
|
||||
case "2uiv": return UniformTypeInfo(2, 0, 'u', true);
|
||||
case "3uiv": return UniformTypeInfo(3, 0, 'u', true);
|
||||
case "4uiv": return UniformTypeInfo(4, 0, 'u', true);
|
||||
|
||||
case "Matrix2fv": return UniformTypeInfo(2, 2, 'f', true);
|
||||
case "Matrix3fv": return UniformTypeInfo(3, 3, 'f', true);
|
||||
case "Matrix4fv": return UniformTypeInfo(4, 4, 'f', true);
|
||||
case "Matrix2x3fv": return UniformTypeInfo(2, 3, 'f', true);
|
||||
case "Matrix3x2fv": return UniformTypeInfo(3, 2, 'f', true);
|
||||
case "Matrix2x4fv": return UniformTypeInfo(2, 4, 'f', true);
|
||||
case "Matrix4x2fv": return UniformTypeInfo(4, 2, 'f', true);
|
||||
case "Matrix3x4fv": return UniformTypeInfo(3, 4, 'f', true);
|
||||
case "Matrix4x3fv": return UniformTypeInfo(4, 3, 'f', true);
|
||||
|
||||
default: throw new Exception("Unknown uniform type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uniform setter function parameter declaration list.
|
||||
*/
|
||||
private static string uniform_param_decl_list(string uniform_type)
|
||||
{
|
||||
UniformTypeInfo uti = uniform_type_info(uniform_type);
|
||||
string type;
|
||||
if (uti.type == 'i')
|
||||
{
|
||||
type = "GLint";
|
||||
}
|
||||
else if (uti.type == 'u')
|
||||
{
|
||||
type = "GLuint";
|
||||
}
|
||||
else if (uti.type == 'f')
|
||||
{
|
||||
type = "GLfloat";
|
||||
}
|
||||
assert(type != "");
|
||||
if (uti.v)
|
||||
{
|
||||
return "const(" ~ type ~ ") * v";
|
||||
}
|
||||
string[] decls;
|
||||
for (int i = 0; i < uti.count; i++)
|
||||
{
|
||||
decls ~= type ~ " v" ~ cast(char)('0' + i);
|
||||
}
|
||||
return decls.join(", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uniform setter function argument list.
|
||||
*/
|
||||
private static string uniform_param_list(string uniform_type)
|
||||
{
|
||||
UniformTypeInfo uti = uniform_type_info(uniform_type);
|
||||
if (uti.v)
|
||||
{
|
||||
return "v";
|
||||
}
|
||||
string[] vees;
|
||||
for (int i = 0; i < uti.count; i++)
|
||||
{
|
||||
vees ~= "v" ~ cast(char)('0' + i);
|
||||
}
|
||||
return vees.join(", ");
|
||||
}
|
||||
|
||||
/*
|
||||
* Define uniform setter function for each uniform specified in the
|
||||
* uniforms template parameter.
|
||||
*/
|
||||
static foreach (uniform_name, uniform_type; uniforms)
|
||||
{
|
||||
mixin("private GLint m_uniform_" ~ uniform_name ~ ";");
|
||||
mixin("public void set_" ~ uniform_name ~ "(" ~ uniform_param_decl_list(uniform_type) ~ ") const" ~
|
||||
"{" ~
|
||||
" glProgramUniform" ~ uniform_type ~ "(m_id, m_uniform_" ~ uniform_name ~ ", " ~ (uniform_type_info(uniform_type).v ? "1, " : "") ~ (uniform_type_info(uniform_type).matrix ? "false, " : "") ~ uniform_param_list(uniform_type) ~ ");" ~
|
||||
"}");
|
||||
static if (uniform_type_info(uniform_type).v)
|
||||
{
|
||||
mixin("public void set_" ~ uniform_name ~ "(GLsizei count, " ~ uniform_param_decl_list(uniform_type) ~ ") const" ~
|
||||
"{" ~
|
||||
" glProgramUniform" ~ uniform_type ~ "(m_id, m_uniform_" ~ uniform_name ~ ", count, " ~ (uniform_type_info(uniform_type).matrix ? "false, " : "") ~ uniform_param_list(uniform_type) ~ ");" ~
|
||||
"}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct program object.
|
||||
*
|
||||
* There are multiple ways to construct a program object:
|
||||
*
|
||||
* (1) Pass a single string argument to the constructor. The string
|
||||
* specifies the source for program shaders. A source line matching
|
||||
* "vertex:", "fragment:", "compute:", "tess_control:",
|
||||
* "tess_evaluation:", or "geometry:" begins the source section for
|
||||
* the corresponding shader. A Shader object is automatically created
|
||||
* from the source in its corresponding section. Any source lines
|
||||
* before a line indicating the start of a shader source are copied to
|
||||
* all shaders. This allows, for example, specifying the shader source
|
||||
* version or any uniform declarations that are common to all shaders.
|
||||
* The program is linked after shaders are created and attached.
|
||||
*
|
||||
* (2) Pass one or more Shader objects to the constructor. The program will
|
||||
* attach the given shaders and then link the program.
|
||||
*
|
||||
* (3) Pass no arguments to the constructor. In this case the program will
|
||||
* not yet be linked and the user should call attach_shader() for each
|
||||
* shader to add and then link() to link the program.
|
||||
*/
|
||||
this(Args...)(Args args)
|
||||
{
|
||||
m_id = glCreateProgram();
|
||||
@ -32,22 +198,37 @@ class Program(uniforms...)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruct program object.
|
||||
*/
|
||||
~this()
|
||||
{
|
||||
glDeleteProgram(m_id);
|
||||
}
|
||||
|
||||
void attach_shader(Shader shader) const
|
||||
{
|
||||
glAttachShader(m_id, shader.id);
|
||||
}
|
||||
|
||||
void bind_attrib_location(string name, uint index) const
|
||||
/**
|
||||
* Bind an attribute to a specific location.
|
||||
*/
|
||||
public void bind_attrib_location(string name, uint index) const
|
||||
{
|
||||
glBindAttribLocation(m_id, index, name.toStringz());
|
||||
}
|
||||
|
||||
void link()
|
||||
/**
|
||||
* Attach a Shader object to the program.
|
||||
*/
|
||||
public void attach_shader(Shader shader) const
|
||||
{
|
||||
glAttachShader(m_id, shader.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link the program.
|
||||
*
|
||||
* This function should only be called if no arguments are passed to the
|
||||
* constructor.
|
||||
*/
|
||||
public void link()
|
||||
{
|
||||
glLinkProgram(m_id);
|
||||
|
||||
@ -69,21 +250,28 @@ class Program(uniforms...)
|
||||
throw new Exception(message);
|
||||
}
|
||||
|
||||
static foreach(i, v; uniforms)
|
||||
static foreach(uniform_name, uniform_type; uniforms)
|
||||
{
|
||||
static if ((i % 2) == 0)
|
||||
{
|
||||
mixin("m_uniform_" ~ v ~ " = glGetUniformLocation(m_id, \"" ~ v ~ "\");");
|
||||
mixin("m_uniform_" ~ uniform_name ~ " = glGetUniformLocation(m_id, \"" ~ uniform_name ~ "\");");
|
||||
}
|
||||
static foreach(attribute_name, attribute_location; attributes)
|
||||
{
|
||||
glBindAttribLocation(m_id, attribute_location, attribute_name.toStringz());
|
||||
}
|
||||
}
|
||||
|
||||
GLint get_uniform_location(string uniform_name) const
|
||||
/**
|
||||
* Get uniform location.
|
||||
*/
|
||||
public 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
|
||||
/**
|
||||
* Get multiple uniform locations.
|
||||
*/
|
||||
public 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)
|
||||
@ -92,89 +280,99 @@ class Program(uniforms...)
|
||||
}
|
||||
}
|
||||
|
||||
@property GLuint id() const
|
||||
/**
|
||||
* Get the program ID.
|
||||
*/
|
||||
public @property GLuint id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void use() const
|
||||
/**
|
||||
* Use the program.
|
||||
*/
|
||||
public void use() const
|
||||
{
|
||||
glUseProgram(m_id);
|
||||
}
|
||||
|
||||
private static string uniform_param_decl_list(string spec)
|
||||
/**
|
||||
* Internal constructor helper method.
|
||||
*/
|
||||
private void build(string program_source)
|
||||
{
|
||||
int count = spec[0] - '0';
|
||||
string type;
|
||||
if (spec[1] == 'i')
|
||||
string common_source;
|
||||
string shader_source;
|
||||
GLenum shader_type;
|
||||
|
||||
foreach (line; program_source.lineSplitter)
|
||||
{
|
||||
type = "GLint";
|
||||
GLenum new_shader_type;
|
||||
switch (line)
|
||||
{
|
||||
case "fragment:":
|
||||
new_shader_type = GL_FRAGMENT_SHADER;
|
||||
break;
|
||||
case "vertex:":
|
||||
new_shader_type = GL_VERTEX_SHADER;
|
||||
break;
|
||||
case "compute:":
|
||||
new_shader_type = GL_COMPUTE_SHADER;
|
||||
break;
|
||||
case "tess_control:":
|
||||
new_shader_type = GL_TESS_CONTROL_SHADER;
|
||||
break;
|
||||
case "tess_evaluation:":
|
||||
new_shader_type = GL_TESS_EVALUATION_SHADER;
|
||||
break;
|
||||
case "geometry:":
|
||||
new_shader_type = GL_GEOMETRY_SHADER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
else if (spec[1..2] == "ui")
|
||||
if (new_shader_type != 0)
|
||||
{
|
||||
type = "GLuint";
|
||||
if (shader_type != 0)
|
||||
{
|
||||
Shader shader = new Shader(shader_type, shader_source);
|
||||
attach_shader(shader);
|
||||
shader_source = "";
|
||||
}
|
||||
else if (spec[1] == 'f')
|
||||
{
|
||||
type = "GLfloat";
|
||||
shader_type = new_shader_type;
|
||||
shader_source = common_source[];
|
||||
}
|
||||
assert(type != "");
|
||||
if (spec[$ - 1] == 'v')
|
||||
else if (shader_type != 0)
|
||||
{
|
||||
return "uint count, const " ~ type ~ " * v";
|
||||
shader_source ~= line;
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] decls;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
decls ~= type ~ " v" ~ cast(char)('0' + i);
|
||||
}
|
||||
return decls.join(", ");
|
||||
common_source ~= line;
|
||||
}
|
||||
}
|
||||
|
||||
private static string uniform_param_list(string spec)
|
||||
if (shader_type != 0 && shader_source != "")
|
||||
{
|
||||
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(", ");
|
||||
}
|
||||
Shader shader = new Shader(shader_type, shader_source);
|
||||
attach_shader(shader);
|
||||
}
|
||||
|
||||
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]) ~ ");" ~
|
||||
"}");
|
||||
}
|
||||
link();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal constructor helper method.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal constructor helper method.
|
||||
*/
|
||||
private void build()
|
||||
{
|
||||
link();
|
||||
|
Loading…
x
Reference in New Issue
Block a user