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