/* * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include #include #include #include "glutils.hpp" namespace glutils { void deleteBuffer(GLuint b) noexcept { glDeleteBuffers(1, &b); } void deleteFrameBuffer(GLuint b) noexcept { glDeleteFramebuffers(1, &b); } void deleteRenderBuffer(GLuint b) noexcept { glDeleteRenderbuffers(1, &b); } void deleteTexture(GLuint t) noexcept { glDeleteTextures(1, &t); } void deleteVertexArray(GLuint v) noexcept { glDeleteVertexArrays(1, &v); } void deleteProgram(GLuint p) noexcept { glDeleteProgram(p); } void deleteShader(GLuint s) noexcept { glDeleteShader(s); } template struct GLObject; template struct GLObject; template struct GLObject; template struct GLObject; template struct GLObject; template struct GLObject; template struct GLObject; const FrameBuffer *FrameBufferBind::s_activeFb = nullptr; FrameBufferBind::FrameBufferBind(const FrameBuffer &fb) noexcept: m_restoreFb(s_activeFb) { s_activeFb = &fb; glBindFramebuffer(GL_FRAMEBUFFER, fb); glViewport(0, 0, fb.width, fb.height); } FrameBufferBind::~FrameBufferBind() noexcept { s_activeFb = m_restoreFb; if (s_activeFb) { glBindFramebuffer(GL_FRAMEBUFFER, *s_activeFb); glViewport(0, 0, s_activeFb->width, s_activeFb->height); } else { glBindFramebuffer(GL_FRAMEBUFFER, 0); } } void bind(const FrameBuffer &fb) noexcept { glBindFramebuffer(GL_FRAMEBUFFER, fb); glViewport(0, 0, fb.width, fb.height); } static ox::Result buildShader( GLuint shaderType, const GLchar *src, ox::CRStringView shaderName) noexcept { GLShader shader(glCreateShader(shaderType)); glShaderSource(shader, 1, &src, nullptr); glCompileShader(shader); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { ox::Vector errMsg(ox::units::KB); glGetShaderInfoLog(shader, static_cast(errMsg.size()), nullptr, errMsg.data()); oxErrorf("shader compile error in {}: {}", shaderName, errMsg.data()); return OxError(1, "shader compile error"); } return shader; } ox::Result buildShaderProgram( ox::CStringView const&vert, ox::CStringView const&frag, ox::CStringView const&geo) noexcept { GLProgram prgm(glCreateProgram()); oxRequire(vs, buildShader(GL_VERTEX_SHADER, vert.c_str(), "vshad")); glAttachShader(prgm, vs); if (geo.c_str() && geo.bytes() != 0) { oxRequire(gs, buildShader(GL_GEOMETRY_SHADER, geo.c_str(), "gshad")); glAttachShader(prgm, gs); } oxRequire(fs, buildShader(GL_FRAGMENT_SHADER, frag.c_str(), "fshad")); glAttachShader(prgm, fs); glLinkProgram(prgm); return prgm; } GLVertexArray generateVertexArrayObject() noexcept { GLVertexArray vao; glGenVertexArrays(1, &vao.id); return vao; } GLBuffer generateBuffer() noexcept { GLBuffer buff; glGenBuffers(1, &buff.id); return buff; } FrameBuffer generateFrameBuffer(int width, int height) noexcept { width = ox::max(1, width); height = ox::max(1, height); FrameBuffer fb; fb.width = width; fb.height = height; glGenFramebuffers(1, &fb.fbo.id); glBindFramebuffer(GL_FRAMEBUFFER, fb); // color texture glGenTextures(1, &fb.color.id); glBindTexture(GL_TEXTURE_2D, fb.color); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.color, 0); // depth texture glGenRenderbuffers(1, &fb.depth.id); glBindRenderbuffer(GL_RENDERBUFFER, fb.depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.depth); // verify FBO oxAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Frame Buffer is incomplete"); // restore primary FB glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0); return fb; } void resizeInitFrameBuffer(FrameBuffer &fb, int width, int height) noexcept { if (!fb) { fb = generateFrameBuffer(width, height); return; } width = ox::max(1, width); height = ox::max(1, height); fb.width = width; fb.height = height; glBindFramebuffer(GL_FRAMEBUFFER, fb); // color texture glBindTexture(GL_TEXTURE_2D, fb.color); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); // depth texture glBindRenderbuffer(GL_RENDERBUFFER, fb.depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); // restore primary FB glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0); } void sendVbo(BufferSet const&bs) noexcept { const auto bufferSize = static_cast(sizeof(decltype(bs.vertices)::value_type) * bs.vertices.size()); glBindBuffer(GL_ARRAY_BUFFER, bs.vbo); glBufferData(GL_ARRAY_BUFFER, bufferSize, bs.vertices.data(), GL_DYNAMIC_DRAW); } void sendEbo(BufferSet const&bs) noexcept { const auto bufferSize = static_cast(sizeof(decltype(bs.elements)::value_type) * bs.elements.size()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bs.ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferSize, bs.elements.data(), GL_STATIC_DRAW); } void clearScreen() noexcept { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); } }