1 module D2DGame.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 uint id()
144 	{
145 		return program;
146 	}
147 
148 	///
149 	public @property bool valid()
150 	{
151 		return program > 0;
152 	}
153 
154 	private uint program;
155 	private int[string] _properties;
156 
157 	/// Regular texture shader.
158 	public static ShaderProgram defaultShader;
159 
160 	static void load()
161 	{
162 		defaultShader = new ShaderProgram();
163 		Shader vertex = new Shader();
164 		vertex.load(ShaderType.Vertex, "#version 330
165 layout(location = 0) in vec3 in_position;
166 layout(location = 1) in vec2 in_tex;
167 
168 uniform mat4 transform;
169 uniform mat4 projection;
170 
171 out vec2 texCoord;
172 
173 void main()
174 {
175 	gl_Position = projection * transform * vec4(in_position, 1);
176 
177 	texCoord = in_tex;
178 }
179 ");
180 		Shader fragment = new Shader();
181 		fragment.load(ShaderType.Fragment, "#version 330
182 uniform sampler2D tex;
183 
184 in vec2 texCoord;
185 
186 layout(location = 0) out vec4 out_frag_color;
187 
188 void main()
189 {
190 	out_frag_color = texture(tex, texCoord);
191 }
192 ");
193 		defaultShader.attach(vertex);
194 		defaultShader.attach(fragment);
195 		defaultShader.link();
196 		defaultShader.bind();
197 		defaultShader.registerUniform("tex");
198 		defaultShader.registerUniform("transform");
199 		defaultShader.registerUniform("projection");
200 		defaultShader.set("tex", 0);
201 	}
202 }