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