]> git.localhorst.tv Git - blobs.git/blob - src/graphics/shader.cpp
0890dfdc8d308bf84f1de0c47db381bc35fca1e5
[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 vec3 base_color;\n"
550                 "uniform vec4 highlight_color;\n"
551                 "uniform sampler2DArray tex_sampler;\n"
552                 "uniform int num_lights;\n"
553                 "uniform LightSource light[8];\n"
554
555                 "out vec3 color;\n"
556
557                 "void main() {\n"
558                         "vec4 tex_color = texture(tex_sampler, frag_tex_uv);\n"
559                         "vec3 mat_color = mix(base_color, highlight_color.rgb, tex_color.r * tex_color.a * highlight_color.a);\n"
560                         "vec3 total_light = mat_color * vec3(0.1, 0.1, 0.1);\n"
561                         "for (int i = 0; i < num_lights; ++i) {\n"
562                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
563                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
564                                 "vec3 light_dir = normalize(to_light);\n"
565                                 "float attenuation = light[i].strength / (distance * distance);\n"
566                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * mat_color;\n"
567                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
568                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
569                                 "if (dot(normal, light_dir) >= 0.0) {\n"
570                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
571                                 "}\n"
572                                 "total_light = total_light + diffuse + specular;\n"
573                         "}\n"
574                         "color = total_light;\n"
575                 "}\n"
576         );
577         prog.Link();
578         if (!prog.Linked()) {
579                 prog.Log(std::cerr);
580                 throw std::runtime_error("link program");
581         }
582         m_handle = prog.UniformLocation("M");
583         mv_handle = prog.UniformLocation("MV");
584         mvp_handle = prog.UniformLocation("MVP");
585         base_color_handle = prog.UniformLocation("base_color");
586         highlight_color_handle = prog.UniformLocation("highlight_color");
587         sampler_handle = prog.UniformLocation("tex_sampler");
588         num_lights_handle = prog.UniformLocation("num_lights");
589         for (int i = 0; i < MAX_LIGHTS; ++i) {
590                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
591                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
592                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
593         }
594 }
595
596 CreatureSkin::~CreatureSkin() {
597 }
598
599 void CreatureSkin::Activate() noexcept {
600         prog.Use();
601         glEnable(GL_DEPTH_TEST);
602         glDepthFunc(GL_LESS);
603         glEnable(GL_CULL_FACE);
604         glDisable(GL_BLEND);
605 }
606
607 void CreatureSkin::SetM(const glm::mat4 &mm) noexcept {
608         m = mm;
609         mv = v * m;
610         mvp = p * mv;
611         prog.Uniform(m_handle, m);
612         prog.Uniform(mv_handle, mv);
613         prog.Uniform(mvp_handle, mvp);
614 }
615
616 void CreatureSkin::SetV(const glm::mat4 &vv) noexcept {
617         v = vv;
618         mv = v * m;
619         mvp = p * mv;
620         prog.Uniform(mv_handle, mv);
621         prog.Uniform(mvp_handle, mvp);
622 }
623
624 void CreatureSkin::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
625         v = vv;
626         p = pp;
627         mv = v * m;
628         mvp = p * mv;
629         prog.Uniform(mv_handle, mv);
630         prog.Uniform(mvp_handle, mvp);
631 }
632
633 void CreatureSkin::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
634         m = mm;
635         v = vv;
636         p = pp;
637         mv = v * m;
638         mvp = p * mv;
639         prog.Uniform(m_handle, m);
640         prog.Uniform(mv_handle, mv);
641         prog.Uniform(mvp_handle, mvp);
642 }
643
644 void CreatureSkin::SetBaseColor(const glm::vec3 &c) noexcept {
645         prog.Uniform(base_color_handle, c);
646 }
647
648 void CreatureSkin::SetHighlightColor(const glm::vec4 &c) noexcept {
649         prog.Uniform(highlight_color_handle, c);
650 }
651
652 void CreatureSkin::SetTexture(ArrayTexture &tex) noexcept {
653         glActiveTexture(GL_TEXTURE0);
654         tex.Bind();
655         prog.Uniform(sampler_handle, GLint(0));
656 }
657
658 void CreatureSkin::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
659         prog.Uniform(light_handle[3 * n + 0], pos);
660         prog.Uniform(light_handle[3 * n + 1], color);
661         prog.Uniform(light_handle[3 * n + 2], strength);
662 }
663
664 void CreatureSkin::SetNumLights(int n) noexcept {
665         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
666 }
667
668
669 AlphaSprite::AlphaSprite()
670 : prog() {
671         prog.LoadShader(
672                 GL_VERTEX_SHADER,
673                 "#version 330 core\n"
674
675                 "layout(location = 0) in vec3 vtx_position;\n"
676                 "layout(location = 1) in vec2 vtx_texture;\n"
677
678                 "uniform mat4 M;\n"
679                 "uniform mat4 MV;\n"
680                 "uniform mat4 MVP;\n"
681
682                 "out vec2 frag_tex_uv;\n"
683
684                 "void main() {\n"
685                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
686                         "frag_tex_uv = vtx_texture;\n"
687                 "}\n"
688         );
689         prog.LoadShader(
690                 GL_FRAGMENT_SHADER,
691                 "#version 330 core\n"
692
693                 "in vec2 frag_tex_uv;\n"
694
695                 "uniform sampler2D tex_sampler;\n"
696                 "uniform vec4 fg_color;\n"
697                 "uniform vec4 bg_color;\n"
698
699                 "out vec4 color;\n"
700
701                 "void main() {\n"
702                         "vec4 tex_color = texture(tex_sampler, frag_tex_uv);\n"
703                         "vec4 factor = mix(bg_color, fg_color, tex_color.a);\n"
704                         "color = vec4((tex_color * factor).rgb, factor.a);\n"
705                 "}\n"
706         );
707         prog.Link();
708         if (!prog.Linked()) {
709                 prog.Log(std::cerr);
710                 throw std::runtime_error("link program");
711         }
712         m_handle = prog.UniformLocation("M");
713         mv_handle = prog.UniformLocation("MV");
714         mvp_handle = prog.UniformLocation("MVP");
715         sampler_handle = prog.UniformLocation("tex_sampler");
716         fg_color_handle = prog.UniformLocation("fg_color");
717         bg_color_handle = prog.UniformLocation("bg_color");
718
719         vao.Bind();
720         vao.BindAttributes();
721         vao.EnableAttribute(0);
722         vao.EnableAttribute(1);
723         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
724         vao.AttributePointer<glm::vec2>(1, false, offsetof(Attributes, texture));
725         vao.ReserveAttributes(4, GL_STATIC_DRAW);
726         {
727                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
728                 attrib[0].position = glm::vec3(-0.5f, -0.5f, 0.0f);
729                 attrib[0].texture = glm::vec2(0.0f, 0.0f);
730                 attrib[1].position = glm::vec3(-0.5f,  0.5f, 0.0f);
731                 attrib[1].texture = glm::vec2(0.0f, 1.0f);
732                 attrib[2].position = glm::vec3( 0.5f, -0.5f, 0.0f);
733                 attrib[2].texture = glm::vec2(1.0f, 0.0f);
734                 attrib[3].position = glm::vec3( 0.5f,  0.5f, 0.0f);
735                 attrib[3].texture = glm::vec2(1.0f, 1.0f);
736         }
737         vao.BindElements();
738         vao.ReserveElements(7, GL_STATIC_DRAW);
739         {
740                 auto element = vao.MapElements(GL_WRITE_ONLY);
741                 element[ 0] = 0;
742                 element[ 1] = 1;
743                 element[ 2] = 2;
744                 element[ 3] = 3;
745         }
746         vao.Unbind();
747 }
748
749 AlphaSprite::~AlphaSprite() {
750 }
751
752 void AlphaSprite::Activate() noexcept {
753         prog.Use();
754         glEnable(GL_DEPTH_TEST);
755         glDepthFunc(GL_LESS);
756         glEnable(GL_CULL_FACE);
757         glEnable(GL_BLEND);
758         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
759 }
760
761 void AlphaSprite::SetM(const glm::mat4 &mm) noexcept {
762         m = mm;
763         mv = v * m;
764         mvp = p * mv;
765         prog.Uniform(m_handle, m);
766         prog.Uniform(mv_handle, mv);
767         prog.Uniform(mvp_handle, mvp);
768 }
769
770 void AlphaSprite::SetVP(const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
771         v = vv;
772         p = pp;
773         mv = v * m;
774         mvp = p * mv;
775         prog.Uniform(mv_handle, mv);
776         prog.Uniform(mvp_handle, mvp);
777 }
778
779 void AlphaSprite::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
780         m = mm;
781         v = vv;
782         p = pp;
783         mv = v * m;
784         mvp = p * mv;
785         prog.Uniform(m_handle, m);
786         prog.Uniform(mv_handle, mv);
787         prog.Uniform(mvp_handle, mvp);
788 }
789
790 void AlphaSprite::SetTexture(Texture &tex) noexcept {
791         glActiveTexture(GL_TEXTURE0);
792         tex.Bind();
793         prog.Uniform(sampler_handle, GLint(0));
794 }
795
796 void AlphaSprite::SetFgColor(const glm::vec4 &color) noexcept {
797         prog.Uniform(fg_color_handle, color);
798 }
799
800 void AlphaSprite::SetBgColor(const glm::vec4 &color) noexcept {
801         prog.Uniform(bg_color_handle, color);
802 }
803
804 void AlphaSprite::DrawRect() const noexcept {
805         vao.Bind();
806         vao.DrawTriangleStrip(4);
807 }
808
809
810 Canvas::Canvas()
811 : prog()
812 , vao() {
813         prog.LoadShader(
814                 GL_VERTEX_SHADER,
815                 "#version 330 core\n"
816
817                 "layout(location = 0) in vec2 vtx_position;\n"
818
819                 "uniform mat4 P;\n"
820                 "uniform float z;\n"
821
822                 "void main() {\n"
823                         // disamond rule adjust
824                         //"vec3 position = vtx_position + vec3(0.5, 0.5, 0.0);\n"
825                         "gl_Position = P * vec4(vtx_position, z, 1);\n"
826                 "}\n"
827         );
828         prog.LoadShader(
829                 GL_FRAGMENT_SHADER,
830                 "#version 330 core\n"
831
832                 "uniform vec4 c;\n"
833
834                 "out vec4 color;\n"
835
836                 "void main() {\n"
837                         "color = c;\n"
838                 "}\n"
839         );
840         prog.Link();
841         if (!prog.Linked()) {
842                 prog.Log(std::cerr);
843                 throw std::runtime_error("link program");
844         }
845         p_handle = prog.UniformLocation("P");
846         z_handle = prog.UniformLocation("z");
847         c_handle = prog.UniformLocation("c");
848
849         vao.Bind();
850         vao.BindAttributes();
851         vao.EnableAttribute(0);
852         vao.AttributePointer<glm::vec2>(0, false, offsetof(Attributes, position));
853         vao.ReserveAttributes(255, GL_DYNAMIC_DRAW);
854         vao.BindElements();
855         vao.ReserveElements(255, GL_DYNAMIC_DRAW);
856         vao.Unbind();
857 }
858
859 Canvas::~Canvas() {
860 }
861
862 void Canvas::Resize(float w, float h) noexcept {
863         prog.Uniform(p_handle, glm::ortho(0.0f, w, h, 0.0f, 1.0e4f, -1.0e4f));
864 }
865
866 void Canvas::ZIndex(float z) noexcept {
867         prog.Uniform(z_handle, -z);
868 }
869
870 void Canvas::SetColor(const glm::vec4 &color) noexcept {
871         prog.Uniform(c_handle, color);
872 }
873
874 void Canvas::Activate() noexcept {
875         prog.Use();
876         glEnable(GL_DEPTH_TEST);
877         glDepthFunc(GL_LESS);
878         glEnable(GL_CULL_FACE);
879         glEnable(GL_BLEND);
880         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
881 }
882
883 void Canvas::DrawLine(const glm::vec2 &p1, const glm::vec2 &p2, float width) {
884         glm::vec2 d = glm::normalize(p2 - p1) * (width * 0.5f);
885         glm::vec2 n = glm::vec2(d.y, -d.x);
886         vao.Bind();
887         vao.BindAttributes();
888         {
889                 auto attr = vao.MapAttributes(GL_WRITE_ONLY);
890                 attr[0].position = p1 - d + n;
891                 attr[1].position = p1 - d - n;
892                 attr[2].position = p2 + d + n;
893                 attr[3].position = p2 + d - n;
894         }
895         vao.BindElements();
896         {
897                 auto elem = vao.MapElements(GL_WRITE_ONLY);
898                 elem[0] = 0;
899                 elem[1] = 1;
900                 elem[2] = 2;
901                 elem[3] = 3;
902         }
903         vao.DrawTriangleStrip(4);
904         vao.Unbind();
905 }
906
907 void Canvas::DrawRect(const glm::vec2 &p1, const glm::vec2 &p2, float width) {
908         glm::vec2 min(std::min(p1.x, p2.x), std::min(p1.y, p2.y));
909         glm::vec2 max(std::max(p1.x, p2.x), std::max(p1.y, p2.y));
910         glm::vec2 dg1(min.x, max.y);
911         glm::vec2 dg2(max.x, min.y);
912         glm::vec2 d(width * 0.5f, width * 0.5f);
913         glm::vec2 n(d.y, -d.x);
914         vao.Bind();
915         vao.BindAttributes();
916         {
917                 auto attr = vao.MapAttributes(GL_WRITE_ONLY);
918                 attr[0].position = min + d;
919                 attr[1].position = min - d;
920                 attr[2].position = dg1 + n;
921                 attr[3].position = dg1 - n;
922                 attr[4].position = max - d;
923                 attr[5].position = max + d;
924                 attr[6].position = dg2 - n;
925                 attr[7].position = dg2 + n;
926         }
927         vao.BindElements();
928         {
929                 auto elem = vao.MapElements(GL_WRITE_ONLY);
930                 elem[0] = 0;
931                 elem[1] = 1;
932                 elem[2] = 2;
933                 elem[3] = 3;
934                 elem[4] = 4;
935                 elem[5] = 5;
936                 elem[6] = 6;
937                 elem[7] = 7;
938                 elem[8] = 0;
939                 elem[9] = 1;
940         }
941         vao.DrawTriangleStrip(10);
942         vao.Unbind();
943 }
944
945 void Canvas::FillRect(const glm::vec2 &p1, const glm::vec2 &p2) {
946         glm::vec2 min(std::min(p1.x, p2.x), std::min(p1.y, p2.y));
947         glm::vec2 max(std::max(p1.x, p2.x), std::max(p1.y, p2.y));
948         glm::vec2 dg1(min.x, max.y);
949         glm::vec2 dg2(max.x, min.y);
950         vao.Bind();
951         vao.BindAttributes();
952         {
953                 auto attr = vao.MapAttributes(GL_WRITE_ONLY);
954                 attr[0].position = min;
955                 attr[1].position = dg1;
956                 attr[2].position = dg2;
957                 attr[3].position = max;
958         }
959         vao.BindElements();
960         {
961                 auto elem = vao.MapElements(GL_WRITE_ONLY);
962                 elem[0] = 0;
963                 elem[1] = 1;
964                 elem[2] = 2;
965                 elem[3] = 3;
966         }
967         vao.DrawTriangleStrip(4);
968         vao.Unbind();
969 }
970
971 }
972 }