1 /*
2  * This file is part of gtkD.
3  *
4  * dui is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * dui is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with dui; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
17  */
18 
19 // To compile:
20 // [gdc] gdc CoreGL.d `pkg-config gtkd-3 gl --cflags --libs`
21 // [dmd] rdmd `pkg-config gtkd-3 --cflags` -L-lGL -L-ldl CoreGL.d
22 
23 module coreGL.CoreGL;
24 
25 import std.string;
26 
27 import gio.Application : GioApplication = Application;
28 import gtk.Application;
29 import gtk.ApplicationWindow;
30 import gdk.GLContext;
31 import gtk.DrawingArea;
32 import gtk.GLArea;
33 import gtk.Widget;
34 
35 import glcore;
36 
37 int main(string[] args)
38 {
39 	Application application;
40 
41 	void activateCoreGL(GioApplication app)
42 	{
43     auto mainWnd = new ApplicationWindow(application);
44     mainWnd.setTitle("Simplest GLArea + CoreGL Example");
45 
46     auto wnd = new CoreGL;
47     mainWnd.add(wnd);
48     mainWnd.showAll();
49 	}
50 
51 	application = new Application("org.gtkd.demo.gl.core", GApplicationFlags.FLAGS_NONE);
52 	application.addOnActivate(&activateCoreGL);
53 	return application.run(args);
54 }
55 
56 /**
57  * A really simple Demo illustrating OpenGL core profile with GtkD
58  * This example is provided under the terms of the GPL License.
59  *
60  * @author sebastien.alaiwan@gmail.com
61  */
62 class CoreGL : GLArea
63 {
64 public:
65   this()
66   {
67     setAutoRender(true);
68 
69     addEvents(GdkEventMask.BUTTON_PRESS_MASK);
70     addEvents(GdkEventMask.SCROLL_MASK);
71 
72     addOnRender(&render);
73     addOnRealize(&realize);
74     addOnUnrealize(&unrealize);
75 
76     showAll();
77   }
78 
79   GLuint m_Vao;
80   GLuint m_Program;
81   GLuint m_Mvp;
82 
83   void realize(Widget)
84   {
85     makeCurrent();
86     GLuint position_index;
87     GLuint color_index;
88     initShaders(&m_Program, &m_Mvp, &position_index, &color_index);
89     initBuffers(position_index, color_index);
90   }
91 
92   void unrealize(Widget)
93   {
94     makeCurrent();
95     glDeleteBuffers(1, &m_Vao);
96     glDeleteProgram(m_Program);
97   }
98 
99   bool render(GLContext c, GLArea a)
100   {
101     makeCurrent();
102 
103     glClearColor(0.3, 0.3, 0.3, 1);
104     glClear(GL_COLOR_BUFFER_BIT);
105 
106     drawTriangle();
107 
108     glFlush();
109 
110     return true;
111   }
112 
113   void drawTriangle()
114   {
115     immutable mvp = getIdentityMatrix();
116 
117     glUseProgram(m_Program);
118 
119     // update the "mvp" matrix we use in the shader
120     glUniformMatrix4fv(m_Mvp, 1, GL_FALSE, mvp.ptr);
121 
122     glBindBuffer(GL_ARRAY_BUFFER, m_Vao);
123     glEnableVertexAttribArray(0);
124     glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, null);
125 
126     // draw the three vertices as a triangle
127     glDrawArrays(GL_TRIANGLES, 0, 3);
128 
129     glDisableVertexAttribArray(0);
130     glBindBuffer(GL_ARRAY_BUFFER, 0);
131     glUseProgram(0);
132   }
133 
134   void initBuffers(uint position_index, uint color_index)
135   {
136     static immutable GLfloat[] vertex_data =
137     [
138       0.0f,   0.5f,   0.0f, 1.0f,
139       0.5f, -0.366f, 0.0f, 1.0f,
140       -0.5f, -0.366f, 0.0f, 1.0f,
141     ];
142 
143     // we need to create a VAO to store the other buffers
144     glGenVertexArrays(1, &m_Vao);
145     glBindVertexArray(m_Vao);
146 
147     // this is the VBO that holds the vertex data
148     GLuint buffer;
149     glGenBuffers(1, &buffer);
150     glBindBuffer(GL_ARRAY_BUFFER, buffer);
151     glBufferData(GL_ARRAY_BUFFER, vertex_data.length * float.sizeof, vertex_data.ptr, GL_STATIC_DRAW);
152 
153     // reset the state; we will re-enable the VAO when needed
154     glBindBuffer(GL_ARRAY_BUFFER, 0);
155   }
156 }
157 
158 void initShaders(uint* program_out, uint* mvp_location_out, uint* position_location_out, uint* color_location_out)
159 {
160   const vertex = compileShader(GL_VERTEX_SHADER, VertShaderCode ~ "\0");
161   scope(exit) glDeleteShader(vertex);
162 
163   const fragment = compileShader(GL_FRAGMENT_SHADER, FragShaderCode ~ "\0");
164   scope(exit) glDeleteShader(fragment);
165 
166   const program = glCreateProgram();
167 
168   glAttachShader(program, vertex);
169   scope(exit) glDetachShader(program, vertex);
170 
171   glAttachShader(program, fragment);
172   scope(exit) glDetachShader(program, fragment);
173 
174   glLinkProgram(program);
175 
176   int status = 0;
177   glGetProgramiv(program, GL_LINK_STATUS, &status);
178 
179   if(status == GL_FALSE)
180   {
181     int len = 0;
182     glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
183 
184     char[] buffer;
185     buffer.length = len + 1;
186     glGetProgramInfoLog(program, len, null, buffer.ptr);
187 
188     glDeleteProgram(program);
189 
190     throw new Exception(format("Linking failure in program: %s", buffer));
191   }
192 
193   *program_out = program;
194   *mvp_location_out = glGetUniformLocation(program, "mvp");
195   *position_location_out = glGetAttribLocation(program, "position");
196   *color_location_out = glGetAttribLocation(program, "color");
197 }
198 
199 uint compileShader(int type, string source)
200 {
201   const shader = glCreateShader(type);
202   scope(failure) glDeleteShader(shader);
203   const(char)*srcPtr = source.ptr;
204   glShaderSource(shader, 1, &srcPtr, null);
205   glCompileShader(shader);
206 
207   int status;
208   glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
209 
210   if(status == GL_FALSE)
211   {
212     int len;
213     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
214 
215     char[] buffer;
216     buffer.length = len + 1;
217     glGetShaderInfoLog(shader, len, null, buffer.ptr);
218 
219     const sType = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
220 
221     throw new Exception(format("Compilation failure in %s shader: %s", sType, buffer));
222   }
223 
224   return shader;
225 }
226 
227 float[16] getIdentityMatrix() pure
228 {
229   float[4 * 4] mat;
230 
231   // identity matrix
232   for(int x=0;x < 4;++x)
233     for(int y=0;y < 4;++y)
234       mat[x+y*4] = x==y ? 1 : 0;
235 
236   return mat;
237 }
238 
239 immutable FragShaderCode = `
240 #version 130
241 
242 smooth in vec4 vertexColor;
243 
244 out vec4 outputColor;
245 
246 void main() {
247   outputColor = vertexColor;
248 }`;
249 
250 immutable VertShaderCode = `
251 #version 130
252 
253 in vec3 position;
254 in vec3 color;
255 
256 uniform mat4 mvp;
257 
258 smooth out vec4 vertexColor;
259 
260 void main() {
261   gl_Position = mvp * vec4(position, 1.0);
262   vertexColor = vec4(color, 1.0);
263 }`;
264