1 module polyplex.core.render.gl.shader;
2 import polyplex.core.render;
3 import bindbc.sdl;
4 import bindbc.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 		Gets whenever this shader is attached.
51 	*/
52 	public override @property bool Attached() {
53 		int i;
54 		glGetIntegerv(GL_CURRENT_PROGRAM, &i);
55 		if (i == shaderprogram) return true;
56 		return false;
57 	}
58 
59 	//Uniform stuff.
60 	public override void SetUniform(int location, float value) { if (!Attached) Attach(); glUniform1f(cast(GLint)location, cast(GLfloat)value); }
61 	public override void SetUniform(int location, Vector2 value) { if (!Attached) Attach(); glUniform2f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y); }
62 	public override void SetUniform(int location, Vector3 value) { if (!Attached) Attach(); glUniform3f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z); }
63 	public override void SetUniform(int location, Vector4 value) { if (!Attached) Attach(); glUniform4f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z, cast(GLfloat)value.W); }
64 	public override void SetUniform(int location, int value) { if (!Attached) Attach(); glUniform1i(cast(GLint)location, cast(GLint)value); }
65 	public override void SetUniform(int location, Vector2i value) { if (!Attached) Attach(); glUniform2i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y); }
66 	public override void SetUniform(int location, Vector3i value) { if (!Attached) Attach(); glUniform3i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z); }
67 	public override void SetUniform(int location, Vector4i value) { if (!Attached) Attach(); glUniform4i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z, cast(GLint)value.W); }
68 	public override void SetUniform(int location, Matrix2x2 value) { if (!Attached) Attach(); glUniformMatrix2fv(location, 1, GL_TRUE, value.ptr); }
69 	public override void SetUniform(int location, Matrix3x3 value) { if (!Attached) Attach(); glUniformMatrix3fv(location, 1, GL_TRUE, value.ptr); }
70 	public override void SetUniform(int location, Matrix4x4 value) { if (!Attached) Attach(); glUniformMatrix4fv(location, 1, GL_TRUE, value.ptr); }
71 
72 	/**
73 		GetUniform gets the position of a uniform.
74 	*/
75 	public override int GetUniform(string name) {
76 		import std.string;
77 		return glGetUniformLocation(shaderprogram, name.toStringz);
78 	}
79 
80 	/**
81 		HasUniform checks whenever the shader contains a unform with name "name"
82 		Returns true if it exists, false otherwise.
83 	*/
84 	public override bool HasUniform(string name) {
85 		auto u = GetUniform(name);
86 		if (u == -1) return false;
87 		return true;
88 	}
89 
90 	/**
91 		Attach attaches the shader.
92 	*/
93 	public override void Attach() {
94 		glUseProgram(this.shaderprogram);
95 	}
96 
97 	/**
98 		Detatch detatches the shader. (Binds shader 0)
99 	*/
100 	public override void Detach() {
101 		glUseProgram(0);
102 	}
103 
104 	//Shader linking
105 	private void link_shaders() {
106 		glLinkProgram(shaderprogram);
107 		int c;
108 		glGetProgramiv(shaderprogram, GL_LINK_STATUS, &c);
109 		if (c == 0) {
110 			log_shader(shaderprogram, LogType.Program);
111 			return;
112 		}
113 		c = 0;
114 		glValidateProgram(shaderprogram);
115 		glGetProgramiv(shaderprogram, GL_VALIDATE_STATUS, &c);
116 		if (c == 0) {
117 			log_shader(shaderprogram, LogType.Program);
118 			return;
119 		}
120 		//TODO: Add logging
121 		//writeln("Compilation completed.");
122 	}
123 
124 	//Compilation of shaders
125 	private void compile_shaders() {
126 		compile_shader(shadersource, ShaderType.Vertex);
127 		if (!(shadersource.Geometry is null)) compile_shader(shadersource, ShaderType.Geometry);
128 		compile_shader(shadersource, ShaderType.Fragment);
129 		shaderprogram = glCreateProgram();
130 		glAttachShader(shaderprogram, vertexshader);
131 		if (!(shadersource.Geometry is null)) glAttachShader(shaderprogram, geometryshader);
132 		glAttachShader(shaderprogram, fragmentshader);
133 	}
134 
135 	private void compile_shader(ShaderCode code, ShaderType type) {
136 		if (type == ShaderType.Vertex) {
137 			vertexshader = glCreateShader(GL_VERTEX_SHADER);
138 
139 			//Get source
140 			int l = cast(int)shadersource.Vertex.length;
141 			GLchar* cs = VertGL;
142 			glShaderSource(vertexshader, 1, &cs, &l);
143 
144 			//Compile
145 			glCompileShader(vertexshader);
146 			int c;
147 			glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &c);
148 			if (c == 0) {
149 				log_shader(vertexshader);
150 				return;
151 			}
152 		} else if (type == ShaderType.Geometry) {
153 			geometryshader = glCreateShader(GL_GEOMETRY_SHADER);
154 
155 			//Get source
156 			int l = cast(int)shadersource.Geometry.length;
157 			GLchar* cs = GeoGL;
158 			glShaderSource(geometryshader, 1, &cs, &l);
159 
160 			//Compile
161 			glCompileShader(geometryshader);
162 			int c;
163 			glGetShaderiv(geometryshader, GL_COMPILE_STATUS, &c);
164 			if (c == 0) {
165 				log_shader(geometryshader);
166 				return;
167 			}
168 		} else {
169 			fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
170 			
171 			//Get source
172 			int l = cast(int)shadersource.Fragment.length;
173 			GLchar* cs = FragGL;
174 			glShaderSource(fragmentshader, 1, &cs, &l);
175 
176 			//Compile
177 			glCompileShader(fragmentshader);
178 			int c;
179 			glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &c);
180 			if (c != GL_TRUE) {
181 				log_shader(fragmentshader);
182 				return;
183 			}
184 		}
185 	}
186 
187 	enum LogType {
188 		Program,
189 		Shader
190 	}
191 	
192 	private void log_shader(GLuint port, LogType type = LogType.Shader) {
193 		if (type == LogType.Shader) {
194 			int maxlen = 512;
195 			char[] logmsg;
196 			logmsg.length = maxlen;
197 			glGetShaderInfoLog(port, maxlen, &maxlen, logmsg.ptr);
198 			throw new Error("GLSLShaderError (shader: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen]));
199 		}
200 		int maxlen = 512;
201 		char[] logmsg;
202 		logmsg.length = maxlen;
203 		glGetProgramInfoLog(port, maxlen, &maxlen, logmsg.ptr);
204 		throw new Error("GLSLProgramError (program: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen]));
205 	}
206 }
207