1 /*
2 Copyright (c) 2013 Andrey Penechko
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license the "Software" to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module anchovy.graphics.shaderprogram;
30 
31 import std.exception;
32 import std.stdio;
33 import std..string;
34 import std.variant;
35 
36 public import derelict.opengl3.gl3;
37 
38 
39 struct Uniform
40 {
41 	uint location;
42 	uint type;
43 	uint num;
44 	float[4] lastValue;
45 }
46 
47 class ShaderParameters
48 {
49 
50 }
51 
52 
53 class ShaderProgram
54 {
55 
56 public:
57 
58 	/++
59 	 + Creates an empty shader program in OpenGL state.
60 	 + OpenGL must be initialized before creating.
61 	 +/
62 	this()
63 	{
64 		programHandler = glCreateProgram();
65 		_isInited = true;
66 	}
67 
68 	this(in string vertShaderSource, in string fragShaderSource)
69 	{
70 		programHandler = glCreateProgram();
71 
72 		attachShader(GL_VERTEX_SHADER, vertShaderSource);
73 		attachShader(GL_FRAGMENT_SHADER, fragShaderSource);
74 
75 		_isInited = true;
76 	}
77 
78 	void close()
79 	{
80 		if (_isInited)
81 		{
82 			glDeleteProgram(programHandler);
83 			_isInited = false;
84 		}
85 	}
86 
87 	/++
88 	 + Binds shader to the current OpenGL state.
89 	 + Must be used only after attaching all the shaders and compilation.
90 	 + If compilation fails, no shader will be used.
91 	 +/
92 	void bind()
93 	{
94 		glUseProgram(programHandler);
95 	}
96 
97 	static void unbind()
98 	{
99 		glUseProgram(0);
100 	}
101 
102 	/++
103 	 + Compiles shader source of the given type and attaches it to the shader program.
104 	 + If shader compilation fails, writes compilation info to the error log of the shader program (_errorLog)
105 	 +
106 	 + Params:
107 	 + 		shaderType = GL_VERTEX_SHADER or GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER
108 	 + 		shaderSource = the string that contains the source of given shader
109 	 +/
110 	void attachShader(in GLenum shaderType, in string shaderSource)
111 	{
112 
113 		GLuint shader = glCreateShader(shaderType);
114 
115 		const char* fileData=toStringz(shaderSource);
116 		glShaderSource(shader, 1, &fileData, null);
117 
118 		glCompileShader(shader);
119 
120 		int status, length;
121 		glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
122 
123 		if (status == GL_FALSE)
124 		{
125 			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
126 
127 			char[] error=new char[length];
128 			glGetShaderInfoLog(shader, length, null, cast(char*)error);
129 
130 			string shaderTypeString;
131 			switch(shaderType)
132 			{
133 				case GL_VERTEX_SHADER:   shaderTypeString = "vertex"; break;
134 				case GL_GEOMETRY_SHADER: shaderTypeString = "geometry"; break;
135 				case GL_FRAGMENT_SHADER: shaderTypeString = "fragment"; break;
136 				default: break;
137 			}
138 
139 			_errorLog ~= "Compile failure in " ~ shaderTypeString ~ " shader:\n" ~ error~"\n";
140 		}
141 		glAttachShader(programHandler, shader);
142 	}
143 
144 	/++
145 	 + Compiles shaderProgram. If any error occurs, writes log to _errorLog.
146 	 + After compile detaches shaders from program.
147 	 + See_Also: errorLog
148 	 +/
149 	bool compile()
150 	{
151 
152 		glLinkProgram(programHandler);
153 
154 		GLint linkStatus;
155 
156 		glGetProgramiv(programHandler, GL_LINK_STATUS, &linkStatus);
157 
158 		/++
159 		 + Detaches all the shaders after compilation
160 		 +/
161 		scope(exit)
162 		{
163 			GLuint[3] shaders;
164 			GLsizei count;
165 
166 			glGetAttachedShaders(programHandler, 3, &count, cast(uint*)shaders);
167 
168 			for( uint i=0; i<count; ++i )
169 			{
170 				glDetachShader(programHandler, shaders[i]);
171 				glDeleteShader(shaders[i]);
172 			}
173 		}
174 
175 		if ( linkStatus == GL_FALSE )
176 		{
177 			GLint infoLogLength;
178 			glGetProgramiv(programHandler, GL_INFO_LOG_LENGTH, &infoLogLength);
179 
180 			char[] strInfoLog = new char[infoLogLength];
181 			glGetProgramInfoLog(programHandler, infoLogLength, null, cast(char*)strInfoLog);
182 			_errorLog ~= "Linker failure: " ~ strInfoLog ~ "\n";
183 
184 			return false;
185 		}
186 
187 		int total = -1;
188 		glGetProgramiv(programHandler, GL_ACTIVE_UNIFORMS, &total);
189 		for(int i=0; i<total; ++i)
190 		{
191 			int name_len = -1, num = -1;
192 			GLenum type = GL_ZERO;
193 			char[100] name;
194 			glGetActiveUniform(programHandler, i, name.length-1,
195 			                   &name_len, &num, &type, name.ptr);
196 
197 			name[name_len] = 0;
198 			GLuint location = glGetUniformLocation(programHandler, name.ptr);
199 			string uniformName = name[0..name_len].idup;
200 			uniforms[uniformName] = Uniform(location, type, num);
201 		}
202 		// Magic bug. Cannot remove this statement
203 		writef("",uniforms);
204 		uniforms.rehash;
205 
206 		return true;
207 	}
208 
209 	/++
210 	 + Returns: empty string if the shader was compiled without	errors, or error log otherwise.
211 	 + Examples:
212 	 + ---
213 	 + if ( !myShaderProgram.compile ){
214 	 + 	  writeln( myShaderProgram.errorLog ); //Writes error log to console
215 	 + }
216 	 + ---
217 	 + See_Also: compile
218 	 +/
219 	@property string errorLog()
220 	{
221 		return _errorLog;
222 	}
223 
224 	private static string getParType(T)()
225 	{
226 		string res;
227 		if (is(T : int) || is(T : byte) || is(T : short))
228 		{
229 			res = "i";
230 		}
231 		if (is(T : uint) || is(T : ubyte) || is(T : ushort))
232 		{
233 			res = "ui";
234 		}
235 		else if (is(T : float))
236 		{
237 			res = "f";
238 		}
239 		return res;
240 	}
241 
242 	private static uint getGlType(T)()
243 	{
244 		uint type;
245 		static if (is(T : byte)) type = GL_BYTE;
246 		else if (is(T : ubyte)) type = GL_UNSIGNED_BYTE;
247 		else if (is(T : short)) type = GL_SHORT;
248 		else if (is(T : ushort)) type = GL_UNSIGNED_SHORT;
249 		else if (is(T : int)) type = GL_INT;
250 		else if (is(T : uint)) type = GL_UNSIGNED_INT;
251 		else if (is(T : float)) type = GL_FLOAT;
252 		else if (is(T : double)) type = GL_DOUBLE;
253 		return type;
254 	}
255 	//glUniform{1|2|3|4}{f|i|ui}v
256 	void setUniform(T)(string uniName, T t1)
257 	{
258 		Uniform* uni = &getUniform(uniName);
259 		if (uni.lastValue[0] != t1)
260 		{
261 			uni.lastValue[0] = t1;
262 			mixin("glUniform1"~getParType!T~"(uni.location, t1);");
263 		}
264 		/+for(uint i = 0; i<t.length-1; ++i)
265 		{
266 			params ~= "t["~to!string(i)~"], ";
267 		}
268 		params ~= "t["~to!string(t.length-1)~"]";
269 		//Uniform* uni = uniName in uniforms;
270 		assert(uni !is null, "Unknown uniform name");
271 		assert(uni.type == getGlType(t[0])(), "Wrong uniform type");
272 
273 		writeln("glUniform1" ~
274 		        getParType!(typeof(t[0]))() ~
275 		        "(" ~
276 		        "uniName, " ~
277 		        params ~
278 		        ");");+/
279 	}
280 
281 	void setUniform2(T)(string uniName, T t1, T t2)
282 	{
283 		Uniform* uni = &getUniform(uniName);
284 		if (uni.lastValue[0..2] != [t1,t2])
285 		{
286 			uni.lastValue[0..2] = [t1,t2];
287 			mixin("glUniform2"~getParType!T~"(uni.location, t1, t2);");
288 		}
289 	}
290 
291 	void setUniform3(T)(string uniName, T t1, T t2, T t3)
292 	{
293 		Uniform* uni = &getUniform(uniName);
294 		if (uni.lastValue[0..3] != [t1, t2, t3])
295 		{
296 			uni.lastValue[0..3] = [t1, t2, t3];
297 			mixin("glUniform3"~getParType!T~"(uni.location, t1, t2, t3);");
298 		}
299 	}
300 
301 	void setUniform4(T)(string uniName, T t1, T t2, T t3, T t4)
302 	{
303 		Uniform* uni = &getUniform(uniName);
304 		if (uni.lastValue[0..4] != [t1, t2, t3, t4])
305 		{
306 			uni.lastValue[0..4] = [t1, t2, t3, t4];
307 			mixin("glUniform4"~getParType!T~"(uni.location, t1, t2, t3, t4);");
308 		}
309 	}
310 
311 	uint getUniformLoc(string name)
312 	{
313 		//assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'");
314 		return uniforms[name].location;
315 	}
316 
317 	ref Uniform getUniform(string name)
318 	{
319 		//assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'");
320 		return uniforms[name];
321 	}
322 
323 	/+
324 	 + Returns: shader program pointer of type GLuint, the result of glCreateProgram();
325 	 + Can be used in some OpenGL functions.
326 	 + Examples:
327 	 + ---
328 	 + myShaderProgram = new CShaderProgram();
329 	 + ---
330 	 + ...
331 	 + ---
332 	 + GLuint myShaderAttribute = glGetUniformLocation( myShaderProgram.program, "myShaderAttribute" );
333 	 + ---
334 	 +/
335 	@property GLuint program(){ return programHandler; }
336 
337 	//void setUniform(T)(string unifName,
338 	//TODO: Add methods for uniforms setting
339 
340 private:
341 
342 	/++
343 	 + The result of glCreateProgram() get in constructor
344 	 +
345 	 + See_Also: this
346 	 +/
347 	GLuint programHandler = 0;
348 	Uniform[string] uniforms;
349 
350 	/++
351 	 + The result of:
352 	 + ---
353 	 + glGetShaderiv( _shaderPointer, GL_COMPILE_STATUS, _compileStatus );
354 	 + ---
355 	 +/
356 	GLuint _compileStatus;
357 
358 	/++
359 	 + Contains full compile log if compile error.
360 	 +/
361 	string _errorLog;
362 
363 	bool _isInited;
364 
365 }
366 
367 static const string[uint] glTypes;
368 
369 static this()
370 {
371 	glTypes = [
372 	           0x1400 : "GL_BYTE",
373 	           0x1401 : "GL_UNSIGNED_BYTE",
374 	           0x1402 : "GL_SHORT",
375 	           0x1403 : "GL_UNSIGNED_SHORT",
376 	           0x1404 : "GL_INT",
377 	           0x1405 : "GL_UNSIGNED_INT",
378 	           0x1406 : "GL_FLOAT",
379 	           0x140A : "GL_DOUBLE",];
380 }