]> git.localhorst.tv Git - blobs.git/blob - src/graphics/shader.cpp
6a08bb5f99be5402edda61b239be9e145b58cb40
[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.1, 0.1, 0.1);\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::SetV(const glm::mat4 &vv) noexcept {
286         v = vv;
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::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
294         v = vv;
295         p = pp;
296         mv = v * m;
297         mvp = p * mv;
298         prog.Uniform(mv_handle, mv);
299         prog.Uniform(mvp_handle, mvp);
300 }
301
302 void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
303         m = mm;
304         v = vv;
305         p = pp;
306         mv = v * m;
307         mvp = p * mv;
308         prog.Uniform(m_handle, m);
309         prog.Uniform(mv_handle, mv);
310         prog.Uniform(mvp_handle, mvp);
311 }
312
313 void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
314         prog.Uniform(normal_handle, n);
315 }
316
317 void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
318         glActiveTexture(GL_TEXTURE0);
319         tex.Bind();
320         prog.Uniform(sampler_handle, GLint(0));
321 }
322
323 void PlanetSurface::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
324         prog.Uniform(light_handle[3 * n + 0], pos);
325         prog.Uniform(light_handle[3 * n + 1], color);
326         prog.Uniform(light_handle[3 * n + 2], strength);
327 }
328
329 void PlanetSurface::SetNumLights(int n) noexcept {
330         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
331 }
332
333
334 SunSurface::SunSurface()
335 : prog() {
336         prog.LoadShader(
337                 GL_VERTEX_SHADER,
338                 "#version 330 core\n"
339
340                 "layout(location = 0) in vec3 vtx_position;\n"
341
342                 "uniform mat4 M;\n"
343                 "uniform mat4 MV;\n"
344                 "uniform mat4 MVP;\n"
345
346                 "out vec3 vtx_viewspace;\n"
347
348                 "void main() {\n"
349                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
350                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
351                 "}\n"
352         );
353         prog.LoadShader(
354                 GL_FRAGMENT_SHADER,
355                 "#version 330 core\n"
356
357                 "in vec3 vtx_viewspace;\n"
358
359                 "uniform vec3 light_color;\n"
360                 "uniform float light_strength;\n"
361
362                 "out vec3 color;\n"
363
364                 "void main() {\n"
365                         "vec3 to_light = -vtx_viewspace;\n"
366                         "float distance = length(to_light);\n"
367                         //"vec3 light_dir = normalize(to_light);\n"
368                         "float attenuation = light_strength / (distance * distance);\n"
369                         "color = attenuation * light_color;\n"
370                 "}\n"
371         );
372         prog.Link();
373         if (!prog.Linked()) {
374                 prog.Log(std::cerr);
375                 throw std::runtime_error("link program");
376         }
377         m_handle = prog.UniformLocation("M");
378         mv_handle = prog.UniformLocation("MV");
379         mvp_handle = prog.UniformLocation("MVP");
380         light_color_handle = prog.UniformLocation("light_color");
381         light_strength_handle = prog.UniformLocation("light_strength");
382
383         vao.Bind();
384         vao.BindAttributes();
385         vao.EnableAttribute(0);
386         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
387         vao.ReserveAttributes(8, GL_STATIC_DRAW);
388         {
389                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
390                 attrib[0].position = glm::vec3(-1.0f, -1.0f, -1.0f);
391                 attrib[1].position = glm::vec3(-1.0f, -1.0f,  1.0f);
392                 attrib[2].position = glm::vec3(-1.0f,  1.0f, -1.0f);
393                 attrib[3].position = glm::vec3(-1.0f,  1.0f,  1.0f);
394                 attrib[4].position = glm::vec3( 1.0f, -1.0f, -1.0f);
395                 attrib[5].position = glm::vec3( 1.0f, -1.0f,  1.0f);
396                 attrib[6].position = glm::vec3( 1.0f,  1.0f, -1.0f);
397                 attrib[7].position = glm::vec3( 1.0f,  1.0f,  1.0f);
398         }
399         vao.BindElements();
400         vao.ReserveElements(36, GL_STATIC_DRAW);
401         {
402                 auto element = vao.MapElements(GL_WRITE_ONLY);
403                 // -X
404                 element[ 0] = 0;
405                 element[ 1] = 1;
406                 element[ 2] = 2;
407                 element[ 3] = 2;
408                 element[ 4] = 1;
409                 element[ 5] = 3;
410                 // -Y
411                 element[ 6] = 0;
412                 element[ 7] = 4;
413                 element[ 8] = 1;
414                 element[ 9] = 1;
415                 element[10] = 4;
416                 element[11] = 5;
417                 // -Z
418                 element[12] = 0;
419                 element[13] = 2;
420                 element[14] = 4;
421                 element[15] = 4;
422                 element[16] = 2;
423                 element[17] = 6;
424                 // +Z
425                 element[18] = 1;
426                 element[19] = 5;
427                 element[20] = 3;
428                 element[21] = 3;
429                 element[22] = 5;
430                 element[23] = 7;
431                 // +Y
432                 element[24] = 3;
433                 element[25] = 7;
434                 element[26] = 2;
435                 element[27] = 2;
436                 element[28] = 7;
437                 element[29] = 6;
438                 // +X
439                 element[30] = 5;
440                 element[31] = 4;
441                 element[32] = 7;
442                 element[33] = 7;
443                 element[34] = 4;
444                 element[35] = 6;
445         }
446         vao.Unbind();
447 }
448
449 SunSurface::~SunSurface() {
450 }
451
452 void SunSurface::Activate() noexcept {
453         prog.Use();
454         glEnable(GL_DEPTH_TEST);
455         glDepthFunc(GL_LESS);
456         glEnable(GL_CULL_FACE);
457         glDisable(GL_BLEND);
458 }
459
460 void SunSurface::SetM(const glm::mat4 &mm) noexcept {
461         m = mm;
462         mv = v * m;
463         mvp = p * mv;
464         prog.Uniform(m_handle, m);
465         prog.Uniform(mv_handle, mv);
466         prog.Uniform(mvp_handle, mvp);
467 }
468
469 void SunSurface::SetV(const glm::mat4 &vv) noexcept {
470         v = vv;
471         mv = v * m;
472         mvp = p * mv;
473         prog.Uniform(mv_handle, mv);
474         prog.Uniform(mvp_handle, mvp);
475 }
476
477 void SunSurface::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
478         v = vv;
479         p = pp;
480         mv = v * m;
481         mvp = p * mv;
482         prog.Uniform(mv_handle, mv);
483         prog.Uniform(mvp_handle, mvp);
484 }
485
486 void SunSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
487         m = mm;
488         v = vv;
489         p = pp;
490         mv = v * m;
491         mvp = p * mv;
492         prog.Uniform(m_handle, m);
493         prog.Uniform(mv_handle, mv);
494         prog.Uniform(mvp_handle, mvp);
495 }
496
497 void SunSurface::SetLight(const glm::vec3 &color, float strength) noexcept {
498         prog.Uniform(light_color_handle, color);
499         prog.Uniform(light_strength_handle, strength);
500 }
501
502 void SunSurface::Draw() const noexcept {
503         vao.Bind();
504         vao.DrawTriangles(36);
505 }
506
507
508 constexpr int CreatureSkin::MAX_LIGHTS;
509
510 CreatureSkin::CreatureSkin()
511 : prog() {
512         prog.LoadShader(
513                 GL_VERTEX_SHADER,
514                 "#version 330 core\n"
515
516                 "layout(location = 0) in vec3 vtx_position;\n"
517                 "layout(location = 1) in vec3 vtx_normal;\n"
518                 "layout(location = 2) in vec3 vtx_tex_uv;\n"
519
520                 "uniform mat4 M;\n"
521                 "uniform mat4 MV;\n"
522                 "uniform mat4 MVP;\n"
523
524                 "out vec3 vtx_viewspace;\n"
525                 "out vec3 frag_tex_uv;\n"
526                 "out vec3 normal;\n"
527
528                 "void main() {\n"
529                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
530                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
531                         "normal = normalize((MV * vec4(vtx_normal, 0)).xyz);\n"
532                         "frag_tex_uv = vtx_tex_uv;\n"
533                 "}\n"
534         );
535         prog.LoadShader(
536                 GL_FRAGMENT_SHADER,
537                 "#version 330 core\n"
538
539                 "struct LightSource {\n"
540                         "vec3 position;\n"
541                         "vec3 color;\n"
542                         "float strength;\n"
543                 "};\n"
544
545                 "in vec3 vtx_viewspace;\n"
546                 "in vec3 frag_tex_uv;\n"
547                 "in vec3 normal;\n"
548
549                 "uniform sampler2DArray tex_sampler;\n"
550                 "uniform int num_lights;\n"
551                 "uniform LightSource light[8];\n"
552
553                 "out vec3 color;\n"
554
555                 "void main() {\n"
556                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
557                         "vec3 total_light = tex_color * vec3(0.1, 0.1, 0.1);\n"
558                         "for (int i = 0; i < num_lights; ++i) {\n"
559                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
560                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
561                                 "vec3 light_dir = normalize(to_light);\n"
562                                 "float attenuation = light[i].strength / (distance * distance);\n"
563                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
564                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
565                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
566                                 "if (dot(normal, light_dir) >= 0.0) {\n"
567                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
568                                 "}\n"
569                                 "total_light = total_light + diffuse + specular;\n"
570                         "}\n"
571                         "color = total_light;\n"
572                 "}\n"
573         );
574         prog.Link();
575         if (!prog.Linked()) {
576                 prog.Log(std::cerr);
577                 throw std::runtime_error("link program");
578         }
579         m_handle = prog.UniformLocation("M");
580         mv_handle = prog.UniformLocation("MV");
581         mvp_handle = prog.UniformLocation("MVP");
582         sampler_handle = prog.UniformLocation("tex_sampler");
583         num_lights_handle = prog.UniformLocation("num_lights");
584         for (int i = 0; i < MAX_LIGHTS; ++i) {
585                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
586                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
587                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
588         }
589 }
590
591 CreatureSkin::~CreatureSkin() {
592 }
593
594 void CreatureSkin::Activate() noexcept {
595         prog.Use();
596         glEnable(GL_DEPTH_TEST);
597         glDepthFunc(GL_LESS);
598         glEnable(GL_CULL_FACE);
599         glDisable(GL_BLEND);
600 }
601
602 void CreatureSkin::SetM(const glm::mat4 &mm) noexcept {
603         m = mm;
604         mv = v * m;
605         mvp = p * mv;
606         prog.Uniform(m_handle, m);
607         prog.Uniform(mv_handle, mv);
608         prog.Uniform(mvp_handle, mvp);
609 }
610
611 void CreatureSkin::SetV(const glm::mat4 &vv) noexcept {
612         v = vv;
613         mv = v * m;
614         mvp = p * mv;
615         prog.Uniform(mv_handle, mv);
616         prog.Uniform(mvp_handle, mvp);
617 }
618
619 void CreatureSkin::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
620         v = vv;
621         p = pp;
622         mv = v * m;
623         mvp = p * mv;
624         prog.Uniform(mv_handle, mv);
625         prog.Uniform(mvp_handle, mvp);
626 }
627
628 void CreatureSkin::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
629         m = mm;
630         v = vv;
631         p = pp;
632         mv = v * m;
633         mvp = p * mv;
634         prog.Uniform(m_handle, m);
635         prog.Uniform(mv_handle, mv);
636         prog.Uniform(mvp_handle, mvp);
637 }
638
639 void CreatureSkin::SetTexture(ArrayTexture &tex) noexcept {
640         glActiveTexture(GL_TEXTURE0);
641         tex.Bind();
642         prog.Uniform(sampler_handle, GLint(0));
643 }
644
645 void CreatureSkin::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
646         prog.Uniform(light_handle[3 * n + 0], pos);
647         prog.Uniform(light_handle[3 * n + 1], color);
648         prog.Uniform(light_handle[3 * n + 2], strength);
649 }
650
651 void CreatureSkin::SetNumLights(int n) noexcept {
652         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
653 }
654
655
656 AlphaSprite::AlphaSprite()
657 : prog() {
658         prog.LoadShader(
659                 GL_VERTEX_SHADER,
660                 "#version 330 core\n"
661
662                 "layout(location = 0) in vec3 vtx_position;\n"
663                 "layout(location = 1) in vec2 vtx_texture;\n"
664
665                 "uniform mat4 M;\n"
666                 "uniform mat4 MV;\n"
667                 "uniform mat4 MVP;\n"
668
669                 "out vec2 frag_tex_uv;\n"
670
671                 "void main() {\n"
672                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
673                         "frag_tex_uv = vtx_texture;\n"
674                 "}\n"
675         );
676         prog.LoadShader(
677                 GL_FRAGMENT_SHADER,
678                 "#version 330 core\n"
679
680                 "in vec2 frag_tex_uv;\n"
681
682                 "uniform sampler2D tex_sampler;\n"
683                 "uniform vec4 fg_color;\n"
684                 "uniform vec4 bg_color;\n"
685
686                 "out vec4 color;\n"
687
688                 "void main() {\n"
689                         "vec4 tex_color = texture(tex_sampler, frag_tex_uv);\n"
690                         "vec4 factor = mix(bg_color, fg_color, tex_color.a);\n"
691                         "color = vec4((tex_color * factor).rgb, factor.a);\n"
692                 "}\n"
693         );
694         prog.Link();
695         if (!prog.Linked()) {
696                 prog.Log(std::cerr);
697                 throw std::runtime_error("link program");
698         }
699         m_handle = prog.UniformLocation("M");
700         mv_handle = prog.UniformLocation("MV");
701         mvp_handle = prog.UniformLocation("MVP");
702         sampler_handle = prog.UniformLocation("tex_sampler");
703         fg_color_handle = prog.UniformLocation("fg_color");
704         bg_color_handle = prog.UniformLocation("bg_color");
705
706         vao.Bind();
707         vao.BindAttributes();
708         vao.EnableAttribute(0);
709         vao.EnableAttribute(1);
710         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
711         vao.AttributePointer<glm::vec2>(1, false, offsetof(Attributes, texture));
712         vao.ReserveAttributes(4, GL_STATIC_DRAW);
713         {
714                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
715                 attrib[0].position = glm::vec3(-0.5f, -0.5f, 0.0f);
716                 attrib[0].texture = glm::vec2(0.0f, 0.0f);
717                 attrib[1].position = glm::vec3(-0.5f,  0.5f, 0.0f);
718                 attrib[1].texture = glm::vec2(0.0f, 1.0f);
719                 attrib[2].position = glm::vec3( 0.5f, -0.5f, 0.0f);
720                 attrib[2].texture = glm::vec2(1.0f, 0.0f);
721                 attrib[3].position = glm::vec3( 0.5f,  0.5f, 0.0f);
722                 attrib[3].texture = glm::vec2(1.0f, 1.0f);
723         }
724         vao.BindElements();
725         vao.ReserveElements(7, GL_STATIC_DRAW);
726         {
727                 auto element = vao.MapElements(GL_WRITE_ONLY);
728                 element[ 0] = 0;
729                 element[ 1] = 1;
730                 element[ 2] = 2;
731                 element[ 3] = 3;
732         }
733         vao.Unbind();
734 }
735
736 AlphaSprite::~AlphaSprite() {
737 }
738
739 void AlphaSprite::Activate() noexcept {
740         prog.Use();
741         glEnable(GL_DEPTH_TEST);
742         glDepthFunc(GL_LESS);
743         glEnable(GL_CULL_FACE);
744         glEnable(GL_BLEND);
745         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
746 }
747
748 void AlphaSprite::SetM(const glm::mat4 &mm) noexcept {
749         m = mm;
750         mv = v * m;
751         mvp = p * mv;
752         prog.Uniform(m_handle, m);
753         prog.Uniform(mv_handle, mv);
754         prog.Uniform(mvp_handle, mvp);
755 }
756
757 void AlphaSprite::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
758         v = vv;
759         p = pp;
760         mv = v * m;
761         mvp = p * mv;
762         prog.Uniform(mv_handle, mv);
763         prog.Uniform(mvp_handle, mvp);
764 }
765
766 void AlphaSprite::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
767         m = mm;
768         v = vv;
769         p = pp;
770         mv = v * m;
771         mvp = p * mv;
772         prog.Uniform(m_handle, m);
773         prog.Uniform(mv_handle, mv);
774         prog.Uniform(mvp_handle, mvp);
775 }
776
777 void AlphaSprite::SetTexture(Texture &tex) noexcept {
778         glActiveTexture(GL_TEXTURE0);
779         tex.Bind();
780         prog.Uniform(sampler_handle, GLint(0));
781 }
782
783 void AlphaSprite::SetFgColor(const glm::vec4 &color) noexcept {
784         prog.Uniform(fg_color_handle, color);
785 }
786
787 void AlphaSprite::SetBgColor(const glm::vec4 &color) noexcept {
788         prog.Uniform(bg_color_handle, color);
789 }
790
791 void AlphaSprite::DrawRect() const noexcept {
792         vao.Bind();
793         vao.DrawTriangleStrip(4);
794 }
795
796
797 Canvas::Canvas()
798 : prog()
799 , vao() {
800         prog.LoadShader(
801                 GL_VERTEX_SHADER,
802                 "#version 330 core\n"
803
804                 "layout(location = 0) in vec2 vtx_position;\n"
805
806                 "uniform mat4 P;\n"
807                 "uniform float z;\n"
808
809                 "void main() {\n"
810                         // disamond rule adjust
811                         //"vec3 position = vtx_position + vec3(0.5, 0.5, 0.0);\n"
812                         "gl_Position = P * vec4(vtx_position, z, 1);\n"
813                 "}\n"
814         );
815         prog.LoadShader(
816                 GL_FRAGMENT_SHADER,
817                 "#version 330 core\n"
818
819                 "uniform vec4 c;\n"
820
821                 "out vec4 color;\n"
822
823                 "void main() {\n"
824                         "color = c;\n"
825                 "}\n"
826         );
827         prog.Link();
828         if (!prog.Linked()) {
829                 prog.Log(std::cerr);
830                 throw std::runtime_error("link program");
831         }
832         p_handle = prog.UniformLocation("P");
833         z_handle = prog.UniformLocation("z");
834         c_handle = prog.UniformLocation("c");
835
836         vao.Bind();
837         vao.BindAttributes();
838         vao.EnableAttribute(0);
839         vao.AttributePointer<glm::vec2>(0, false, offsetof(Attributes, position));
840         vao.ReserveAttributes(255, GL_DYNAMIC_DRAW);
841         vao.BindElements();
842         vao.ReserveElements(255, GL_DYNAMIC_DRAW);
843         vao.Unbind();
844 }
845
846 Canvas::~Canvas() {
847 }
848
849 void Canvas::Resize(float w, float h) noexcept {
850         prog.Uniform(p_handle, glm::ortho(0.0f, w, h, 0.0f, 1.0e4f, -1.0e4f));
851 }
852
853 void Canvas::ZIndex(float z) noexcept {
854         prog.Uniform(z_handle, -z);
855 }
856
857 void Canvas::SetColor(const glm::vec4 &color) noexcept {
858         prog.Uniform(c_handle, color);
859 }
860
861 void Canvas::Activate() noexcept {
862         prog.Use();
863         glEnable(GL_DEPTH_TEST);
864         glDepthFunc(GL_LESS);
865         glEnable(GL_CULL_FACE);
866         glEnable(GL_BLEND);
867         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
868 }
869
870 void Canvas::DrawLine(const glm::vec2 &p1, const glm::vec2 &p2, float width) {
871         glm::vec2 d = glm::normalize(p2 - p1) * (width * 0.5f);
872         glm::vec2 n = glm::vec2(d.y, -d.x);
873         vao.Bind();
874         vao.BindAttributes();
875         {
876                 auto attr = vao.MapAttributes(GL_WRITE_ONLY);
877                 attr[0].position = p1 - d + n;
878                 attr[1].position = p1 - d - n;
879                 attr[2].position = p2 + d + n;
880                 attr[3].position = p2 + d - n;
881         }
882         vao.BindElements();
883         {
884                 auto elem = vao.MapElements(GL_WRITE_ONLY);
885                 elem[0] = 0;
886                 elem[1] = 1;
887                 elem[2] = 2;
888                 elem[3] = 3;
889         }
890         vao.DrawTriangleStrip(4);
891         vao.Unbind();
892 }
893
894 void Canvas::DrawRect(const glm::vec2 &p1, const glm::vec2 &p2, float width) {
895         glm::vec2 min(std::min(p1.x, p2.x), std::min(p1.y, p2.y));
896         glm::vec2 max(std::max(p1.x, p2.x), std::max(p1.y, p2.y));
897         glm::vec2 dg1(min.x, max.y);
898         glm::vec2 dg2(max.x, min.y);
899         glm::vec2 d(width * 0.5f, width * 0.5f);
900         glm::vec2 n(d.y, -d.x);
901         vao.Bind();
902         vao.BindAttributes();
903         {
904                 auto attr = vao.MapAttributes(GL_WRITE_ONLY);
905                 attr[0].position = min + d;
906                 attr[1].position = min - d;
907                 attr[2].position = dg1 + n;
908                 attr[3].position = dg1 - n;
909                 attr[4].position = max - d;
910                 attr[5].position = max + d;
911                 attr[6].position = dg2 - n;
912                 attr[7].position = dg2 + n;
913         }
914         vao.BindElements();
915         {
916                 auto elem = vao.MapElements(GL_WRITE_ONLY);
917                 elem[0] = 0;
918                 elem[1] = 1;
919                 elem[2] = 2;
920                 elem[3] = 3;
921                 elem[4] = 4;
922                 elem[5] = 5;
923                 elem[6] = 6;
924                 elem[7] = 7;
925                 elem[8] = 0;
926                 elem[9] = 1;
927         }
928         vao.DrawTriangleStrip(10);
929         vao.Unbind();
930 }
931
932 void Canvas::FillRect(const glm::vec2 &p1, const glm::vec2 &p2) {
933         glm::vec2 min(std::min(p1.x, p2.x), std::min(p1.y, p2.y));
934         glm::vec2 max(std::max(p1.x, p2.x), std::max(p1.y, p2.y));
935         glm::vec2 dg1(min.x, max.y);
936         glm::vec2 dg2(max.x, min.y);
937         vao.Bind();
938         vao.BindAttributes();
939         {
940                 auto attr = vao.MapAttributes(GL_WRITE_ONLY);
941                 attr[0].position = min;
942                 attr[1].position = dg1;
943                 attr[2].position = dg2;
944                 attr[3].position = max;
945         }
946         vao.BindElements();
947         {
948                 auto elem = vao.MapElements(GL_WRITE_ONLY);
949                 elem[0] = 0;
950                 elem[1] = 1;
951                 elem[2] = 2;
952                 elem[3] = 3;
953         }
954         vao.DrawTriangleStrip(4);
955         vao.Unbind();
956 }
957
958 }
959 }