1 module d2d.rendering.shaderprogram;
2 
3 import d2d;
4 
5 static import std.file;
6 
7 /// Class for combining shaders to a bindable ShaderProgram.
8 class ShaderProgram : IVerifiable
9 {
10 	///
11 	public this()
12 	{
13 		program = glCreateProgram();
14 	}
15 
16 	/// Will directly load the content from vertex and fragment and will create and return a ShaderProgram.
17 	public static ShaderProgram fromVertexFragmentFiles(string vertex, string fragment)
18 	{
19 		Shader v = new Shader();
20 		v.load(ShaderType.Vertex, std.file.readText(vertex));
21 
22 		Shader f = new Shader();
23 		f.load(ShaderType.Fragment, std.file.readText(fragment));
24 
25 		ShaderProgram program = new ShaderProgram;
26 		program.attach(v);
27 		program.attach(f);
28 		program.link();
29 		return program;
30 	}
31 
32 	/// Attaches a new shader to the program.
33 	/// Will call `shader.compile()` if necessary.
34 	public void attach(Shader shader)
35 	{
36 		shader.compile();
37 		glAttachShader(program, shader.id);
38 	}
39 
40 	/// Creates the program and binds it.
41 	public void link()
42 	{
43 		glLinkProgram(program);
44 		bind();
45 	}
46 
47 	/// Binds `this` for usage.
48 	public void bind()
49 	{
50 		glUseProgram(program);
51 	}
52 
53 	/// Regsiters a uniform variable in the shader for later setting.
54 	public int registerUniform(string uniform)
55 	{
56 		if ((uniform in _properties) !is null)
57 			return _properties[uniform];
58 		_properties[uniform] = glGetUniformLocation(program, uniform.toStringz());
59 		return _properties[uniform];
60 	}
61 
62 	///
63 	public void set(string uniform, int value)
64 	{
65 		debug if ((uniform in _properties) is null)
66 			{
67 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
68 			}
69 		glUniform1i(_properties[uniform], value);
70 	}
71 
72 	///
73 	public void set(string uniform, float value)
74 	{
75 		debug if ((uniform in _properties) is null)
76 			{
77 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
78 			}
79 		glUniform1f(_properties[uniform], value);
80 	}
81 
82 	///
83 	public void set(string uniform, vec2 value)
84 	{
85 		debug if ((uniform in _properties) is null)
86 			{
87 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
88 			}
89 		glUniform2fv(_properties[uniform], 1, value.value_ptr);
90 	}
91 
92 	///
93 	public void set(string uniform, vec3 value)
94 	{
95 		debug if ((uniform in _properties) is null)
96 			{
97 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
98 			}
99 		glUniform3fv(_properties[uniform], 1, value.value_ptr);
100 	}
101 
102 	///
103 	public void set(string uniform, vec4 value)
104 	{
105 		debug if ((uniform in _properties) is null)
106 			{
107 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
108 			}
109 		glUniform4fv(_properties[uniform], 1, value.value_ptr);
110 	}
111 
112 	///
113 	public void set(string uniform, mat2 value)
114 	{
115 		debug if ((uniform in _properties) is null)
116 			{
117 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
118 			}
119 		glUniformMatrix2fv(_properties[uniform], 1, 1, value.value_ptr);
120 	}
121 
122 	///
123 	public void set(string uniform, mat3 value)
124 	{
125 		debug if ((uniform in _properties) is null)
126 			{
127 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
128 			}
129 		glUniformMatrix3fv(_properties[uniform], 1, 1, value.value_ptr);
130 	}
131 
132 	///
133 	public void set(string uniform, mat4 value)
134 	{
135 		debug if ((uniform in _properties) is null)
136 			{
137 				throw new Exception("Uniform '" ~ uniform ~ "' is not defined!");
138 			}
139 		glUniformMatrix4fv(_properties[uniform], 1, 1, value.value_ptr);
140 	}
141 
142 	///
143 	public void opIndexAssign(T)(T value, string uniform)
144 	{
145 		set(uniform, value);
146 	}
147 
148 	///
149 	public uint id()
150 	{
151 		return program;
152 	}
153 
154 	///
155 	public @property bool valid()
156 	{
157 		return program > 0;
158 	}
159 
160 	private uint program;
161 	private int[string] _properties;
162 
163 	/// Regular texture shader.
164 	public static ShaderProgram defaultShader;
165 
166 	static void load()
167 	{
168 		defaultShader = new ShaderProgram();
169 		Shader vertex = new Shader();
170 		vertex.load(ShaderType.Vertex, "#version 330
171 layout(location = 0) in vec3 in_position;
172 layout(location = 1) in vec2 in_tex;
173 
174 uniform mat4 transform;
175 uniform mat4 projection;
176 
177 out vec2 texCoord;
178 
179 void main()
180 {
181 	gl_Position = projection * transform * vec4(in_position, 1);
182 
183 	texCoord = in_tex;
184 }
185 ");
186 		Shader fragment = new Shader();
187 		fragment.load(ShaderType.Fragment, "#version 330
188 uniform sampler2D tex;
189 
190 in vec2 texCoord;
191 
192 layout(location = 0) out vec4 out_frag_color;
193 
194 void main()
195 {
196 	out_frag_color = texture(tex, texCoord);
197 }
198 ");
199 		defaultShader.attach(vertex);
200 		defaultShader.attach(fragment);
201 		defaultShader.link();
202 		defaultShader.bind();
203 		defaultShader.registerUniform("tex");
204 		defaultShader.registerUniform("transform");
205 		defaultShader.registerUniform("projection");
206 		defaultShader.set("tex", 0);
207 	}
208 }