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 std.string;
|
||||||
import gl;
|
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. */
|
/** Program ID. */
|
||||||
static assert((uniforms.length % 2) == 0);
|
|
||||||
|
|
||||||
private GLuint m_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)
|
this(Args...)(Args args)
|
||||||
{
|
{
|
||||||
m_id = glCreateProgram();
|
m_id = glCreateProgram();
|
||||||
@ -32,22 +198,37 @@ class Program(uniforms...)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destruct program object.
|
||||||
|
*/
|
||||||
~this()
|
~this()
|
||||||
{
|
{
|
||||||
glDeleteProgram(m_id);
|
glDeleteProgram(m_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void attach_shader(Shader shader) const
|
/**
|
||||||
{
|
* Bind an attribute to a specific location.
|
||||||
glAttachShader(m_id, shader.id);
|
*/
|
||||||
}
|
public void bind_attrib_location(string name, uint index) const
|
||||||
|
|
||||||
void bind_attrib_location(string name, uint index) const
|
|
||||||
{
|
{
|
||||||
glBindAttribLocation(m_id, index, name.toStringz());
|
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);
|
glLinkProgram(m_id);
|
||||||
|
|
||||||
@ -69,21 +250,28 @@ class Program(uniforms...)
|
|||||||
throw new Exception(message);
|
throw new Exception(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static foreach(i, v; uniforms)
|
static foreach(uniform_name, uniform_type; uniforms)
|
||||||
{
|
{
|
||||||
static if ((i % 2) == 0)
|
mixin("m_uniform_" ~ uniform_name ~ " = glGetUniformLocation(m_id, \"" ~ uniform_name ~ "\");");
|
||||||
{
|
|
||||||
mixin("m_uniform_" ~ v ~ " = glGetUniformLocation(m_id, \"" ~ v ~ "\");");
|
|
||||||
}
|
}
|
||||||
|
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());
|
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);
|
*uniform_location = get_uniform_location(uniform_name);
|
||||||
static if (args.length > 0u)
|
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;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void use() const
|
/**
|
||||||
|
* Use the program.
|
||||||
|
*/
|
||||||
|
public void use() const
|
||||||
{
|
{
|
||||||
glUseProgram(m_id);
|
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 common_source;
|
||||||
string type;
|
string shader_source;
|
||||||
if (spec[1] == 'i')
|
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')
|
shader_type = new_shader_type;
|
||||||
{
|
shader_source = common_source[];
|
||||||
type = "GLfloat";
|
|
||||||
}
|
}
|
||||||
assert(type != "");
|
else if (shader_type != 0)
|
||||||
if (spec[$ - 1] == 'v')
|
|
||||||
{
|
{
|
||||||
return "uint count, const " ~ type ~ " * v";
|
shader_source ~= line;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string[] decls;
|
common_source ~= line;
|
||||||
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)
|
if (shader_type != 0 && shader_source != "")
|
||||||
{
|
{
|
||||||
int count = spec[0] - '0';
|
Shader shader = new Shader(shader_type, shader_source);
|
||||||
if (spec[$ - 1] == 'v')
|
attach_shader(shader);
|
||||||
{
|
|
||||||
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)
|
link();
|
||||||
{
|
|
||||||
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]) ~ ");" ~
|
|
||||||
"}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal constructor helper method.
|
||||||
|
*/
|
||||||
private void build(Args...)(Shader s, Args args)
|
private void build(Args...)(Shader s, Args args)
|
||||||
{
|
{
|
||||||
attach_shader(s);
|
attach_shader(s);
|
||||||
build(args);
|
build(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void build(Args...)(string attrib_name, uint index, Args args)
|
/**
|
||||||
{
|
* Internal constructor helper method.
|
||||||
bind_attrib_location(attrib_name, index);
|
*/
|
||||||
build(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void build()
|
private void build()
|
||||||
{
|
{
|
||||||
link();
|
link();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user