1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Graphic.ShaderProgram;
25 
26 import derelict.opengl3.gl;
27 
28 import Dgame.Graphic.Color;
29 import Dgame.Graphic.Shader;
30 
31 import Dgame.Math.Vector2;
32 import Dgame.Math.Vector3;
33 import Dgame.Math.Matrix4x4;
34 
35 import Dgame.Internal.m3;
36 import Dgame.Internal.d2c;
37 import Dgame.Internal.Error;
38 
39 /**
40  * The Program struct manages a OpenGL-Shader-Program. Multiple Shaders can be attached to it.
41  * The Program links the attached Shaders and then they can be used.
42  *
43  * Author: Randy Schuett (rswhite4@googlemail.com)
44  */
45 struct ShaderProgram {
46 private:
47     uint _program;
48 
49     @nogc
50     void _init() nothrow {
51         if (_program == 0)
52             _program = glCreateProgram();
53     }
54 
55 public:
56     /**
57      * CTor
58      * Takes multiple Shader instances and links them together
59      *
60      * Note: The shaders must be already compiled.
61      */
62     @nogc
63     this(const Shader[] shaders...) nothrow {
64         this.link(shaders);
65     }
66 
67     /**
68      * Postblit is disabled
69      */
70     @disable
71     this(this);
72 
73     /**
74      * DTor
75      */
76     @nogc
77     ~this() nothrow {
78         glDeleteProgram(_program);
79     }
80 
81     /**
82      * Returns the internal Program ID
83      */
84     @nogc
85     @property
86     uint ID() const pure nothrow {
87         return _program;
88     }
89 
90     /**
91      * Links multiple Shaders together.
92      *
93      * Note: The shaders must be already compiled.
94      */
95     @nogc
96     bool link(const Shader[] shaders...) nothrow {
97         _init();
98 
99         // Attach the shaders
100         foreach (ref const Shader shader; shaders) {
101             glAttachShader(_program, shader.id);
102         }
103 
104         //Link our program
105         glLinkProgram(_program);
106 
107         int isLinked = 0;
108         glGetProgramiv(_program, GL_LINK_STATUS, &isLinked);
109         if (isLinked == GL_FALSE) {
110             int maxLength = 0;
111             glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &maxLength);
112             //The maxLength includes the NULL character
113             char[] infoLog = make!(char[])(maxLength);
114             scope(exit) unmake(infoLog);
115 
116             glGetProgramInfoLog(_program, maxLength, &maxLength, infoLog.ptr);
117             //We don't need the program anymore.
118             glDeleteProgram(_program);
119          
120             print_fmt("Shader-Program could not be compiled: %s\n", infoLog.ptr);
121 
122             return false;
123         }
124 
125         // Always detach shaders after a successful link.
126         foreach (ref const Shader shader; shaders) {
127             glDetachShader(_program, shader.id);
128         }
129 
130         return true;
131     }
132 
133     /**
134      * Bind and use the current program
135      */
136     @nogc
137     void bind() const nothrow {
138         glUseProgram(_program);
139     }
140 
141     /**
142      * Unbind the current program. It is no longer used.
143      */
144     @nogc
145     void unbind() const nothrow {
146         glUseProgram(0);
147     }
148 
149     /**
150      * Set between 1 and 4 parameter and binds them to variables of the Shaders
151      */
152     @nogc
153     bool setParameter(string name, const float[] values...) const nothrow {
154         immutable int loc = glGetUniformLocation(_program, toStringz(name));
155         if (loc == -1) {
156             print_fmt("No such variable %s\n", name.ptr);
157             return false;
158         }
159 
160         this.bind();
161 
162         switch (values.length) {
163             case 1:
164                 glUniform1f(loc, values[0]);
165             break;
166             case 2:
167                 glUniform2f(loc, values[0], values[1]);
168             break;
169             case 3:
170                 glUniform3f(loc, values[0], values[1], values[2]);
171             break;
172             case 4:
173                 glUniform4f(loc, values[0], values[1], values[2], values[3]);
174             break;
175             default:
176                 assert(0, "Need between 1 and 4 floats");
177         }
178 
179         return true;
180     }
181 
182     /**
183      * Bind a Vector2f to a specific variable name of the Shaders
184      */
185     @nogc
186     bool setParameter(string name, const Vector2f vec) const nothrow {
187         return this.setParameter(name, vec.x, vec.y);
188     }
189 
190     /**
191      * Bind a Vector3f to a specific variable name of the Shaders
192      */
193     @nogc
194     bool setParameter(string name, const Vector3f vec) const nothrow {
195         return this.setParameter(name, vec.x, vec.y, vec.z);
196     }
197 
198     /**
199      * Bind a Color4b to a specific variable name of the Shaders
200      */
201     @nogc
202     bool setParameter(string name, const Color4b color) const nothrow {
203         const Color4f col = color;
204         return this.setParameter(name, col.red, col.green, col.blue, col.alpha);
205     }
206 
207     /**
208      * Bind a Matrix4x4 to a specific variable name of the Shaders
209      */
210     @nogc
211     bool setParameter(string name, ref const Matrix4x4 mat) const nothrow {
212         immutable int loc = glGetUniformLocation(_program, toStringz(name));
213         if (loc == -1) {
214             print_fmt("No such variable %s\n", name.ptr);
215             return false;
216         }
217 
218         this.bind();
219 
220         glUniformMatrix4fv(loc, 1, GL_FALSE, mat.getValues().ptr);
221 
222         return true;
223     }
224 }