1 module gfm.opengl.shader;
2 
3 import std..string,
4        std.conv;
5 
6 import derelict.opengl3.gl3;
7 
8 import gfm.core.log,
9        gfm.core.text,
10        gfm.opengl.opengl;
11 
12 
13 /// OpenGL Shader wrapper.
14 final class GLShader
15 {
16     public
17     {
18         /// Creates a shader devoid of source code.
19         /// Throws: $(D OpenGLException) on error.
20         this(OpenGL gl, GLenum shaderType)
21         {
22             _gl = gl;
23             _shader = glCreateShader(shaderType);
24             if (_shader == 0)
25                 throw new OpenGLException("glCreateShader failed");
26             _initialized = true;
27         }
28 
29         /// Creates a shader with source code and compiles it.
30         /// Throws: $(D OpenGLException) on error.
31         this(OpenGL gl, GLenum shaderType, string[] lines)
32         {
33             this(gl, shaderType);
34             load(lines);
35             compile();
36         }
37 
38         ~this()
39         {
40             close();
41         }
42 
43         /// Releases the OpenGL shader resource.
44         void close()
45         {
46             if (_initialized)
47             {
48                 glDeleteShader(_shader);
49                 _initialized = false;
50             }
51         }
52 
53         /// Load source code for this shader.
54         /// Throws: $(D OpenGLException) on error.
55         void load(string[] lines)
56         {
57             size_t lineCount = lines.length;
58 
59             auto lengths = new GLint[lineCount];
60             auto addresses = new immutable(GLchar)*[lineCount];
61             auto localLines = new string[lineCount];
62 
63             for (size_t i = 0; i < lineCount; ++i)
64             {
65                 localLines[i] = lines[i] ~ "\n";
66                 lengths[i] = cast(GLint)(localLines[i].length);
67                 addresses[i] = localLines[i].ptr;
68             }
69 
70             glShaderSource(_shader,
71                            cast(GLint)lineCount,
72                            cast(const(char)**)addresses.ptr,
73                            cast(const(int)*)(lengths.ptr));
74             _gl.runtimeCheck();
75         }
76 
77         /// Compile this OpenGL shader.
78         /// Throws: $(D OpenGLException) on compilation error.
79         void compile()
80         {
81             glCompileShader(_shader);
82             _gl.runtimeCheck();
83 
84             // print info log
85             string infoLog = getInfoLog();
86             if (infoLog != null)
87                 _gl._logger.info(infoLog);
88 
89             GLint compiled;
90             glGetShaderiv(_shader, GL_COMPILE_STATUS, &compiled);
91 
92             if (compiled != GL_TRUE)
93                 throw new OpenGLException("shader did not compile");
94         }
95 
96         /// Gets the compiling report. 
97         /// Returns: Log output of the GLSL compiler. Can return null!
98         /// Throws: $(D OpenGLException) on error.
99         string getInfoLog()
100         {
101             GLint logLength;
102             glGetShaderiv(_shader, GL_INFO_LOG_LENGTH, &logLength);
103             if (logLength <= 0) // "If shader has no information log, a value of 0 is returned."
104                 return null;
105 
106             char[] log = new char[logLength];
107             GLint dummy;
108             glGetShaderInfoLog(_shader, logLength, &dummy, log.ptr);
109             _gl.runtimeCheck();
110             return sanitizeUTF8(log.ptr);
111         }
112     }
113 
114     package
115     {
116         GLuint _shader;
117     }
118 
119     private
120     {
121         OpenGL _gl;
122         bool _initialized;
123     }
124 }
125 
126