]> git.localhorst.tv Git - blobs.git/blob - src/graphics/shader.cpp
allow alpha in plain color shader
[blobs.git] / src / graphics / shader.cpp
1 #include "AlphaSprite.hpp"
2 #include "CreatureSkin.hpp"
3 #include "PlainColor.hpp"
4 #include "PlanetSurface.hpp"
5 #include "Program.hpp"
6 #include "Shader.hpp"
7 #include "SunSurface.hpp"
8
9 #include "ArrayTexture.hpp"
10 #include "Texture.hpp"
11 #include "../app/init.hpp"
12
13 #include <algorithm>
14 #include <iostream>
15 #include <memory>
16 #include <ostream>
17 #include <stdexcept>
18 #include <string>
19 #include <glm/gtc/type_ptr.hpp>
20
21
22 namespace {
23
24 void gl_error(std::string msg) {
25         const GLubyte *errBegin = gluErrorString(glGetError());
26         if (errBegin && *errBegin != '\0') {
27                 const GLubyte *errEnd = errBegin;
28                 while (*errEnd != '\0') {
29                         ++errEnd;
30                 }
31                 msg += ": ";
32                 msg.append(errBegin, errEnd);
33         }
34         throw std::runtime_error(msg);
35 }
36
37 }
38
39 namespace blobs {
40 namespace graphics {
41
42 Shader::Shader(GLenum type)
43 : handle(glCreateShader(type)) {
44         if (handle == 0) {
45                 gl_error("glCreateShader");
46         }
47 }
48
49 Shader::~Shader() {
50         if (handle != 0) {
51                 glDeleteShader(handle);
52         }
53 }
54
55 Shader::Shader(Shader &&other) noexcept
56 : handle(other.handle) {
57         other.handle = 0;
58 }
59
60 Shader &Shader::operator =(Shader &&other) noexcept {
61         std::swap(handle, other.handle);
62         return *this;
63 }
64
65
66 void Shader::Source(const GLchar *src) noexcept {
67         const GLchar* src_arr[] = { src };
68         glShaderSource(handle, 1, src_arr, nullptr);
69 }
70
71 void Shader::Compile() noexcept {
72         glCompileShader(handle);
73 }
74
75 bool Shader::Compiled() const noexcept {
76         GLint compiled = GL_FALSE;
77         glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
78         return compiled == GL_TRUE;
79 }
80
81 void Shader::Log(std::ostream &out) const {
82         int log_len = 0, max_len = 0;
83         glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len);
84         std::unique_ptr<char[]> log(new char[max_len]);
85         glGetShaderInfoLog(handle, max_len, &log_len, log.get());
86         out.write(log.get(), log_len);
87 }
88
89
90 void Shader::AttachToProgram(GLuint id) const noexcept {
91         glAttachShader(id, handle);
92 }
93
94
95 Program::Program()
96 : handle(glCreateProgram()) {
97         if (handle == 0) {
98                 gl_error("glCreateProgram");
99         }
100 }
101
102 Program::~Program() {
103         if (handle != 0) {
104                 glDeleteProgram(handle);
105         }
106 }
107
108
109 const Shader &Program::LoadShader(GLenum type, const GLchar *src) {
110         shaders.emplace_back(type);
111         Shader &shader = shaders.back();
112         shader.Source(src);
113         shader.Compile();
114         if (!shader.Compiled()) {
115                 shader.Log(std::cerr);
116                 throw std::runtime_error("compile shader");
117         }
118         Attach(shader);
119         return shader;
120 }
121
122 void Program::Attach(Shader &shader) noexcept {
123         shader.AttachToProgram(handle);
124 }
125
126 void Program::Link() noexcept {
127         glLinkProgram(handle);
128 }
129
130 bool Program::Linked() const noexcept {
131         GLint linked = GL_FALSE;
132         glGetProgramiv(handle, GL_LINK_STATUS, &linked);
133         return linked == GL_TRUE;
134 }
135
136 void Program::Log(std::ostream &out) const {
137         int log_len = 0, max_len = 0;
138         glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len);
139         std::unique_ptr<char[]> log(new char[max_len]);
140         glGetProgramInfoLog(handle, max_len, &log_len, log.get());
141         out.write(log.get(), log_len);
142 }
143
144
145 GLint Program::AttributeLocation(const GLchar *name) const noexcept {
146         return glGetAttribLocation(handle, name);
147 }
148
149 GLint Program::AttributeLocation(const std::string &name) const noexcept {
150         return AttributeLocation(name.c_str());
151 }
152
153 GLint Program::UniformLocation(const GLchar *name) const noexcept {
154         return glGetUniformLocation(handle, name);
155 }
156
157 GLint Program::UniformLocation(const std::string &name) const noexcept {
158         return UniformLocation(name.c_str());
159 }
160
161
162 void Program::Uniform(GLint loc, GLint val) noexcept {
163         glUniform1i(loc, val);
164 }
165
166 void Program::Uniform(GLint loc, float val) noexcept {
167         glUniform1f(loc, val);
168 }
169
170 void Program::Uniform(GLint loc, const glm::vec3 &val) noexcept {
171         glUniform3fv(loc, 1, glm::value_ptr(val));
172 }
173
174 void Program::Uniform(GLint loc, const glm::vec4 &val) noexcept {
175         glUniform4fv(loc, 1, glm::value_ptr(val));
176 }
177
178 void Program::Uniform(GLint loc, const glm::mat4 &val) noexcept {
179         glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val));
180 }
181
182
183 constexpr int PlanetSurface::MAX_LIGHTS;
184
185 PlanetSurface::PlanetSurface()
186 : prog() {
187         prog.LoadShader(
188                 GL_VERTEX_SHADER,
189                 "#version 330 core\n"
190
191                 "layout(location = 0) in vec3 vtx_position;\n"
192                 "layout(location = 1) in vec3 vtx_tex_uv;\n"
193
194                 "uniform mat4 M;\n"
195                 "uniform mat4 MV;\n"
196                 "uniform mat4 MVP;\n"
197
198                 "out vec3 frag_tex_uv;\n"
199                 "out vec3 vtx_viewspace;\n"
200
201                 "void main() {\n"
202                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
203                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
204                         "frag_tex_uv = vtx_tex_uv;\n"
205                 "}\n"
206         );
207         prog.LoadShader(
208                 GL_FRAGMENT_SHADER,
209                 "#version 330 core\n"
210
211                 "struct LightSource {\n"
212                         "vec3 position;\n"
213                         "vec3 color;\n"
214                         "float strength;\n"
215                 "};\n"
216
217                 "in vec3 vtx_viewspace;\n"
218                 "in vec3 frag_tex_uv;\n"
219
220                 "uniform sampler2DArray tex_sampler;\n"
221                 "uniform vec3 normal;\n"
222                 "uniform int num_lights;\n"
223                 "uniform LightSource light[8];\n"
224
225                 "out vec3 color;\n"
226
227                 "void main() {\n"
228                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
229                         "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
230                         "for (int i = 0; i < num_lights; ++i) {\n"
231                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
232                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
233                                 "vec3 light_dir = normalize(to_light);\n"
234                                 "float attenuation = light[i].strength / (distance * distance);\n"
235                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
236                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
237                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
238                                 "if (dot(normal, light_dir) >= 0.0) {\n"
239                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
240                                 "}\n"
241                                 "total_light = total_light + diffuse + specular;\n"
242                         "}\n"
243                         "color = total_light;\n"
244                 "}\n"
245         );
246         prog.Link();
247         if (!prog.Linked()) {
248                 prog.Log(std::cerr);
249                 throw std::runtime_error("link program");
250         }
251         m_handle = prog.UniformLocation("M");
252         mv_handle = prog.UniformLocation("MV");
253         mvp_handle = prog.UniformLocation("MVP");
254         sampler_handle = prog.UniformLocation("tex_sampler");
255         normal_handle = prog.UniformLocation("normal");
256         num_lights_handle = prog.UniformLocation("num_lights");
257         for (int i = 0; i < MAX_LIGHTS; ++i) {
258                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
259                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
260                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
261         }
262 }
263
264 PlanetSurface::~PlanetSurface() {
265 }
266
267 void PlanetSurface::Activate() noexcept {
268         prog.Use();
269         glEnable(GL_DEPTH_TEST);
270         glDepthFunc(GL_LESS);
271         glEnable(GL_CULL_FACE);
272         glDisable(GL_BLEND);
273 }
274
275 void PlanetSurface::SetM(const glm::mat4 &mm) noexcept {
276         m = mm;
277         mv = v * m;
278         mvp = p * mv;
279         prog.Uniform(m_handle, m);
280         prog.Uniform(mv_handle, mv);
281         prog.Uniform(mvp_handle, mvp);
282 }
283
284 void PlanetSurface::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
285         v = vv;
286         p = pp;
287         mv = v * m;
288         mvp = p * mv;
289         prog.Uniform(mv_handle, mv);
290         prog.Uniform(mvp_handle, mvp);
291 }
292
293 void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
294         m = mm;
295         v = vv;
296         p = pp;
297         mv = v * m;
298         mvp = p * mv;
299         prog.Uniform(m_handle, m);
300         prog.Uniform(mv_handle, mv);
301         prog.Uniform(mvp_handle, mvp);
302 }
303
304 void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
305         prog.Uniform(normal_handle, n);
306 }
307
308 void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
309         glActiveTexture(GL_TEXTURE0);
310         tex.Bind();
311         prog.Uniform(sampler_handle, GLint(0));
312 }
313
314 void PlanetSurface::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
315         prog.Uniform(light_handle[3 * n + 0], pos);
316         prog.Uniform(light_handle[3 * n + 1], color);
317         prog.Uniform(light_handle[3 * n + 2], strength);
318 }
319
320 void PlanetSurface::SetNumLights(int n) noexcept {
321         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
322 }
323
324
325 SunSurface::SunSurface()
326 : prog() {
327         prog.LoadShader(
328                 GL_VERTEX_SHADER,
329                 "#version 330 core\n"
330
331                 "layout(location = 0) in vec3 vtx_position;\n"
332
333                 "uniform mat4 M;\n"
334                 "uniform mat4 MV;\n"
335                 "uniform mat4 MVP;\n"
336
337                 "out vec3 vtx_viewspace;\n"
338
339                 "void main() {\n"
340                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
341                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
342                 "}\n"
343         );
344         prog.LoadShader(
345                 GL_FRAGMENT_SHADER,
346                 "#version 330 core\n"
347
348                 "in vec3 vtx_viewspace;\n"
349
350                 "uniform vec3 light_color;\n"
351                 "uniform float light_strength;\n"
352
353                 "out vec3 color;\n"
354
355                 "void main() {\n"
356                         "vec3 to_light = -vtx_viewspace;\n"
357                         "float distance = length(to_light);\n"
358                         //"vec3 light_dir = normalize(to_light);\n"
359                         "float attenuation = light_strength / (distance * distance);\n"
360                         "color = attenuation * light_color;\n"
361                 "}\n"
362         );
363         prog.Link();
364         if (!prog.Linked()) {
365                 prog.Log(std::cerr);
366                 throw std::runtime_error("link program");
367         }
368         m_handle = prog.UniformLocation("M");
369         mv_handle = prog.UniformLocation("MV");
370         mvp_handle = prog.UniformLocation("MVP");
371         light_color_handle = prog.UniformLocation("light_color");
372         light_strength_handle = prog.UniformLocation("light_strength");
373
374         vao.Bind();
375         vao.BindAttributes();
376         vao.EnableAttribute(0);
377         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
378         vao.ReserveAttributes(8, GL_STATIC_DRAW);
379         {
380                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
381                 attrib[0].position = glm::vec3(-1.0f, -1.0f, -1.0f);
382                 attrib[1].position = glm::vec3(-1.0f, -1.0f,  1.0f);
383                 attrib[2].position = glm::vec3(-1.0f,  1.0f, -1.0f);
384                 attrib[3].position = glm::vec3(-1.0f,  1.0f,  1.0f);
385                 attrib[4].position = glm::vec3( 1.0f, -1.0f, -1.0f);
386                 attrib[5].position = glm::vec3( 1.0f, -1.0f,  1.0f);
387                 attrib[6].position = glm::vec3( 1.0f,  1.0f, -1.0f);
388                 attrib[7].position = glm::vec3( 1.0f,  1.0f,  1.0f);
389         }
390         vao.BindElements();
391         vao.ReserveElements(36, GL_STATIC_DRAW);
392         {
393                 auto element = vao.MapElements(GL_WRITE_ONLY);
394                 // -X
395                 element[ 0] = 0;
396                 element[ 1] = 1;
397                 element[ 2] = 2;
398                 element[ 3] = 2;
399                 element[ 4] = 1;
400                 element[ 5] = 3;
401                 // -Y
402                 element[ 6] = 0;
403                 element[ 7] = 4;
404                 element[ 8] = 1;
405                 element[ 9] = 1;
406                 element[10] = 4;
407                 element[11] = 5;
408                 // -Z
409                 element[12] = 0;
410                 element[13] = 2;
411                 element[14] = 4;
412                 element[15] = 4;
413                 element[16] = 2;
414                 element[17] = 6;
415                 // +Z
416                 element[18] = 1;
417                 element[19] = 5;
418                 element[20] = 3;
419                 element[21] = 3;
420                 element[22] = 5;
421                 element[23] = 7;
422                 // +Y
423                 element[24] = 3;
424                 element[25] = 7;
425                 element[26] = 2;
426                 element[27] = 2;
427                 element[28] = 7;
428                 element[29] = 6;
429                 // +X
430                 element[30] = 5;
431                 element[31] = 4;
432                 element[32] = 7;
433                 element[33] = 7;
434                 element[34] = 4;
435                 element[35] = 6;
436         }
437         vao.Unbind();
438 }
439
440 SunSurface::~SunSurface() {
441 }
442
443 void SunSurface::Activate() noexcept {
444         prog.Use();
445         glEnable(GL_DEPTH_TEST);
446         glDepthFunc(GL_LESS);
447         glEnable(GL_CULL_FACE);
448         glDisable(GL_BLEND);
449 }
450
451 void SunSurface::SetM(const glm::mat4 &mm) noexcept {
452         m = mm;
453         mv = v * m;
454         mvp = p * mv;
455         prog.Uniform(m_handle, m);
456         prog.Uniform(mv_handle, mv);
457         prog.Uniform(mvp_handle, mvp);
458 }
459
460 void SunSurface::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
461         v = vv;
462         p = pp;
463         mv = v * m;
464         mvp = p * mv;
465         prog.Uniform(mv_handle, mv);
466         prog.Uniform(mvp_handle, mvp);
467 }
468
469 void SunSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
470         m = mm;
471         v = vv;
472         p = pp;
473         mv = v * m;
474         mvp = p * mv;
475         prog.Uniform(m_handle, m);
476         prog.Uniform(mv_handle, mv);
477         prog.Uniform(mvp_handle, mvp);
478 }
479
480 void SunSurface::SetLight(const glm::vec3 &color, float strength) noexcept {
481         prog.Uniform(light_color_handle, color);
482         prog.Uniform(light_strength_handle, strength);
483 }
484
485 void SunSurface::Draw() const noexcept {
486         vao.Bind();
487         vao.DrawTriangles(36);
488 }
489
490
491 constexpr int CreatureSkin::MAX_LIGHTS;
492
493 CreatureSkin::CreatureSkin()
494 : prog() {
495         prog.LoadShader(
496                 GL_VERTEX_SHADER,
497                 "#version 330 core\n"
498
499                 "layout(location = 0) in vec3 vtx_position;\n"
500                 "layout(location = 1) in vec3 vtx_normal;\n"
501                 "layout(location = 2) in vec3 vtx_tex_uv;\n"
502
503                 "uniform mat4 M;\n"
504                 "uniform mat4 MV;\n"
505                 "uniform mat4 MVP;\n"
506
507                 "out vec3 vtx_viewspace;\n"
508                 "out vec3 frag_tex_uv;\n"
509                 "out vec3 normal;\n"
510
511                 "void main() {\n"
512                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
513                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
514                         "normal = normalize((MV * vec4(vtx_normal, 0)).xyz);\n"
515                         "frag_tex_uv = vtx_tex_uv;\n"
516                 "}\n"
517         );
518         prog.LoadShader(
519                 GL_FRAGMENT_SHADER,
520                 "#version 330 core\n"
521
522                 "struct LightSource {\n"
523                         "vec3 position;\n"
524                         "vec3 color;\n"
525                         "float strength;\n"
526                 "};\n"
527
528                 "in vec3 vtx_viewspace;\n"
529                 "in vec3 frag_tex_uv;\n"
530                 "in vec3 normal;\n"
531
532                 "uniform sampler2DArray tex_sampler;\n"
533                 "uniform int num_lights;\n"
534                 "uniform LightSource light[8];\n"
535
536                 "out vec3 color;\n"
537
538                 "void main() {\n"
539                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
540                         "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
541                         "for (int i = 0; i < num_lights; ++i) {\n"
542                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
543                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
544                                 "vec3 light_dir = normalize(to_light);\n"
545                                 "float attenuation = light[i].strength / (distance * distance);\n"
546                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
547                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
548                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
549                                 "if (dot(normal, light_dir) >= 0.0) {\n"
550                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
551                                 "}\n"
552                                 "total_light = total_light + diffuse + specular;\n"
553                         "}\n"
554                         "color = total_light;\n"
555                 "}\n"
556         );
557         prog.Link();
558         if (!prog.Linked()) {
559                 prog.Log(std::cerr);
560                 throw std::runtime_error("link program");
561         }
562         m_handle = prog.UniformLocation("M");
563         mv_handle = prog.UniformLocation("MV");
564         mvp_handle = prog.UniformLocation("MVP");
565         sampler_handle = prog.UniformLocation("tex_sampler");
566         num_lights_handle = prog.UniformLocation("num_lights");
567         for (int i = 0; i < MAX_LIGHTS; ++i) {
568                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
569                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
570                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
571         }
572 }
573
574 CreatureSkin::~CreatureSkin() {
575 }
576
577 void CreatureSkin::Activate() noexcept {
578         prog.Use();
579         glEnable(GL_DEPTH_TEST);
580         glDepthFunc(GL_LESS);
581         glEnable(GL_CULL_FACE);
582         glDisable(GL_BLEND);
583 }
584
585 void CreatureSkin::SetM(const glm::mat4 &mm) noexcept {
586         m = mm;
587         mv = v * m;
588         mvp = p * mv;
589         prog.Uniform(m_handle, m);
590         prog.Uniform(mv_handle, mv);
591         prog.Uniform(mvp_handle, mvp);
592 }
593
594 void CreatureSkin::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
595         v = vv;
596         p = pp;
597         mv = v * m;
598         mvp = p * mv;
599         prog.Uniform(mv_handle, mv);
600         prog.Uniform(mvp_handle, mvp);
601 }
602
603 void CreatureSkin::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
604         m = mm;
605         v = vv;
606         p = pp;
607         mv = v * m;
608         mvp = p * mv;
609         prog.Uniform(m_handle, m);
610         prog.Uniform(mv_handle, mv);
611         prog.Uniform(mvp_handle, mvp);
612 }
613
614 void CreatureSkin::SetTexture(ArrayTexture &tex) noexcept {
615         glActiveTexture(GL_TEXTURE0);
616         tex.Bind();
617         prog.Uniform(sampler_handle, GLint(0));
618 }
619
620 void CreatureSkin::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
621         prog.Uniform(light_handle[3 * n + 0], pos);
622         prog.Uniform(light_handle[3 * n + 1], color);
623         prog.Uniform(light_handle[3 * n + 2], strength);
624 }
625
626 void CreatureSkin::SetNumLights(int n) noexcept {
627         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
628 }
629
630
631 PlainColor::PlainColor()
632 : prog() {
633         prog.LoadShader(
634                 GL_VERTEX_SHADER,
635                 "#version 330 core\n"
636
637                 "layout(location = 0) in vec3 vtx_position;\n"
638
639                 "uniform mat4 M;\n"
640                 "uniform mat4 MV;\n"
641                 "uniform mat4 MVP;\n"
642
643                 "void main() {\n"
644                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
645                 "}\n"
646         );
647         prog.LoadShader(
648                 GL_FRAGMENT_SHADER,
649                 "#version 330 core\n"
650
651                 "uniform vec4 fg_color;\n"
652
653                 "out vec4 color;\n"
654
655                 "void main() {\n"
656                         "color = fg_color;\n"
657                 "}\n"
658         );
659         prog.Link();
660         if (!prog.Linked()) {
661                 prog.Log(std::cerr);
662                 throw std::runtime_error("link program");
663         }
664         m_handle = prog.UniformLocation("M");
665         mv_handle = prog.UniformLocation("MV");
666         mvp_handle = prog.UniformLocation("MVP");
667         fg_color_handle = prog.UniformLocation("fg_color");
668
669         vao.Bind();
670         vao.BindAttributes();
671         vao.EnableAttribute(0);
672         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
673         vao.ReserveAttributes(4, GL_STATIC_DRAW);
674         {
675                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
676                 attrib[0].position = glm::vec3(-0.5f, -0.5f, 0.0f);
677                 attrib[1].position = glm::vec3(-0.5f,  0.5f, 0.0f);
678                 attrib[2].position = glm::vec3( 0.5f, -0.5f, 0.0f);
679                 attrib[3].position = glm::vec3( 0.5f,  0.5f, 0.0f);
680         }
681         vao.BindElements();
682         vao.ReserveElements(7, GL_STATIC_DRAW);
683         {
684                 auto element = vao.MapElements(GL_WRITE_ONLY);
685                 element[ 0] = 0;
686                 element[ 1] = 3;
687                 element[ 2] = 2;
688                 element[ 3] = 0;
689                 element[ 4] = 1;
690                 element[ 5] = 3;
691                 element[ 6] = 2;
692         }
693         vao.Unbind();
694 }
695
696 PlainColor::~PlainColor() {
697 }
698
699 void PlainColor::Activate() noexcept {
700         prog.Use();
701         glEnable(GL_DEPTH_TEST);
702         glDepthFunc(GL_LESS);
703         glEnable(GL_CULL_FACE);
704         glEnable(GL_BLEND);
705         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
706 }
707
708 void PlainColor::SetM(const glm::mat4 &mm) noexcept {
709         m = mm;
710         mv = v * m;
711         mvp = p * mv;
712         prog.Uniform(m_handle, m);
713         prog.Uniform(mv_handle, mv);
714         prog.Uniform(mvp_handle, mvp);
715 }
716
717 void PlainColor::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
718         v = vv;
719         p = pp;
720         mv = v * m;
721         mvp = p * mv;
722         prog.Uniform(mv_handle, mv);
723         prog.Uniform(mvp_handle, mvp);
724 }
725
726 void PlainColor::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
727         m = mm;
728         v = vv;
729         p = pp;
730         mv = v * m;
731         mvp = p * mv;
732         prog.Uniform(m_handle, m);
733         prog.Uniform(mv_handle, mv);
734         prog.Uniform(mvp_handle, mvp);
735 }
736
737 void PlainColor::SetColor(const glm::vec4 &color) noexcept {
738         prog.Uniform(fg_color_handle, color);
739 }
740
741 void PlainColor::DrawRect() const noexcept {
742         vao.Bind();
743         vao.DrawTriangles(6);
744 }
745
746 void PlainColor::OutlineRect() const noexcept {
747         vao.Bind();
748         vao.DrawLineLoop(4, 3);
749 }
750
751
752 AlphaSprite::AlphaSprite()
753 : prog() {
754         prog.LoadShader(
755                 GL_VERTEX_SHADER,
756                 "#version 330 core\n"
757
758                 "layout(location = 0) in vec3 vtx_position;\n"
759                 "layout(location = 1) in vec2 vtx_texture;\n"
760
761                 "uniform mat4 M;\n"
762                 "uniform mat4 MV;\n"
763                 "uniform mat4 MVP;\n"
764
765                 "out vec2 frag_tex_uv;\n"
766
767                 "void main() {\n"
768                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
769                         "frag_tex_uv = vtx_texture;\n"
770                 "}\n"
771         );
772         prog.LoadShader(
773                 GL_FRAGMENT_SHADER,
774                 "#version 330 core\n"
775
776                 "in vec2 frag_tex_uv;\n"
777
778                 "uniform sampler2D tex_sampler;\n"
779                 "uniform vec4 fg_color;\n"
780                 "uniform vec4 bg_color;\n"
781
782                 "out vec4 color;\n"
783
784                 "void main() {\n"
785                         "vec4 tex_color = texture(tex_sampler, frag_tex_uv);\n"
786                         "vec4 factor = mix(bg_color, fg_color, tex_color.a);\n"
787                         "color = vec4((tex_color * factor).rgb, factor.a);\n"
788                 "}\n"
789         );
790         prog.Link();
791         if (!prog.Linked()) {
792                 prog.Log(std::cerr);
793                 throw std::runtime_error("link program");
794         }
795         m_handle = prog.UniformLocation("M");
796         mv_handle = prog.UniformLocation("MV");
797         mvp_handle = prog.UniformLocation("MVP");
798         sampler_handle = prog.UniformLocation("tex_sampler");
799         fg_color_handle = prog.UniformLocation("fg_color");
800         bg_color_handle = prog.UniformLocation("bg_color");
801
802         vao.Bind();
803         vao.BindAttributes();
804         vao.EnableAttribute(0);
805         vao.EnableAttribute(1);
806         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
807         vao.AttributePointer<glm::vec2>(1, false, offsetof(Attributes, texture));
808         vao.ReserveAttributes(4, GL_STATIC_DRAW);
809         {
810                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
811                 attrib[0].position = glm::vec3(-0.5f, -0.5f, 0.0f);
812                 attrib[0].texture = glm::vec2(0.0f, 0.0f);
813                 attrib[1].position = glm::vec3(-0.5f,  0.5f, 0.0f);
814                 attrib[1].texture = glm::vec2(0.0f, 1.0f);
815                 attrib[2].position = glm::vec3( 0.5f, -0.5f, 0.0f);
816                 attrib[2].texture = glm::vec2(1.0f, 0.0f);
817                 attrib[3].position = glm::vec3( 0.5f,  0.5f, 0.0f);
818                 attrib[3].texture = glm::vec2(1.0f, 1.0f);
819         }
820         vao.BindElements();
821         vao.ReserveElements(7, GL_STATIC_DRAW);
822         {
823                 auto element = vao.MapElements(GL_WRITE_ONLY);
824                 element[ 0] = 0;
825                 element[ 1] = 1;
826                 element[ 2] = 2;
827                 element[ 3] = 3;
828         }
829         vao.Unbind();
830 }
831
832 AlphaSprite::~AlphaSprite() {
833 }
834
835 void AlphaSprite::Activate() noexcept {
836         prog.Use();
837         glEnable(GL_DEPTH_TEST);
838         glDepthFunc(GL_LESS);
839         glEnable(GL_CULL_FACE);
840         glEnable(GL_BLEND);
841         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
842 }
843
844 void AlphaSprite::SetM(const glm::mat4 &mm) noexcept {
845         m = mm;
846         mv = v * m;
847         mvp = p * mv;
848         prog.Uniform(m_handle, m);
849         prog.Uniform(mv_handle, mv);
850         prog.Uniform(mvp_handle, mvp);
851 }
852
853 void AlphaSprite::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
854         v = vv;
855         p = pp;
856         mv = v * m;
857         mvp = p * mv;
858         prog.Uniform(mv_handle, mv);
859         prog.Uniform(mvp_handle, mvp);
860 }
861
862 void AlphaSprite::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
863         m = mm;
864         v = vv;
865         p = pp;
866         mv = v * m;
867         mvp = p * mv;
868         prog.Uniform(m_handle, m);
869         prog.Uniform(mv_handle, mv);
870         prog.Uniform(mvp_handle, mvp);
871 }
872
873 void AlphaSprite::SetTexture(Texture &tex) noexcept {
874         glActiveTexture(GL_TEXTURE0);
875         tex.Bind();
876         prog.Uniform(sampler_handle, GLint(0));
877 }
878
879 void AlphaSprite::SetFgColor(const glm::vec4 &color) noexcept {
880         prog.Uniform(fg_color_handle, color);
881 }
882
883 void AlphaSprite::SetBgColor(const glm::vec4 &color) noexcept {
884         prog.Uniform(bg_color_handle, color);
885 }
886
887 void AlphaSprite::DrawRect() const noexcept {
888         vao.Bind();
889         vao.DrawTriangleStrip(4);
890 }
891
892 }
893 }