1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module g4d.shader.base;
8 import g4d.gl.buffer,
9        g4d.gl.lib,
10        g4d.gl.texture,
11        g4d.shader.matrix,
12        g4d.exception;
13 import gl3n.linalg;
14 import std.conv,
15        std.string;
16 
17 /// A baseclass of shader.
18 abstract class Shader
19 {
20     protected static GLuint compileShader ( GLenum shaderType, string src )
21     {
22         const csrc = src.toStringz;
23 
24         GLuint shader = enforce!glCreateShader( shaderType );
25         enforce!glShaderSource( shader, 1, &csrc, null );
26         enforce!glCompileShader( shader );
27 
28         GLint status;
29         enforce!glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
30         if ( status == GL_FALSE ) {
31             ShaderException.throwShaderLog( shader );
32         }
33         return shader;
34     }
35 
36     /// Invalid shader program id.
37     enum NullId = 0;
38 
39     protected GLuint _vertex;
40     protected GLuint _fragment;
41     protected GLuint _program;
42     protected GLuint _vao;
43 
44     protected ShaderMatrix _matrix;
45     /// Data of translation, rotation and transformation.
46     @property ref matrix () { return _matrix; }
47 
48     /// GLSL source code of vertex shader.
49     const pure @property string vertexSource ();
50     /// GLSL source code of fragment shader.
51     const pure @property string fragSource ();
52 
53     ///
54     this ()
55     {
56         _vertex = compileShader( GL_VERTEX_SHADER, vertexSource );
57         scope(failure) enforce!glDeleteShader( _vertex );
58 
59         _fragment = compileShader( GL_FRAGMENT_SHADER, fragSource );
60         scope(failure) enforce!glDeleteShader( _fragment );
61 
62         _program = enforce!glCreateProgram();
63         scope(failure) enforce!glDeleteProgram( _program );
64         enforce!glAttachShader( _program, _vertex );
65         enforce!glAttachShader( _program, _fragment );
66         enforce!glLinkProgram( _program );
67 
68         GLint status;
69         enforce!glGetProgramiv( _program, GL_LINK_STATUS, &status );
70         if ( status == GL_FALSE ) {
71             ShaderException.throwProgramLog( _program );
72         }
73         initVertexShader();
74         initFragShader();
75 
76         enforce!glGenVertexArrays( 1, &_vao );
77         use();
78     }
79 
80     ///
81     ~this ()
82     {
83         dispose();
84     }
85     /// Checks if the shader is disposed.
86     const @property disposed ()
87     {
88         return _program == NullId;
89     }
90     /// Deletes all programs and shaders.
91     void dispose ()
92     {
93         if ( !disposed ) {
94             enforce!glDeleteVertexArrays( 1, &_vao );
95             enforce!glDeleteProgram( _program );
96             enforce!glDeleteShader( _vertex );
97             enforce!glDeleteShader( _fragment );
98         }
99         _program = NullId;
100     }
101 
102     protected GLint getUniformLoc ( string name )
103     {
104         return enforce!glGetUniformLocation( _program, name.toStringz );
105     }
106     protected GLint getAttribLoc ( string name )
107     {
108         return enforce!glGetAttribLocation( _program, name.toStringz );
109     }
110 
111     protected void initVertexShader ();
112     protected void initFragShader ();
113 
114     /// Checks the shader supports texture.
115     const @property bool textureSupport () { return false; }
116 
117     /// Sets the shader binded.
118     const void use ()
119     {
120         enforce!glUseProgram( _program );
121         enforce!glBindVertexArray( _vao );
122     }
123 
124     /// Uploads matrix.
125     void uploadMatrix ( mat4 );
126 
127     /// Uploads position buffer.
128     void uploadPositionBuffer ( in ArrayBuffer );
129 
130     /// Uploads uv buffer.
131     void uploadUvBuffer ( in ArrayBuffer )
132     {
133         throw new ShaderException( "This shader doesn't support texture." );
134     }
135     /// Uploads texture.
136     void uploadTexture ( in Texture )
137     {
138         throw new ShaderException( "This shader doesn't support texture." );
139     }
140 
141     /// Applies the matrix.
142     void applyMatrix ()
143     {
144         uploadMatrix( _matrix.cache );
145     }
146 
147     /// Calls glDrawArrays with GL_TRIANGLE_FAN.
148     void drawFan ( size_t polyCnt )
149     {
150         enforce!glDrawArrays( GL_TRIANGLE_FAN, 0, polyCnt.to!int );
151     }
152     /// Calls glDrawArrays with GL_TRIANGLE_STRIP.
153     void drawStrip ( size_t polyCnt )
154     {
155         enforce!glDrawArrays( GL_TRIANGLE_STRIP, 0, polyCnt.to!int );
156     }
157 }
158 
159 /// An exception type used in shader modules.
160 class ShaderException : G4dException
161 {
162     /// Checks if the shader throwed errors, and throws it.
163     static void throwShaderLog ( GLuint shader, string file = __FILE__, size_t line = __LINE__ )
164     {
165         GLint logLength = 0;
166         enforce!glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &logLength );
167 
168         auto log = new char[logLength];
169         enforce!glGetShaderInfoLog( shader, logLength, &logLength, log.ptr );
170         throw new ShaderException( "ShaderLog: "~log.to!string, file, line );
171     }
172 
173     /// Checks if the shader program throwed errors, and throws it.
174     static void throwProgramLog ( GLuint program, string file = __FILE__, size_t line = __LINE__ )
175     {
176         GLint logLength = 0;
177         enforce!glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logLength );
178 
179         auto log = new char[logLength];
180         enforce!glGetProgramInfoLog( program, logLength, &logLength, log.ptr );
181         throw new ShaderException( "ProgramLog: "~log.to!string, file, line );
182     }
183 
184     ///
185     this ( string msg, string file = __FILE__, size_t line = __LINE__ )
186     {
187         super( msg, file, line );
188     }
189 }
190 
191 /// A struct that saves status of the shader,
192 /// and restores the shader using saved status.
193 struct ShaderStateSaver
194 {
195     protected Shader       _target;
196     protected ShaderMatrix _backup;
197 
198     ///
199     this ( Shader s ) {
200         _target = s;
201         _backup = s.matrix;
202     }
203 
204     ///
205     ~this ()
206     {
207         _target.matrix = _backup;
208     }
209 }