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