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