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 	}
66 	
67 	this(in string vertShaderSource, in string fragShaderSource)
68 	{
69 		programHandler = glCreateProgram();
70 		
71 		attachShader(GL_VERTEX_SHADER, vertShaderSource);
72 		attachShader(GL_FRAGMENT_SHADER, fragShaderSource);
73 	}
74 	
75 	~this()
76 	{
77 		glDeleteProgram(programHandler);
78 	}
79 	
80 	/++
81 	 + Binds shader to the current OpenGL state.
82 	 + Must be used only after attaching all the shaders and compilation.
83 	 + If compilation fails, no shader will be used.
84 	 +/
85 	void bind()
86 	{
87 		glUseProgram(programHandler);
88 	}
89 	
90 	static void unbind()
91 	{
92 		glUseProgram(0);
93 	}
94 	
95 	/++
96 	 + Compiles shader source of the given type and attaches it to the shader program.
97 	 + If shader compilation fails, writes compilation info to the error log of the shader program (_errorLog)
98 	 + 
99 	 + Params:
100 	 + 		shaderType = GL_VERTEX_SHADER or GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER
101 	 + 		shaderSource = the string that contains the source of given shader
102 	 +/
103 	void attachShader(in GLenum shaderType, in string shaderSource)
104 	{
105 		
106 		GLuint shader = glCreateShader(shaderType);
107 		
108 		const char* fileData=toStringz(shaderSource);
109 		glShaderSource(shader, 1, &fileData, null);
110 		
111 		glCompileShader(shader);
112 		
113 		int status, length;
114 		glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
115 		
116 		if (status == GL_FALSE)
117 		{
118 			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
119 			
120 			char[] error=new char[length];
121 			glGetShaderInfoLog(shader, length, null, cast(char*)error);
122 			
123 			string shaderTypeString;
124 			switch(shaderType)
125 			{
126 				case GL_VERTEX_SHADER:   shaderTypeString = "vertex"; break;
127 				case GL_GEOMETRY_SHADER: shaderTypeString = "geometry"; break;
128 				case GL_FRAGMENT_SHADER: shaderTypeString = "fragment"; break;
129 				default: break;
130 			}
131 			
132 			_errorLog ~= "Compile failure in " ~ shaderTypeString ~ " shader:\n" ~ error~"\n";
133 		}
134 		glAttachShader(programHandler, shader);
135 	}
136 	
137 	/++
138 	 + Compiles shaderProgram. If any error occurs, writes log to _errorLog.
139 	 + After compile detaches shaders from program.
140 	 + See_Also: errorLog
141 	 +/
142 	bool compile()
143 	{
144 		
145 		glLinkProgram(programHandler);
146 		
147 		GLint linkStatus;
148 		
149 		glGetProgramiv(programHandler, GL_LINK_STATUS, &linkStatus);
150 		
151 		/++
152 		 + Detaches all the shaders after compilation
153 		 +/
154 		scope(exit)
155 		{			
156 			GLuint[3] shaders;
157 			GLsizei count;
158 			
159 			glGetAttachedShaders(programHandler, 3, &count, cast(uint*)shaders);
160 			
161 			for( uint i=0; i<count; ++i )
162 			{
163 				glDetachShader(programHandler, shaders[i]);
164 				glDeleteShader(shaders[i]);
165 			}
166 		}
167 		
168 		if ( linkStatus == GL_FALSE )
169 		{
170 			GLint infoLogLength;
171 			glGetProgramiv(programHandler, GL_INFO_LOG_LENGTH, &infoLogLength);
172 			
173 			char[] strInfoLog = new char[infoLogLength];
174 			glGetProgramInfoLog(programHandler, infoLogLength, null, cast(char*)strInfoLog);
175 			_errorLog ~= "Linker failure: " ~ strInfoLog ~ "\n";
176 			
177 			return false;
178 		}
179 		
180 		int total = -1;
181 		glGetProgramiv(programHandler, GL_ACTIVE_UNIFORMS, &total); 
182 		for(int i=0; i<total; ++i) 
183 		{
184 			int name_len = -1, num = -1;
185 			GLenum type = GL_ZERO;
186 			char[100] name;
187 			glGetActiveUniform(programHandler, i, name.length-1,
188 			                   &name_len, &num, &type, name.ptr);
189 			
190 			name[name_len] = 0;
191 			GLuint location = glGetUniformLocation(programHandler, name.ptr);
192 			string uniformName = name[0..name_len].idup;
193 			uniforms[uniformName] = Uniform(location, type, num);
194 		}
195 		// Magic bug. Cannot remove this statement
196 		writef("",uniforms);
197 		uniforms.rehash;
198 		return true;
199 	}
200 	
201 	/++
202 	 + Returns: empty string if the shader was compiled without	errors, or error log otherwise.
203 	 + Examples:
204 	 + ---
205 	 + if ( !myShaderProgram.compile ){
206 	 + 	  writeln( myShaderProgram.errorLog ); //Writes error log to console
207 	 + }
208 	 + ---
209 	 + See_Also: compile
210 	 +/
211 	@property string errorLog()
212 	{
213 		return _errorLog;
214 	}
215 	
216 	private static string getParType(T)()
217 	{
218 		string res;
219 		if (is(T : int) || is(T : byte) || is(T : short))
220 		{
221 			res = "i";
222 		}
223 		if (is(T : uint) || is(T : ubyte) || is(T : ushort))
224 		{
225 			res = "ui";
226 		}
227 		else if (is(T : float))
228 		{
229 			res = "f";
230 		}
231 		return res;
232 	}
233 	
234 	private static uint getGlType(T)()
235 	{
236 		uint type;
237 		static if (is(T : byte)) type = GL_BYTE;
238 		else if (is(T : ubyte)) type = GL_UNSIGNED_BYTE;
239 		else if (is(T : short)) type = GL_SHORT;
240 		else if (is(T : ushort)) type = GL_UNSIGNED_SHORT;
241 		else if (is(T : int)) type = GL_INT;
242 		else if (is(T : uint)) type = GL_UNSIGNED_INT;
243 		else if (is(T : float)) type = GL_FLOAT;
244 		else if (is(T : double)) type = GL_DOUBLE;
245 		return type;
246 	}
247 	//glUniform{1|2|3|4}{f|i|ui}v
248 	void setUniform(T)(string uniName, T t1)
249 	{
250 		Uniform* uni = &getUniform(uniName);
251 		if (uni.lastValue[0] != t1)
252 		{
253 			uni.lastValue[0] = t1;
254 			mixin("glUniform1"~getParType!T~"(uni.location, t1);");
255 		}
256 		/+for(uint i = 0; i<t.length-1; ++i)
257 		{
258 			params ~= "t["~to!string(i)~"], ";
259 		}
260 		params ~= "t["~to!string(t.length-1)~"]";
261 		//Uniform* uni = uniName in uniforms;
262 		assert(uni !is null, "Unknown uniform name");
263 		assert(uni.type == getGlType(t[0])(), "Wrong uniform type");
264 		
265 		writeln("glUniform1" ~
266 		        getParType!(typeof(t[0]))() ~
267 		        "(" ~
268 		        "uniName, " ~
269 		        params ~
270 		        ");");+/
271 	}
272 	
273 	void setUniform2(T)(string uniName, T t1, T t2)
274 	{
275 		Uniform* uni = &getUniform(uniName);
276 		if (uni.lastValue[0..2] != [t1,t2])
277 		{
278 			uni.lastValue[0..2] = [t1,t2];
279 			mixin("glUniform2"~getParType!T~"(uni.location, t1, t2);");
280 		}
281 	}
282 	
283 	void setUniform3(T)(string uniName, T t1, T t2, T t3)
284 	{
285 		Uniform* uni = &getUniform(uniName);
286 		if (uni.lastValue[0..3] != [t1, t2, t3])
287 		{
288 			uni.lastValue[0..3] = [t1, t2, t3];
289 			mixin("glUniform3"~getParType!T~"(uni.location, t1, t2, t3);");
290 		}
291 	}
292 	
293 	void setUniform4(T)(string uniName, T t1, T t2, T t3, T t4)
294 	{
295 		Uniform* uni = &getUniform(uniName);
296 		if (uni.lastValue[0..4] != [t1, t2, t3, t4])
297 		{
298 			uni.lastValue[0..4] = [t1, t2, t3, t4];
299 			mixin("glUniform4"~getParType!T~"(uni.location, t1, t2, t3, t4);");
300 		}
301 	}
302 	
303 	uint getUniformLoc(string name)
304 	{
305 		//assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'");
306 		return uniforms[name].location;
307 	}
308 	
309 	ref Uniform getUniform(string name)
310 	{
311 		//assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'");
312 		return uniforms[name];
313 	}
314 	
315 	/+
316 	 + Returns: shader program pointer of type GLuint, the result of glCreateProgram();
317 	 + Can be used in some OpenGL functions.
318 	 + Examples:
319 	 + ---
320 	 + myShaderProgram = new CShaderProgram();
321 	 + ---
322 	 + ...
323 	 + ---
324 	 + GLuint myShaderAttribute = glGetUniformLocation( myShaderProgram.program, "myShaderAttribute" );
325 	 + ---
326 	 +/
327 	@property GLuint program(){ return programHandler; }
328 	
329 	//void setUniform(T)(string unifName, 
330 	//TODO: Add methods for uniforms setting
331 	
332 private:
333 	
334 	/++
335 	 + The result of glCreateProgram() get in constructor
336 	 + 
337 	 + See_Also: this
338 	 +/
339 	GLuint programHandler = 0;
340 	Uniform[string] uniforms;
341 	
342 	/++
343 	 + The result of:
344 	 + ---
345 	 + glGetShaderiv( _shaderPointer, GL_COMPILE_STATUS, _compileStatus );
346 	 + ---
347 	 +/
348 	GLuint _compileStatus;
349 	
350 	/++
351 	 + Contains full compile log if compile error.
352 	 +/
353 	string _errorLog;
354 	
355 }
356 
357 static const string[uint] glTypes;
358 
359 static this()
360 {
361 	glTypes = [
362 	           0x1400 : "GL_BYTE",
363 	           0x1401 : "GL_UNSIGNED_BYTE",
364 	           0x1402 : "GL_SHORT",
365 	           0x1403 : "GL_UNSIGNED_SHORT",
366 	           0x1404 : "GL_INT",
367 	           0x1405 : "GL_UNSIGNED_INT",
368 	           0x1406 : "GL_FLOAT",
369 	           0x140A : "GL_DOUBLE",];
370 	glTypes.rehash;
371 }