1 #include "CreatureSkin.hpp"
2 #include "PlanetSurface.hpp"
5 #include "SunSurface.hpp"
7 #include "ArrayTexture.hpp"
8 #include "../app/init.hpp"
16 #include <glm/gtc/type_ptr.hpp>
21 void gl_error(std::string msg) {
22 const GLubyte *errBegin = gluErrorString(glGetError());
23 if (errBegin && *errBegin != '\0') {
24 const GLubyte *errEnd = errBegin;
25 while (*errEnd != '\0') {
29 msg.append(errBegin, errEnd);
31 throw std::runtime_error(msg);
39 Shader::Shader(GLenum type)
40 : handle(glCreateShader(type)) {
42 gl_error("glCreateShader");
48 glDeleteShader(handle);
52 Shader::Shader(Shader &&other) noexcept
53 : handle(other.handle) {
57 Shader &Shader::operator =(Shader &&other) noexcept {
58 std::swap(handle, other.handle);
63 void Shader::Source(const GLchar *src) noexcept {
64 const GLchar* src_arr[] = { src };
65 glShaderSource(handle, 1, src_arr, nullptr);
68 void Shader::Compile() noexcept {
69 glCompileShader(handle);
72 bool Shader::Compiled() const noexcept {
73 GLint compiled = GL_FALSE;
74 glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
75 return compiled == GL_TRUE;
78 void Shader::Log(std::ostream &out) const {
79 int log_len = 0, max_len = 0;
80 glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len);
81 std::unique_ptr<char[]> log(new char[max_len]);
82 glGetShaderInfoLog(handle, max_len, &log_len, log.get());
83 out.write(log.get(), log_len);
87 void Shader::AttachToProgram(GLuint id) const noexcept {
88 glAttachShader(id, handle);
93 : handle(glCreateProgram()) {
95 gl_error("glCreateProgram");
101 glDeleteProgram(handle);
106 const Shader &Program::LoadShader(GLenum type, const GLchar *src) {
107 shaders.emplace_back(type);
108 Shader &shader = shaders.back();
111 if (!shader.Compiled()) {
112 shader.Log(std::cerr);
113 throw std::runtime_error("compile shader");
119 void Program::Attach(Shader &shader) noexcept {
120 shader.AttachToProgram(handle);
123 void Program::Link() noexcept {
124 glLinkProgram(handle);
127 bool Program::Linked() const noexcept {
128 GLint linked = GL_FALSE;
129 glGetProgramiv(handle, GL_LINK_STATUS, &linked);
130 return linked == GL_TRUE;
133 void Program::Log(std::ostream &out) const {
134 int log_len = 0, max_len = 0;
135 glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len);
136 std::unique_ptr<char[]> log(new char[max_len]);
137 glGetProgramInfoLog(handle, max_len, &log_len, log.get());
138 out.write(log.get(), log_len);
142 GLint Program::AttributeLocation(const GLchar *name) const noexcept {
143 return glGetAttribLocation(handle, name);
146 GLint Program::AttributeLocation(const std::string &name) const noexcept {
147 return AttributeLocation(name.c_str());
150 GLint Program::UniformLocation(const GLchar *name) const noexcept {
151 return glGetUniformLocation(handle, name);
154 GLint Program::UniformLocation(const std::string &name) const noexcept {
155 return UniformLocation(name.c_str());
159 void Program::Uniform(GLint loc, GLint val) noexcept {
160 glUniform1i(loc, val);
163 void Program::Uniform(GLint loc, float val) noexcept {
164 glUniform1f(loc, val);
167 void Program::Uniform(GLint loc, const glm::vec3 &val) noexcept {
168 glUniform3fv(loc, 1, glm::value_ptr(val));
171 void Program::Uniform(GLint loc, const glm::vec4 &val) noexcept {
172 glUniform4fv(loc, 1, glm::value_ptr(val));
175 void Program::Uniform(GLint loc, const glm::mat4 &val) noexcept {
176 glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val));
180 constexpr int PlanetSurface::MAX_LIGHTS;
182 PlanetSurface::PlanetSurface()
186 "#version 330 core\n"
188 "layout(location = 0) in vec3 vtx_position;\n"
189 "layout(location = 1) in vec3 vtx_tex_uv;\n"
193 "uniform mat4 MVP;\n"
195 "out vec3 frag_tex_uv;\n"
196 "out vec3 vtx_viewspace;\n"
199 "gl_Position = MVP * vec4(vtx_position, 1);\n"
200 "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
201 "frag_tex_uv = vtx_tex_uv;\n"
206 "#version 330 core\n"
208 "struct LightSource {\n"
214 "in vec3 vtx_viewspace;\n"
215 "in vec3 frag_tex_uv;\n"
217 "uniform sampler2DArray tex_sampler;\n"
218 "uniform vec3 normal;\n"
219 "uniform int num_lights;\n"
220 "uniform LightSource light[8];\n"
225 "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
226 "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
227 "for (int i = 0; i < num_lights; ++i) {\n"
228 "vec3 to_light = light[i].position - vtx_viewspace;\n"
229 "float distance = length(to_light) + length(vtx_viewspace);\n"
230 "vec3 light_dir = normalize(to_light);\n"
231 "float attenuation = light[i].strength / (distance * distance);\n"
232 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
233 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
234 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
235 "if (dot(normal, light_dir) >= 0.0) {\n"
236 "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
238 "total_light = total_light + diffuse + specular;\n"
240 "color = total_light;\n"
244 if (!prog.Linked()) {
246 throw std::runtime_error("link program");
248 m_handle = prog.UniformLocation("M");
249 mv_handle = prog.UniformLocation("MV");
250 mvp_handle = prog.UniformLocation("MVP");
251 sampler_handle = prog.UniformLocation("tex_sampler");
252 normal_handle = prog.UniformLocation("normal");
253 num_lights_handle = prog.UniformLocation("num_lights");
254 for (int i = 0; i < MAX_LIGHTS; ++i) {
255 light_handle[3 * i + 0] = prog.UniformLocation("light[" + std::to_string(i) + "].position");
256 light_handle[3 * i + 1] = prog.UniformLocation("light[" + std::to_string(i) + "].color");
257 light_handle[3 * i + 2] = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
261 PlanetSurface::~PlanetSurface() {
264 void PlanetSurface::Activate() noexcept {
266 glEnable(GL_DEPTH_TEST);
267 glDepthFunc(GL_LESS);
268 glEnable(GL_CULL_FACE);
272 void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
278 prog.Uniform(m_handle, m);
279 prog.Uniform(mv_handle, mv);
280 prog.Uniform(mvp_handle, mvp);
283 void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
284 prog.Uniform(normal_handle, n);
287 void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
288 glActiveTexture(GL_TEXTURE0);
290 prog.Uniform(sampler_handle, GLint(0));
293 void PlanetSurface::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
294 prog.Uniform(light_handle[3 * n + 0], pos);
295 prog.Uniform(light_handle[3 * n + 1], color);
296 prog.Uniform(light_handle[3 * n + 2], strength);
299 void PlanetSurface::SetNumLights(int n) noexcept {
300 prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
304 SunSurface::SunSurface()
308 "#version 330 core\n"
310 "layout(location = 0) in vec3 vtx_position;\n"
314 "uniform mat4 MVP;\n"
316 "out vec3 vtx_viewspace;\n"
319 "gl_Position = MVP * vec4(vtx_position, 1);\n"
320 "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
325 "#version 330 core\n"
327 "in vec3 vtx_viewspace;\n"
329 "uniform vec3 light_color;\n"
330 "uniform float light_strength;\n"
335 "vec3 to_light = -vtx_viewspace;\n"
336 "float distance = length(to_light);\n"
337 //"vec3 light_dir = normalize(to_light);\n"
338 "float attenuation = light_strength / (distance * distance);\n"
339 "color = attenuation * light_color;\n"
343 if (!prog.Linked()) {
345 throw std::runtime_error("link program");
347 m_handle = prog.UniformLocation("M");
348 mv_handle = prog.UniformLocation("MV");
349 mvp_handle = prog.UniformLocation("MVP");
350 light_color_handle = prog.UniformLocation("light_color");
351 light_strength_handle = prog.UniformLocation("light_strength");
354 vao.BindAttributes();
355 vao.EnableAttribute(0);
356 vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
357 vao.ReserveAttributes(8, GL_STATIC_DRAW);
359 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
360 attrib[0].position = glm::vec3(-1.0f, -1.0f, -1.0f);
361 attrib[1].position = glm::vec3(-1.0f, -1.0f, 1.0f);
362 attrib[2].position = glm::vec3(-1.0f, 1.0f, -1.0f);
363 attrib[3].position = glm::vec3(-1.0f, 1.0f, 1.0f);
364 attrib[4].position = glm::vec3( 1.0f, -1.0f, -1.0f);
365 attrib[5].position = glm::vec3( 1.0f, -1.0f, 1.0f);
366 attrib[6].position = glm::vec3( 1.0f, 1.0f, -1.0f);
367 attrib[7].position = glm::vec3( 1.0f, 1.0f, 1.0f);
370 vao.ReserveElements(36, GL_STATIC_DRAW);
372 auto element = vao.MapElements(GL_WRITE_ONLY);
419 SunSurface::~SunSurface() {
422 void SunSurface::Activate() noexcept {
424 glEnable(GL_DEPTH_TEST);
425 glDepthFunc(GL_LESS);
426 glEnable(GL_CULL_FACE);
430 void SunSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
436 prog.Uniform(m_handle, m);
437 prog.Uniform(mv_handle, mv);
438 prog.Uniform(mvp_handle, mvp);
441 void SunSurface::SetLight(const glm::vec3 &color, float strength) noexcept {
442 prog.Uniform(light_color_handle, color);
443 prog.Uniform(light_strength_handle, strength);
446 void SunSurface::Draw() const noexcept {
448 vao.DrawTriangles(36);
452 constexpr int CreatureSkin::MAX_LIGHTS;
454 CreatureSkin::CreatureSkin()
458 "#version 330 core\n"
460 "layout(location = 0) in vec3 vtx_position;\n"
461 "layout(location = 1) in vec3 vtx_normal;\n"
462 "layout(location = 2) in vec3 vtx_tex_uv;\n"
466 "uniform mat4 MVP;\n"
468 "out vec3 vtx_viewspace;\n"
469 "out vec3 frag_tex_uv;\n"
473 "gl_Position = MVP * vec4(vtx_position, 1);\n"
474 "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
475 "normal = normalize((MV * vec4(vtx_normal, 0)).xyz);\n"
476 "frag_tex_uv = vtx_tex_uv;\n"
481 "#version 330 core\n"
483 "struct LightSource {\n"
489 "in vec3 vtx_viewspace;\n"
490 "in vec3 frag_tex_uv;\n"
493 "uniform sampler2DArray tex_sampler;\n"
494 "uniform int num_lights;\n"
495 "uniform LightSource light[8];\n"
500 "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
501 "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
502 "for (int i = 0; i < num_lights; ++i) {\n"
503 "vec3 to_light = light[i].position - vtx_viewspace;\n"
504 "float distance = length(to_light) + length(vtx_viewspace);\n"
505 "vec3 light_dir = normalize(to_light);\n"
506 "float attenuation = light[i].strength / (distance * distance);\n"
507 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
508 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
509 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
510 "if (dot(normal, light_dir) >= 0.0) {\n"
511 "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
513 "total_light = total_light + diffuse + specular;\n"
515 "color = total_light;\n"
519 if (!prog.Linked()) {
521 throw std::runtime_error("link program");
523 m_handle = prog.UniformLocation("M");
524 mv_handle = prog.UniformLocation("MV");
525 mvp_handle = prog.UniformLocation("MVP");
526 sampler_handle = prog.UniformLocation("tex_sampler");
527 num_lights_handle = prog.UniformLocation("num_lights");
528 for (int i = 0; i < MAX_LIGHTS; ++i) {
529 light_handle[3 * i + 0] = prog.UniformLocation("light[" + std::to_string(i) + "].position");
530 light_handle[3 * i + 1] = prog.UniformLocation("light[" + std::to_string(i) + "].color");
531 light_handle[3 * i + 2] = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
535 CreatureSkin::~CreatureSkin() {
538 void CreatureSkin::Activate() noexcept {
540 glEnable(GL_DEPTH_TEST);
541 glDepthFunc(GL_LESS);
542 glEnable(GL_CULL_FACE);
546 void CreatureSkin::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
552 prog.Uniform(m_handle, m);
553 prog.Uniform(mv_handle, mv);
554 prog.Uniform(mvp_handle, mvp);
557 void CreatureSkin::SetTexture(ArrayTexture &tex) noexcept {
558 glActiveTexture(GL_TEXTURE0);
560 prog.Uniform(sampler_handle, GLint(0));
563 void CreatureSkin::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
564 prog.Uniform(light_handle[3 * n + 0], pos);
565 prog.Uniform(light_handle[3 * n + 1], color);
566 prog.Uniform(light_handle[3 * n + 2], strength);
569 void CreatureSkin::SetNumLights(int n) noexcept {
570 prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));