1 module nanovg.glInterface;
2 
3 //
4 // OpenGL Interfacing D
5 // Copyright (c) 2015 S.Percentage
6 //
7 // This software is provided 'as-is', without any express or implied
8 // warranty.  In no event will the authors be held liable for any damages
9 // arising from the use of this software.
10 // Permission is granted to anyone to use this software for any purpose,
11 // including commercial applications, and to alter it and redistribute it
12 // freely, subject to the following restrictions:
13 // 1. The origin of this software must not be misrepresented; you must not
14 //    claim that you wrote the original software. If you use this software
15 //    in a product, an acknowledgment in the product documentation would be
16 //    appreciated but is not required.
17 // 2. Altered source versions must be plainly marked as such, and must not be
18 //    misrepresented as being the original software.
19 // 3. This notice may not be removed or altered from any source distribution.
20 //
21 
22 import derelict.opengl3.gl3, derelict.opengl3.gl;
23 import std.typecons, std.string, std.algorithm;
24 import std.experimental.logger;
25 
26 alias Size = Tuple!(int, "width", int, "height");
27 bool raised(int flag)(int flagSet) { return (flagSet & flag) != 0; }
28 
29 class GLShaderCompileError : Exception
30 {
31 	public this(GLuint shader, string type)
32 	{
33 		GLint logLength;
34 		GLchar[] log;
35 		
36 		shader.glGetShaderiv(GLShaderInfo.LogLength, &logLength);
37 		if(logLength > 0)
38 		{
39 			log.length = logLength;
40 			shader.glGetShaderInfoLog(logLength, null, log.ptr);
41 			super(type ~ " Compilation Error: " ~ log.ptr.fromStringz.idup);
42 		}
43 		else super(type ~ " Compilation Error: No info available.");
44 	}
45 }
46 class GLProgramLinkError : Exception
47 {
48 	public this(GLuint program)
49 	{
50 		GLint logLength;
51 		GLchar[] log;
52 		
53 		program.glGetProgramiv(GLProgramInfo.LogLength, &logLength);
54 		if(logLength > 0)
55 		{
56 			log.length = logLength;
57 			program.glGetProgramInfoLog(logLength, null, log.ptr);
58 			super("Program Linking Error: " ~ log.ptr.fromStringz.idup);
59 		}
60 		else super("Program Linking Error: No info available.");
61 	}
62 }
63 class GLException : Exception
64 {
65 	public this(GLenum errcode)
66 	{
67 		super("OpenGL Error: 0x" ~ format("%04x", errcode));
68 	}
69 }
70 void glCheckError()
71 {
72 	auto err = glGetError();
73 	if(err != GL_NO_ERROR) throw new GLException(err);
74 }
75 
76 enum GLShaderInfo : GLenum
77 {
78 	CompileStatus = GL_COMPILE_STATUS,
79 	LogLength = GL_INFO_LOG_LENGTH,
80 }
81 enum GLProgramInfo : GLenum
82 {
83 	LinkStatus = GL_LINK_STATUS,
84 	LogLength = GL_INFO_LOG_LENGTH
85 }
86 
87 static class GLPixelStore
88 {
89 	static class Parameter(GLenum E)
90 	{
91 		static void opAssign(int i)
92 		{
93 			glPixelStorei(E, i);
94 		}
95 	}
96 	
97 	alias Alignment = Parameter!GL_UNPACK_ALIGNMENT;
98 	alias RowLength = Parameter!GL_UNPACK_ROW_LENGTH;
99 	alias SkipPixels = Parameter!GL_UNPACK_SKIP_PIXELS;
100 	alias SkipRows = Parameter!GL_UNPACK_SKIP_ROWS;
101 }
102 static class GLTexture2D
103 {
104 	static class Parameter(GLenum E)
105 	{
106 		static void opAssign(int i)
107 		{
108 			glTexParameteri(GL_TEXTURE_2D, E, i);
109 		}
110 	}
111 	
112 	static class Wrap
113 	{
114 		alias S = Parameter!GL_TEXTURE_WRAP_S;
115 		alias T = Parameter!GL_TEXTURE_WRAP_T;
116 	}
117 	static class Filter
118 	{
119 		alias Min = Parameter!GL_TEXTURE_MIN_FILTER;
120 		alias Mag = Parameter!GL_TEXTURE_MAG_FILTER;
121 	}
122 }
123 static class ArrayData(GLenum T)
124 {
125 	static void opAssign(E)(E[] ary)
126 	{
127 		glBufferData(T, ary.length * E.sizeof, ary.ptr, GL_STREAM_DRAW);
128 		glCheckError();
129 	}
130 }
131 struct ByteRange
132 {
133 	size_t offset, length;
134 }
135 static class GLUniformBuffer
136 {
137 	alias ArrayData = .ArrayData!GL_UNIFORM_BUFFER;
138 	static class BindRange
139 	{
140 		static void opIndexAssign(ByteRange br, GLint buffer_name, GLint ui)
141 		{
142 			info(false, "BindBufferRange: ", buffer_name, "(shader binded through ", ui, ") <=> ", br);
143 			glBindBufferRange(GL_UNIFORM_BUFFER, ui, buffer_name, br.offset, br.length);
144 			glCheckError();
145 		}
146 	}
147 	
148 	static int opDispatch(string op)() if(op == "OffsetAlignment")
149 	{
150 		int v;
151 		glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &v);
152 		return v;
153 	}
154 }
155 struct DisablePointer { /* empty */ }
156 alias PlainFloatPointer = Tuple!(int, "dim", size_t, "stride", size_t, "offset");
157 static class GLArrayBuffer
158 {
159 	alias ArrayData = .ArrayData!GL_ARRAY_BUFFER;
160 	static class AttribPointer
161 	{
162 		static void opIndexAssign(PlainFloatPointer p, GLint l)
163 		{
164 			glEnableVertexAttribArray(l); glCheckError();
165 			glVertexAttribPointer(l, p.dim, GL_FLOAT, GL_FALSE,
166 				cast(GLint)p.stride, cast(const(GLvoid)*)p.offset);
167 			glCheckError();
168 		}
169 		static void opIndexAssign(DisablePointer p, GLint l)
170 		{
171 			glDisableVertexAttribArray(l);
172 			glCheckError();
173 		}
174 	}
175 }
176 static class GLProgram
177 {
178 	static class Uniform
179 	{
180 		static void opIndexAssign(GLint i, GLint l) out { glCheckError(); } body
181 		{
182 			glUniform1i(l, i);
183 		}
184 		static void opIndexAssign(float[2] v2, GLint l) out { glCheckError(); } body
185 		{
186 			glUniform2fv(l, 1, v2.ptr);
187 		}
188 	}
189 }
190 
191 const GLuint NullTexture = 0;
192 const GLuint NullSampler = 0;
193 const GLuint DisableProgram = 0;
194 alias GLBlendFuncParams = Tuple!(GLenum, "src", GLenum, "dst");
195 static class GLBlendPresets
196 {
197 	// src + dst * (1.0f - src.alpha)
198 	static const PremultipliedBlending = GLBlendFuncParams(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
199 }
200 alias GLStencilOpSet = Tuple!(GLenum, "stencilFail", GLenum, "depthFail", GLenum, "depthSucc");
201 static class GLStencilOpPresets
202 {
203 	static const Keep = GLStencilOpSet(GL_KEEP, GL_KEEP, GL_KEEP);
204 	static const Zero = GLStencilOpSet(GL_ZERO, GL_ZERO, GL_ZERO);
205 	static const IncrementOnSucceeded = GLStencilOpSet(GL_KEEP, GL_KEEP, GL_INCR);
206 }
207 alias GLStencilFuncParams = Tuple!(GLenum, "func", GLint, "refv", GLuint, "mask");
208 // Switch Constant
209 enum : bool { Disable = false, Enable = true }
210 enum GLFaceDirection : GLenum
211 {
212 	Front = GL_FRONT, Back = GL_BACK
213 }
214 enum GLPathDirection : GLenum
215 {
216 	Clockwise = GL_CW, CounterClockwise = GL_CCW
217 }
218 static class GLSwitchOptions(GLenum E)
219 {
220 	static void opAssign(bool sw) { (sw ? glEnable : glDisable)(E); }
221 }
222 static class GLTupleSetter(alias F, T)
223 {
224 	static void opAssign(T p) { F(p.expand); }
225 }
226 static class GLSingleSetter(alias F, T)
227 {
228 	static void opAssign(T p) { F(p); }
229 }
230 auto asGLbool(bool b) { return b ? GL_TRUE : GL_FALSE; }
231 static class GLContext
232 {
233 	static class BindTexture(GLenum E)
234 	{
235 		static void opAssign(GLuint i) out { glCheckError(); } body
236 		{
237 			glBindTexture(E, i);
238 		}
239 	}
240 	static class BindBuffer(GLenum E, T)
241 	{
242 		static void opAssign(T i) { info(false, "UniformBuffer?", E == GL_UNIFORM_BUFFER, " binding: ", i is null ? 0 : i.id); glBindBuffer(E, i is null ? 0 : i.id); }
243 	}
244 	static class VertexArray
245 	{
246 		static void opAssign(VertexArrayObject o) { glBindVertexArray(o is null ? 0 : o.id); }
247 	}
248 	static class RenderProgram
249 	{
250 		static void opAssign(GLuint p) { glUseProgram(p); }
251 	}
252 	static class Blend
253 	{
254 		alias Enable = GLSwitchOptions!GL_BLEND;
255 		alias Func = GLTupleSetter!(glBlendFunc, GLBlendFuncParams);
256 	}
257 	static class CullFace
258 	{
259 		alias Enable = GLSwitchOptions!GL_CULL_FACE;
260 		alias Direction = GLSingleSetter!(glCullFace, GLFaceDirection);
261 		alias FrontFace = GLSingleSetter!(glFrontFace, GLPathDirection);
262 	}
263 	static class DepthTest
264 	{
265 		alias Enable = GLSwitchOptions!GL_DEPTH_TEST;
266 	}
267 	static class ScissorTest
268 	{
269 		alias Enable = GLSwitchOptions!GL_SCISSOR_TEST;
270 	}
271 	static class Stencil
272 	{
273 		alias EnableTest = GLSwitchOptions!GL_STENCIL_TEST;
274 		alias Mask = GLSingleSetter!(glStencilMask, GLuint);
275 		static class Operations
276 		{
277 			static void opAssign(GLStencilOpSet args) { glStencilOp(args.expand); }
278 			static void opIndexAssign(GLStencilOpSet args, GLFaceDirection dir)
279 			{
280 				glStencilOpSeparate(dir, args.expand);
281 			}
282 		}
283 		alias Func = GLTupleSetter!(glStencilFunc, GLStencilFuncParams);
284 	}
285 	static class ActiveTexture
286 	{
287 		static void opAssign(GLuint index)
288 		{
289 			glActiveTexture(GL_TEXTURE0 + index);
290 			glCheckError();
291 		}
292 	}
293 	static class ColorMask
294 	{
295 		static void opAssign(bool[4] col)
296 		{
297 			glColorMask(col[0].asGLbool, col[1].asGLbool, col[2].asGLbool, col[3].asGLbool);
298 		}
299 	}
300 	
301 	alias Texture2D = BindTexture!GL_TEXTURE_2D;
302 	alias UniformBuffer = BindBuffer!(GL_UNIFORM_BUFFER, UniformBufferObject);
303 	alias ArrayBuffer = BindBuffer!(GL_ARRAY_BUFFER, ArrayBufferObject);
304 }
305 
306 // OpenGL Objects
307 class BufferObject
308 {
309 	GLuint buffer;
310 	
311 	@property id() { return this.buffer; }
312 	
313 	public this()
314 	{
315 		glGenBuffers(1, &this.buffer);
316 	}
317 	~this()
318 	{
319 		glDeleteBuffers(1, &this.buffer);
320 	}
321 }
322 alias UniformBufferObject = BufferObject;
323 alias ArrayBufferObject = BufferObject;
324 class VertexArrayObject
325 {
326 	GLuint buffer;
327 	@property id() { return this.buffer; }
328 	
329 	public this()
330 	{
331 		glGenVertexArrays(1, &this.buffer);
332 	}
333 	~this()
334 	{
335 		glDeleteVertexArrays(1, &this.buffer);
336 	}
337 }