1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module inochi2d.core.shader;
8 import inochi2d.math;
9 import std.string;
10 import bindbc.opengl;
11 
12 /**
13     A shader
14 */
15 class Shader {
16 private:
17     GLuint shaderProgram;
18     GLuint fragShader;
19     GLuint vertShader;
20 
21     void compileShaders(string vertex, string fragment) {
22 
23         // Compile vertex shader
24         vertShader = glCreateShader(GL_VERTEX_SHADER);
25         auto c_vert = vertex.toStringz;
26         glShaderSource(vertShader, 1, &c_vert, null);
27         glCompileShader(vertShader);
28         verifyShader(vertShader);
29 
30         // Compile fragment shader
31         fragShader = glCreateShader(GL_FRAGMENT_SHADER);
32         auto c_frag = fragment.toStringz;
33         glShaderSource(fragShader, 1, &c_frag, null);
34         glCompileShader(fragShader);
35         verifyShader(fragShader);
36 
37         // Attach and link them
38         shaderProgram = glCreateProgram();
39         glAttachShader(shaderProgram, vertShader);
40         glAttachShader(shaderProgram, fragShader);
41         glLinkProgram(shaderProgram);
42         verifyProgram();
43     }
44 
45     void verifyShader(GLuint shader) {
46 
47         int compileStatus;
48         glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
49         if (compileStatus == GL_FALSE) {
50 
51             // Get the length of the error log
52             GLint logLength;
53             glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
54             if (logLength > 0) {
55 
56                 // Fetch the error log
57                 char[] log = new char[logLength];
58                 glGetShaderInfoLog(shader, logLength, null, log.ptr);
59 
60                 throw new Exception(cast(string)log);
61             }
62         }
63     }
64 
65     void verifyProgram() {
66 
67         int linkStatus;
68         glGetProgramiv(shaderProgram, GL_LINK_STATUS, &linkStatus);
69         if (linkStatus == GL_FALSE) {
70 
71             // Get the length of the error log
72             GLint logLength;
73             glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logLength);
74             if (logLength > 0) {
75 
76                 // Fetch the error log
77                 char[] log = new char[logLength];
78                 glGetProgramInfoLog(shaderProgram, logLength, null, log.ptr);
79 
80                 throw new Exception(cast(string)log);
81             }
82         }
83     }
84 
85 public:
86 
87     /**
88         Destructor
89     */
90     ~this() {
91         glDetachShader(shaderProgram, vertShader);
92         glDetachShader(shaderProgram, fragShader);
93         glDeleteProgram(shaderProgram);
94         
95         glDeleteShader(fragShader);
96         glDeleteShader(vertShader);
97     }
98 
99     /**
100         Creates a new shader object from source
101     */
102     this(string vertex, string fragment) {
103         compileShaders(vertex, fragment);
104     }
105 
106     /**
107         Use the shader
108     */
109     void use() {
110         glUseProgram(shaderProgram);
111     }
112 
113     GLint getUniformLocation(string name) {
114         return glGetUniformLocation(shaderProgram, name.ptr);
115     }
116 
117     void setUniform(GLint uniform, bool value) {
118         glUniform1i(uniform, cast(int)value);
119     }
120 
121     void setUniform(GLint uniform, int value) {
122         glUniform1i(uniform, value);
123     }
124 
125     void setUniform(GLint uniform, float value) {
126         glUniform1f(uniform, value);
127     }
128 
129     void setUniform(GLint uniform, vec2 value) {
130         glUniform2f(uniform, value.x, value.y);
131     }
132 
133     void setUniform(GLint uniform, vec3 value) {
134         glUniform3f(uniform, value.x, value.y, value.z);
135     }
136 
137     void setUniform(GLint uniform, vec4 value) {
138         glUniform4f(uniform, value.x, value.y, value.z, value.w);
139     }
140 
141     void setUniform(GLint uniform, mat4 value) {
142         glUniformMatrix4fv(uniform, 1, GL_TRUE, value.ptr);
143     }
144 }