extern "C" { #include } #include "gl.h" #include "log.h" #include "context.h" #include "texture.h" #include "objects/object.h" namespace Mauri { auto uniform_type_to_string(UniformType type) -> std::string { switch (type) { case TYPE_UINT: return "uint"; case TYPE_FLOAT: return "float"; case TYPE_VEC4: return "vec4"; case TYPE_VEC3: return "vec3"; case TYPE_VEC2: return "vec2"; case TYPE_MAT4: return "mat4"; case TYPE_MAT3: return "mat3"; case TYPE_SAMPLER2D: return "sampler2D"; case TYPE_INVALID: default: return "INVALID"; } } auto uniform_type_from_string(const std::string_view &s) -> UniformType { if (s.compare(0, 4, "uint") == 0) return TYPE_UINT; else if (s.compare(0, 5, "float") == 0) return TYPE_FLOAT; else if (s.compare(0, 4, "vec4") == 0) return TYPE_VEC4; else if (s.compare(0, 4, "vec3") == 0) return TYPE_VEC3; else if (s.compare(0, 4, "vec2") == 0) return TYPE_VEC2; else if (s.compare(0, 4, "mat4") == 0) return TYPE_MAT4; else if (s.compare(0, 4, "mat3") == 0) return TYPE_MAT3; else if (s.compare(0, 9, "sampler2D") == 0) return TYPE_SAMPLER2D; return TYPE_INVALID; } } // namespace Mauri using namespace Mauri; RenderObject::RenderObject(ObjectType type, Object *object, Texture *texture) { if (!context()->is_draw_enabled()) return; f32 width = (object) ? object->size[0] : 0.f; f32 height = (object) ? object->size[1] : 0.f; f32 x_scale = 1.f; f32 y_scale = 1.f; if (texture && !texture->is_gif() && !(type == COMBINE && object->effects.size() != 0)) { x_scale = (*texture->texture_resolution)[2] / (*texture->texture_resolution)[0]; y_scale = (*texture->texture_resolution)[3] / (*texture->texture_resolution)[1]; } switch (type) { case DEFAULT: { f32 vertices[] = { -1.f, -1.f, 0.f, 0.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, -1.f, 0.f, 1.f, 0.f, 1.f, -1.f, 0.f, 1.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 0.f, 1.f, 1.f }; this->set_data(vertices, sizeof(vertices)); this->vertex_count = 6; break; } case DEFAULT_FLIPPED: { f32 vertices[] = { -1.f, 1.f, 0.f, 0.f, 0.f, -1.f, -1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f, 0.f, -1.f, -1.f, 0.f, 0.f, 1.f, 1.f, -1.f, 0.f, 1.f, 1.f }; this->set_data(vertices, sizeof(vertices)); this->vertex_count = 6; break; } case FULLSCREEN: { f32 vertices[] = { -1.f, -1.f, 0.f, 0.f, 0.f, -1.f, 3.f, 0.f, 0.f, 2.f, 3.f, -1.f, 0.f, 2.f, 0.f }; this->set_data(vertices, sizeof(vertices)); this->vertex_count = 3; break; } case MODEL: { f32 vertices[] = { 0.f, height, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, y_scale, width, height, 0.f, x_scale, 0.f, width, height, 0.f, x_scale, 0.f, 0.f, 0.f, 0.f, 0.f, y_scale, width, 0.f, 0.f, x_scale, y_scale }; this->set_data(vertices, sizeof(vertices)); this->vertex_count = 6; break; } case COMBINE: { f32 half_width = width / 2.f; f32 half_height = height / 2.f; f32 vertices[] = { -half_width, half_height, 0.f, 0.f, 0.f, -half_width, -half_height, 0.f, 0.f, y_scale, half_width, half_height, 0.f, x_scale, 0.f, half_width, half_height, 0.f, x_scale, 0.f, -half_width, -half_height, 0.f, 0.f, y_scale, half_width, -half_height, 0.f, x_scale, y_scale }; this->set_data(vertices, sizeof(vertices)); this->vertex_count = 6; break; } } } auto RenderObject::set_data(f32 *vertices, s32 vertex_count) -> void { if (!context()->is_draw_enabled()) return; glGenVertexArrays(1, &this->vao); glBindVertexArray(this->vao); glGenBuffers(1, &this->vbo); glBindBuffer(GL_ARRAY_BUFFER, this->vbo); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)12); glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); } auto RenderObject::bind() -> void { if (!context()->is_draw_enabled()) return; glBindVertexArray(this->vao); glBindBuffer(GL_ARRAY_BUFFER, this->vbo); } auto RenderObject::draw() -> void { if (!context()->is_draw_enabled()) return; glDrawArrays(GL_TRIANGLES, 0, this->vertex_count); } RenderObject::~RenderObject() { if (!context()->is_draw_enabled()) return; glDeleteVertexArrays(1, &this->vao); glDeleteBuffers(1, &this->vbo); } RenderShader::RenderShader(const std::string &vs_source, const std::string &fs_source) { if (!context()->is_draw_enabled()) return; this->program = glCreateProgram(); char error[256]; const char *c_str; u32 vs_shader = glCreateShader(GL_VERTEX_SHADER); c_str = vs_source.c_str(); if (!compile(vs_shader, c_str, error)) { error("failed to compile vertex shader: (%s)", error); debug("\n%s\n", c_str); return; } u32 fs_shader = glCreateShader(GL_FRAGMENT_SHADER); c_str = fs_source.c_str(); if (!compile(fs_shader, c_str, error)) { error("failed to compile fragment shader: (%s)", error); debug("\n%s\n", c_str); return; } glAttachShader(this->program, vs_shader); glAttachShader(this->program, fs_shader); glLinkProgram(this->program); } auto RenderShader::compile(u32 shader, const char *source, char *error) -> bool { if (!context()->is_draw_enabled()) return true; glShaderSource(shader, 1, &source, 0); glCompileShader(shader); s32 status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { glGetShaderInfoLog(shader, 256, NULL, error); return false; } return true; } auto RenderShader::bind() -> void { if (!context()->is_draw_enabled()) return; glUseProgram(this->program); } auto RenderShader::get_uniform_location(const std::string &name) -> s32 { if (!context()->is_draw_enabled()) return 0; auto loc = this->uniform_locations.find(name); if (loc == this->uniform_locations.end()) { this->uniform_locations[name] = glGetUniformLocation(this->program, name.c_str()); return this->uniform_locations[name]; } else { return loc->second; } } auto RenderShader::set_uniform(const std::string &name, UniformType type, void *value) -> void { if (!context()->is_draw_enabled()) return; s32 loc = this->get_uniform_location(name); if (loc == -1) return; #ifdef UNIFORM_DEBUG al_printf("%s(%i) %f %f %f %f\n", name.c_str(), type, ((f32 *)value)[0], ((f32 *)value)[1], ((f32 *)value)[2], ((f32 *)value)[3]); #endif switch (type) { case TYPE_UINT: case TYPE_SAMPLER2D: glUniform1i(loc, (u32)(*((f32 *)value))); break; case TYPE_FLOAT: glUniform1f(loc, *((f32 *)value)); break; case TYPE_VEC2: glUniform2fv(loc, 1, (f32 *)value); break; case TYPE_VEC3: glUniform3fv(loc, 1, (f32 *)value); break; case TYPE_VEC4: glUniform4fv(loc, 1, (f32 *)value); break; case TYPE_MAT4: glUniformMatrix4fv(loc, 1, GL_FALSE, (f32 *)value); break; case TYPE_MAT3: glUniformMatrix3fv(loc, 1, GL_FALSE, (f32 *)value); break; case TYPE_INVALID: break; } } RenderShader::~RenderShader() { if (!context()->is_draw_enabled()) return; if (this->program != 0) glDeleteShader(this->program); } RenderTexture::RenderTexture(bool no_interp, bool clamp, bool filtering, s32 mm_count) { if (!context()->is_draw_enabled()) return; glGenTextures(1, &this->id); glBindTexture(GL_TEXTURE_2D, this->id); /* if (filtering) { f32 aniso; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &aniso); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso); } */ (void)filtering; if (no_interp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (clamp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mm_count - 1); glBindTexture(GL_TEXTURE_2D, 0); } auto RenderTexture::upload(const void *ptr, s32 width, s32 height, s32 level, s32 iformat) -> void { if (asset_manager()->prepare_output && ptr && level == 0) { this->save_pixels(ptr, width, height, iformat == GL_UNSIGNED_INT_8_8_8_8); } if (!context()->is_draw_enabled()) return; this->bind(0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, format, iformat, ptr); } auto RenderTexture::bind(s32 index) -> void { if (!context()->is_draw_enabled()) return; glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, this->id); } static auto pack_rgba(byte r, byte g, byte b, byte a) -> u32 { return (a << 24 | b << 16 | g << 8 | r); } static auto pack_rgb(byte r, byte g, byte b) -> u32 { return (255 << 24 | b << 16 | g << 8 | r); } static auto pack_rg(byte r, byte g) -> u32 { return (255 << 24 | 0 << 16 | g << 8 | r); } static auto pack_red(byte r) -> u32 { return (255 << 24 | 0 << 16 | 0 << 8 | r); } auto RenderTexture::save_pixels(const void *ptr, s32 width, s32 height, bool dxt) -> void { this->pixels.resize(width * height); if (dxt) { for (s32 i = 0; i < width * height; i++) { this->pixels[i] = nn_htonl(((u32 *)ptr)[i]); } return; } byte *dp = (byte *)ptr; switch (format) { case GL_RGBA: { for (s32 i = 0; i < width * height; i++) { this->pixels[i] = pack_rgba(*dp, *(dp + 1), *(dp + 2), *(dp + 3)); dp += 4; } break; } case GL_RGB: { for (s32 i = 0; i < width * height; i++) { this->pixels[i] = pack_rgb(*dp, *(dp + 1), *(dp + 2)); dp += 3; } break; } case GL_RG: { for (s32 i = 0; i < width * height; i++) { this->pixels[i] = pack_rg(*dp, *(dp + 1)); dp += 2; } break; } case GL_RED: { for (s32 i = 0; i < width * height; i++) { this->pixels[i] = pack_red(*dp); dp += 1; } break; } default: break; } } RenderTexture::~RenderTexture() { if (!context()->is_draw_enabled()) return; glDeleteTextures(1, &this->id); } RenderFramebuffer::RenderFramebuffer(s32 width, s32 height, f32 scale) : width(width / scale), height(height / scale) { this->texture = new RenderTexture(false, true, false, 1); this->texture->format = GL_RGBA; if (!context()->is_draw_enabled()) return; this->texture->upload(nullptr, this->width, this->height, 0); glGenFramebuffers(1, &this->id); glBindFramebuffer(GL_FRAMEBUFFER, this->id); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->texture->id, 0); GLenum attatchment[] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, attatchment); } auto RenderFramebuffer::bind() -> void { if (!context()->is_draw_enabled()) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->id); } auto RenderFramebuffer::blit(u32 dest, s32 width, s32 height) -> void { if (!context()->is_draw_enabled()) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dest); glBindFramebuffer(GL_READ_FRAMEBUFFER, this->id); GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, GL_NEAREST); } auto RenderFramebuffer::clear(vec4 color) -> void { if (!context()->is_draw_enabled()) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->id); glClearBufferfv(GL_COLOR, 0, glm::value_ptr(color)); GLfloat depth = 0.f; glClearBufferfv(GL_DEPTH, 0, &depth); } RenderFramebuffer::~RenderFramebuffer() { if (!context()->is_draw_enabled()) return; delete texture; glDeleteFramebuffers(1, &this->id); } auto Render::blend_func(s32 sfactor, s32 dfactor) -> void { if (!context()->is_draw_enabled()) return; glBlendFunc(sfactor, dfactor); } auto Render::color_mask(f32 r, f32 g, f32 b, f32 a) -> void { if (!context()->is_draw_enabled()) return; glColorMask(r, g, b, a); } auto Render::depth_test(bool enabled) -> void { if (!context()->is_draw_enabled()) return; if (enabled) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); } auto Render::bind_framebuffer(s32 id) -> void { if (!context()->is_draw_enabled()) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id); } auto Render::set_viewport(s32 x, s32 y, s32 width, s32 height) -> void { if (!context()->is_draw_enabled()) return; glViewport(x, y, width, height); }