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