1 module polyplex.core.render.gl.shader;
2 import polyplex.core.render;
3 import derelict.sdl2.sdl;
4 import derelict.opengl;
5 import polyplex.math;
6 import polyplex.utils.logging;
7 import std.conv;
8 
9 
10 class GLShader : Shader {
11 	private GLuint shaderprogram;
12 	private GLuint vertexshader = 0;
13 	private GLuint fragmentshader = 0;
14 	private GLuint geometryshader = 0;
15 	private ShaderCode shadersource;
16 
17 	this(ShaderCode code) {
18 		shadersource = code;
19 		compile_shaders();
20 		link_shaders();
21 	}
22 
23 	~this() {
24 		glUseProgram(0);
25 		glDetachShader(shaderprogram, vertexshader);
26 		if (geometryshader != 0) glDetachShader(shaderprogram, geometryshader);
27 		glDetachShader(shaderprogram, fragmentshader);
28 		glDeleteProgram(shaderprogram);
29 		glDeleteShader(vertexshader);
30 		if (geometryshader != 0) glDeleteShader(geometryshader);
31 		glDeleteShader(fragmentshader);
32 	}
33 	
34 	/**
35 		Vertex Shader Id.
36 	*/
37 	public @property GLchar* VertGL() { return cast(GLchar*)(shadersource.Vertex.ptr); }
38 	
39 	/**
40 		Fragment Shader Id.
41 	*/
42 	public @property GLchar* FragGL() { return cast(GLchar*)(shadersource.Fragment.ptr); }
43 	
44 	/**
45 		Geometry Shader Id.
46 	*/
47 	public @property GLchar* GeoGL() { return cast(GLchar*)(shadersource.Geometry.ptr); }
48 
49 	/**
50 		Shader Attributes.
51 	*/
52 	public @property GLchar*[] AttribGL() {
53 		GLchar*[] attributes = new GLchar*[shadersource.Attributes.length];
54 		for (int i = 0; i < attributes.length; i++) {
55 			string attr = shadersource.Attributes[i];
56 			char[] buffer = (attr ~ '\0').dup;
57 			attributes[i] = buffer.ptr;
58 		}
59 		return attributes;
60 	}
61 
62 	/**
63 		Gets whenever this shader is attached.
64 	*/
65 	public override @property bool Attached() {
66 		int i;
67 		glGetIntegerv(GL_CURRENT_PROGRAM, &i);
68 		if (i == shaderprogram) return true;
69 		return false;
70 	}
71 
72 	//Uniform stuff.
73 	public override void SetUniform(int location, float value) { glUniform1f(cast(GLint)location, cast(GLfloat)value); }
74 	public override void SetUniform(int location, Vector2 value) { glUniform2f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y); }
75 	public override void SetUniform(int location, Vector3 value) { glUniform3f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z); }
76 	public override void SetUniform(int location, Vector4 value) { glUniform4f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z, cast(GLfloat)value.W); }
77 	public override void SetUniform(int location, int value) { glUniform1i(cast(GLint)location, cast(GLint)value); }
78 	public override void SetUniform(int location, Vector2i value) { glUniform2i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y); }
79 	public override void SetUniform(int location, Vector3i value) { glUniform3i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z); }
80 	public override void SetUniform(int location, Vector4i value) { glUniform4i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z, cast(GLint)value.W); }
81 	public override void SetUniform(int location, Matrix2x2 value) { glUniformMatrix2fv(location, 1, GL_TRUE, value.ptr); }
82 	public override void SetUniform(int location, Matrix3x3 value) { glUniformMatrix3fv(location, 1, GL_TRUE, value.ptr); }
83 	public override void SetUniform(int location, Matrix4x4 value) { glUniformMatrix4fv(location, 1, GL_TRUE, value.ptr); }
84 
85 	/**
86 		GetUniform gets the position of a uniform.
87 	*/
88 	public override uint GetUniform(string name) {
89 		bool wasAttached = true;
90 		if (!Attached) {
91 			wasAttached = false;
92 			Attach();
93 		}
94 		uint u = glGetUniformLocation(shaderprogram, name.ptr);
95 		if (!wasAttached) Detach();
96 		return u;
97 	}
98 
99 	/**
100 		HasUniform checks whenever the shader contains a unform with name "name"
101 		Returns true if it exists, false otherwise.
102 	*/
103 	public override bool HasUniform(string name) {
104 		auto u = GetUniform(name);
105 		if (u == -1) return false;
106 		return true;
107 	}
108 
109 	/**
110 		Attach attaches the shader.
111 	*/
112 	public override void Attach() {
113 		glUseProgram(this.shaderprogram);
114 	}
115 
116 	/**
117 		Detatch detatches the shader. (Binds shader 0)
118 	*/
119 	public override void Detach() {
120 		glUseProgram(0);
121 	}
122 
123 	//Shader linking
124 	private void link_shaders() {
125 		bind_attribs();
126 		glLinkProgram(shaderprogram);
127 		int c;
128 		glGetProgramiv(shaderprogram, GL_LINK_STATUS, &c);
129 		if (c == 0) {
130 			log_shader(shaderprogram, LogType.Program);
131 			return;
132 		}
133 		c = 0;
134 		glValidateProgram(shaderprogram);
135 		glGetProgramiv(shaderprogram, GL_VALIDATE_STATUS, &c);
136 		if (c == 0) {
137 			log_shader(shaderprogram, LogType.Program);
138 			return;
139 		}
140 		//TODO: Add logging
141 		//writeln("Compilation completed.");
142 	}
143 
144 	private void bind_attribs() {
145 		GLchar*[] attribs = AttribGL;
146 		for (int i = 0; i < attribs.length; i++) {
147 			//TODO: Add logging
148 			//writeln("Binding attribute ", i, " named ", to!string(attribs[i]));
149 			Logger.Debug("Binding attribute {0} named {1}...", i, attribs[i]);
150 			glBindAttribLocation(shaderprogram, i, attribs[i]);
151 		}
152 	}
153 
154 	//Compilation of shaders
155 	private void compile_shaders() {
156 		compile_shader(shadersource, ShaderType.Vertex);
157 		if (!(shadersource.Geometry is null)) compile_shader(shadersource, ShaderType.Geometry);
158 		compile_shader(shadersource, ShaderType.Fragment);
159 		shaderprogram = glCreateProgram();
160 		glAttachShader(shaderprogram, vertexshader);
161 		if (!(shadersource.Geometry is null)) glAttachShader(shaderprogram, geometryshader);
162 		glAttachShader(shaderprogram, fragmentshader);
163 	}
164 
165 	private void compile_shader(ShaderCode code, ShaderType type) {
166 		if (type == ShaderType.Vertex) {
167 			vertexshader = glCreateShader(GL_VERTEX_SHADER);
168 
169 			//Get source
170 			int l = cast(int)shadersource.Vertex.length;
171 			GLchar* cs = VertGL;
172 			glShaderSource(vertexshader, 1, &cs, &l);
173 
174 			//Compile
175 			glCompileShader(vertexshader);
176 			int c;
177 			glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &c);
178 			if (c == 0) {
179 				log_shader(vertexshader);
180 				return;
181 			}
182 		} else if (type == ShaderType.Geometry) {
183 			geometryshader = glCreateShader(GL_GEOMETRY_SHADER);
184 
185 			//Get source
186 			int l = cast(int)shadersource.Geometry.length;
187 			GLchar* cs = GeoGL;
188 			glShaderSource(geometryshader, 1, &cs, &l);
189 
190 			//Compile
191 			glCompileShader(geometryshader);
192 			int c;
193 			glGetShaderiv(geometryshader, GL_COMPILE_STATUS, &c);
194 			if (c == 0) {
195 				log_shader(geometryshader);
196 				return;
197 			}
198 		} else {
199 			fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
200 			
201 			//Get source
202 			int l = cast(int)shadersource.Fragment.length;
203 			GLchar* cs = FragGL;
204 			glShaderSource(fragmentshader, 1, &cs, &l);
205 
206 			//Compile
207 			glCompileShader(fragmentshader);
208 			int c;
209 			glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &c);
210 			if (c != GL_TRUE) {
211 				log_shader(fragmentshader);
212 				return;
213 			}
214 		}
215 	}
216 
217 	enum LogType {
218 		Program,
219 		Shader
220 	}
221 	
222 	private void log_shader(GLuint port, LogType type = LogType.Shader) {
223 		if (type == LogType.Shader) {
224 			int maxlen = 512;
225 			char[] logmsg;
226 			logmsg.length = maxlen;
227 			glGetShaderInfoLog(port, maxlen, &maxlen, logmsg.ptr);
228 			throw new Error("GLSLShaderError (shader: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen]));
229 		}
230 		int maxlen = 512;
231 		char[] logmsg;
232 		logmsg.length = maxlen;
233 		glGetProgramInfoLog(port, maxlen, &maxlen, logmsg.ptr);
234 		throw new Error("GLSLProgramError (program: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen]));
235 	}
236 }