This commit is contained in:
59
src/graphics/opengl/buffer.cpp
Normal file
59
src/graphics/opengl/buffer.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "graphics/opengl/buffer.hpp"
|
||||
#include <memory>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
buffer::s_ptr buffer::make(GLenum target) noexcept {
|
||||
GLuint name = 0;
|
||||
glGenBuffers(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<buffer>(name, target);
|
||||
}
|
||||
|
||||
void buffer::destroy() noexcept {
|
||||
if (m_id)
|
||||
glDeleteBuffers(1, &m_id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void buffer::bind() const noexcept {
|
||||
return glBindBuffer(m_target, m_id);
|
||||
}
|
||||
|
||||
void buffer::bind_to_location(std::uint32_t location) const noexcept {
|
||||
return glBindBufferBase(m_target, location, m_id);
|
||||
}
|
||||
|
||||
void buffer::upload(GLenum usage, std::size_t size, const void* data) noexcept {
|
||||
m_size = size;
|
||||
|
||||
bind();
|
||||
|
||||
return glBufferData(m_target, static_cast<GLsizeiptr>(size), data, usage);
|
||||
}
|
||||
|
||||
void buffer::update(std::size_t offset, std::size_t size, const void* data) noexcept {
|
||||
bind();
|
||||
|
||||
return glBufferSubData(m_target, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), data);
|
||||
}
|
||||
|
||||
void buffer::set_vertex_attrib(std::uint32_t index,
|
||||
std::size_t size,
|
||||
GLenum type,
|
||||
std::size_t stride,
|
||||
std::size_t offset,
|
||||
bool normalised) noexcept {
|
||||
bind();
|
||||
|
||||
return glVertexAttribPointer(index,
|
||||
static_cast<GLint>(size),
|
||||
type,
|
||||
normalised,
|
||||
static_cast<GLsizei>(stride),
|
||||
reinterpret_cast<const void*>(offset));
|
||||
}
|
||||
118
src/graphics/opengl/framebuffer.cpp
Normal file
118
src/graphics/opengl/framebuffer.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
framebuffer::s_ptr framebuffer::make() noexcept {
|
||||
GLuint name = 0;
|
||||
glGenFramebuffers(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<framebuffer>(name);
|
||||
}
|
||||
|
||||
framebuffer::s_ptr framebuffer::make_default(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
if (!width || !height)
|
||||
return nullptr;
|
||||
|
||||
auto fb = framebuffer::make();
|
||||
if (!fb)
|
||||
return nullptr;
|
||||
|
||||
// Colour
|
||||
|
||||
auto col_tex = texture::make();
|
||||
col_tex->upload(
|
||||
GL_RGBA8, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
col_tex->set_param(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
col_tex->set_param(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Depth & stencil
|
||||
|
||||
auto depth_tex = texture::make();
|
||||
depth_tex->upload(GL_DEPTH24_STENCIL8,
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
GL_DEPTH_STENCIL,
|
||||
GL_UNSIGNED_INT_24_8,
|
||||
nullptr);
|
||||
depth_tex->set_param(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
depth_tex->set_param(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
fb->attach_colour(std::move(col_tex));
|
||||
fb->attach_depth(std::move(depth_tex));
|
||||
|
||||
fb->bind();
|
||||
const auto fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
void framebuffer::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteFramebuffers(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void framebuffer::bind() const noexcept {
|
||||
return glBindFramebuffer(GL_FRAMEBUFFER, m_id);
|
||||
}
|
||||
|
||||
void framebuffer::resize(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
for (const auto& col_tex : m_colour_texs) {
|
||||
if (col_tex == nullptr)
|
||||
continue;
|
||||
|
||||
col_tex->resize(width, height);
|
||||
}
|
||||
|
||||
if (m_depth_tex) {
|
||||
m_depth_tex->resize(width, height);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void framebuffer::attach_colour(texture::s_ptr&& tex, std::uint32_t attachment_point) noexcept {
|
||||
KUIPER_ASSERT(attachment_point < framebuffer::max_colour_attachments);
|
||||
|
||||
m_colour_texs[attachment_point] = std::move(tex);
|
||||
const auto& size = m_colour_texs[attachment_point]->dimensions();
|
||||
m_width = size.x;
|
||||
m_height = size.y;
|
||||
|
||||
bind();
|
||||
return glFramebufferTexture2D(GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0 + attachment_point,
|
||||
GL_TEXTURE_2D,
|
||||
m_colour_texs[attachment_point]->id(),
|
||||
0);
|
||||
}
|
||||
|
||||
void framebuffer::attach_depth(texture::s_ptr&& tex) noexcept {
|
||||
m_depth_tex = std::move(tex);
|
||||
const auto& size = m_depth_tex->dimensions();
|
||||
m_width = size.x;
|
||||
m_height = size.y;
|
||||
|
||||
bind();
|
||||
return glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_tex->id(), 0);
|
||||
}
|
||||
149
src/graphics/opengl/program.cpp
Normal file
149
src/graphics/opengl/program.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "graphics/opengl/program.hpp"
|
||||
|
||||
#include "glm/ext/vector_uint3.hpp"
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
#include "logging/engine_logger.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
program::s_ptr program::make() noexcept {
|
||||
GLuint name = glCreateProgram();
|
||||
|
||||
return std::make_shared<program>(name);
|
||||
}
|
||||
|
||||
program::s_ptr program::from_shaders(std::initializer_list<shader::s_ptr> shaders) {
|
||||
auto prog = program::make();
|
||||
|
||||
for (const auto& s : shaders) {
|
||||
prog->attach_shader(s);
|
||||
}
|
||||
|
||||
if (!prog->link()) {
|
||||
std::array<char, 0x80> link_err_buf {};
|
||||
GLsizei n_chars = 0;
|
||||
glGetProgramInfoLog(prog->id(), link_err_buf.size() - 1 /* null terminator */, &n_chars, link_err_buf.data());
|
||||
engine_logger::get().error("Failed to link program {}: {}", prog->id(), link_err_buf.data());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
void program::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteProgram(m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::use() const noexcept {
|
||||
return glUseProgram(m_id);
|
||||
}
|
||||
|
||||
void program::attach_shader(const shader::s_ptr& shader) noexcept {
|
||||
return glAttachShader(m_id, shader->id());
|
||||
}
|
||||
|
||||
bool program::link() noexcept {
|
||||
glLinkProgram(m_id);
|
||||
|
||||
GLint link_status = GL_FALSE;
|
||||
glGetProgramiv(m_id, GL_LINK_STATUS, &link_status);
|
||||
|
||||
return link_status == GL_TRUE;
|
||||
}
|
||||
|
||||
// Uniforms
|
||||
|
||||
GLint program::uniform_location(const char* name) const noexcept {
|
||||
const auto loc = glGetUniformLocation(m_id, name);
|
||||
|
||||
if (loc < 0)
|
||||
engine_logger::get().error("glGetUniformLocation failed for uniform with name: \"{}\"", name);
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
void program::set_uint(const char* uniform, std::uint32_t value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1ui(loc, value);
|
||||
}
|
||||
|
||||
void program::set_int(const char* uniform, std::int32_t value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1i(loc, value);
|
||||
}
|
||||
|
||||
void program::set_int_array(const char* uniform, const std::int32_t* value_arr, std::size_t len) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1iv(loc, static_cast<GLsizei>(len), value_arr);
|
||||
}
|
||||
|
||||
void program::set_float(const char* uniform, float value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1f(loc, value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_vec3(const char* uniform, const glm::vec3& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform3f(loc, value.x, value.y, value.z);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_uvec3(const char* uniform, const glm::uvec3& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform3ui(loc, value.x, value.y, value.z);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_uvec2(const char* uniform, const glm::uvec2& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform2ui(loc, value.x, value.y);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_mat3(const char* uniform, const glm::mat3& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniformMatrix3fv(loc, 1, GL_FALSE, glm::value_ptr(value));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_mat4(const char* uniform, const glm::mat4& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(value));
|
||||
|
||||
return;
|
||||
}
|
||||
94
src/graphics/opengl/render_context.cpp
Normal file
94
src/graphics/opengl/render_context.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "graphics/opengl/render_context.hpp"
|
||||
|
||||
#include "logging/engine_logger.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
using namespace kuiper;
|
||||
using namespace kuiper::gl;
|
||||
|
||||
void gl::initialise() {
|
||||
int version = gladLoadGL(glfwGetProcAddress);
|
||||
if (!version) {
|
||||
throw std::runtime_error("Failed to load & resolve OpenGL symbols with GLAD");
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
#if !defined(NDEBUG)
|
||||
GLint flags = 0;
|
||||
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(gl::debug_callback, nullptr);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto logger = engine_logger::get();
|
||||
logger.debug("OpenGL version information:");
|
||||
logger.debug("\tVendor: {}", (const char*) glGetString(GL_VENDOR));
|
||||
logger.debug("\tRenderer: {}", (const char*) glGetString(GL_RENDERER));
|
||||
logger.debug("\tVersion: {}", (const char*) glGetString(GL_VERSION));
|
||||
logger.debug("\tGLSL version: {}", (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
dump_hardware_info();
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gl::dump_hardware_info() {
|
||||
auto& logger = engine_logger::get();
|
||||
|
||||
logger.debug("OpenGL driver information:");
|
||||
logger.debug("\tGL_MAX_FRAGMENT_UNIFORM_BLOCKS: {}", render_context::get<GLint>(GL_MAX_FRAGMENT_UNIFORM_BLOCKS));
|
||||
logger.debug("\tGL_MAX_UNIFORM_BLOCK_SIZE: {}", render_context::get<GLint>(GL_MAX_UNIFORM_BLOCK_SIZE));
|
||||
logger.debug("\tGL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: {}",
|
||||
render_context::get<GLint>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT));
|
||||
logger.debug("\tGL_MAX_COLOR_ATTACHMENTS: {}", render_context::get<GLint>(GL_MAX_COLOR_ATTACHMENTS));
|
||||
logger.debug("\tGL_MAX_DRAW_BUFFERS: {}", render_context::get<GLint>(GL_MAX_DRAW_BUFFERS));
|
||||
logger.debug("\tGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: {}",
|
||||
render_context::get<GLint>(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
|
||||
logger.debug("\tGL_MAX_TEXTURE_IMAGE_UNITS: {}", render_context::get<GLint>(GL_MAX_TEXTURE_IMAGE_UNITS));
|
||||
logger.debug("\tGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: {}",
|
||||
render_context::get<GLint>(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
void gl::debug_callback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
const void* userParam) noexcept {
|
||||
auto logger = engine_logger::get();
|
||||
logger.error("OpenGL error: {}", message);
|
||||
}
|
||||
#endif
|
||||
|
||||
void render_context::bind_framebuffer(const framebuffer::s_ptr& fb) noexcept {
|
||||
return fb->bind();
|
||||
}
|
||||
|
||||
void render_context::bind_default_framebuffer() noexcept {
|
||||
return glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void render_context::set_viewport_size(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
return glViewport(0, 0, static_cast<GLint>(width), static_cast<GLint>(height));
|
||||
}
|
||||
|
||||
void render_context::enable_feature(GLenum feature) noexcept {
|
||||
return glEnable(feature);
|
||||
}
|
||||
|
||||
void render_context::disable_feature(GLenum feature) noexcept {
|
||||
return glDisable(feature);
|
||||
}
|
||||
91
src/graphics/opengl/shader.cpp
Normal file
91
src/graphics/opengl/shader.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
shader::s_ptr shader::make(shader_type type) noexcept {
|
||||
|
||||
GLenum gl_type = 0;
|
||||
switch (type) {
|
||||
case shader_type::vertex:
|
||||
gl_type = GL_VERTEX_SHADER;
|
||||
break;
|
||||
case shader_type::fragment:
|
||||
gl_type = GL_FRAGMENT_SHADER;
|
||||
break;
|
||||
case shader_type::compute:
|
||||
gl_type = GL_COMPUTE_SHADER;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLuint name = glCreateShader(gl_type);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<shader>(name, gl_type);
|
||||
}
|
||||
|
||||
shader::s_ptr shader::from_path(const std::filesystem::path& shader_path, shader_type type) noexcept {
|
||||
if (shader_path.empty() || !std::filesystem::is_regular_file(shader_path) || !std::filesystem::exists(shader_path))
|
||||
return nullptr;
|
||||
|
||||
const auto file_size = std::filesystem::file_size(shader_path);
|
||||
std::vector<char> read_buf(file_size + 1); // +1 for NUL terminator
|
||||
std::ifstream in_file(shader_path); // TODO: std::ios::ate & tellg() trick
|
||||
|
||||
if (!in_file.is_open())
|
||||
return nullptr;
|
||||
|
||||
in_file.read(read_buf.data(), file_size);
|
||||
|
||||
std::string_view file_ext(shader_path.extension().c_str());
|
||||
|
||||
if (type == shader_type::none) {
|
||||
if (file_ext.compare(".vs") == 0 || file_ext.compare(".vert") == 0) {
|
||||
type = shader_type::vertex;
|
||||
} else if (file_ext.compare(".fs") == 0 || file_ext.compare(".frag") == 0) {
|
||||
type = shader_type::fragment;
|
||||
} else if (file_ext.compare(".cs") == 0 || file_ext.compare(".comp") == 0) {
|
||||
type = shader_type::compute;
|
||||
}
|
||||
}
|
||||
|
||||
return shader::from_source(read_buf.data(), type);
|
||||
}
|
||||
|
||||
shader::s_ptr shader::from_source(std::string_view source, shader_type type) noexcept {
|
||||
auto shader = shader::make(type);
|
||||
shader->attach_source(source);
|
||||
shader->compile();
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
void shader::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteShader(m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void shader::attach_source(std::string_view source) noexcept {
|
||||
const GLint n_shader_sources = 1;
|
||||
const char* const s = source.data();
|
||||
const GLint s_len = static_cast<GLint>(source.size());
|
||||
|
||||
return glShaderSource(m_id, n_shader_sources, &s, &s_len);
|
||||
}
|
||||
|
||||
void shader::compile() noexcept {
|
||||
|
||||
return glCompileShader(m_id);
|
||||
}
|
||||
136
src/graphics/opengl/texture.cpp
Normal file
136
src/graphics/opengl/texture.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
texture::s_ptr texture::make(texture_type type) noexcept {
|
||||
GLuint name = 0;
|
||||
|
||||
glGenTextures(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
GLenum gl_target = 0;
|
||||
switch (type) {
|
||||
case texture_type::tex_2d:
|
||||
gl_target = GL_TEXTURE_2D;
|
||||
break;
|
||||
case texture_type::none:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<texture>(name, gl_target);
|
||||
}
|
||||
|
||||
texture::s_ptr texture::make_placeholder() noexcept {
|
||||
// FIXME: this might be cooked because it's endian-dependent
|
||||
static constexpr std::array<std::uint32_t, 16> data {0xff00ffff,
|
||||
0xff00ffff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0xff00ffff,
|
||||
0xff00ffff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0xff00ffff,
|
||||
0xff00ffff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0xff00ffff,
|
||||
0xff00ffff};
|
||||
|
||||
auto tex = texture::make();
|
||||
tex->upload(GL_RGBA, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
|
||||
tex->gen_mipmaps();
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
texture::s_ptr texture::from_image_path(const std::filesystem::path& image_path, bool flip_y) noexcept {
|
||||
if (image_path.empty() || !std::filesystem::is_regular_file(image_path) || !std::filesystem::exists(image_path))
|
||||
return nullptr;
|
||||
|
||||
const auto file_size = std::filesystem::file_size(image_path);
|
||||
std::vector<std::uint8_t> read_buf(file_size);
|
||||
std::ifstream in_file(image_path, std::ios::binary); // TODO: std::ios::ate & tellg() trick
|
||||
|
||||
if (!in_file.is_open())
|
||||
return nullptr;
|
||||
|
||||
in_file.read((char*) read_buf.data(), file_size);
|
||||
|
||||
return texture::from_image_data(read_buf.data(), read_buf.size(), flip_y);
|
||||
}
|
||||
|
||||
texture::s_ptr texture::from_image_data(const std::uint8_t* buffer, std::size_t length, bool flip_y) noexcept {
|
||||
if (buffer == nullptr || length == 0)
|
||||
return nullptr;
|
||||
|
||||
stbi_set_flip_vertically_on_load(flip_y);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels_in_file = 0;
|
||||
std::uint8_t* image_data =
|
||||
stbi_load_from_memory(buffer, static_cast<int>(length), &width, &height, &channels_in_file, 4);
|
||||
|
||||
if (!image_data)
|
||||
return nullptr;
|
||||
|
||||
auto tex = texture::make();
|
||||
tex->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
|
||||
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
void texture::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void texture::bind() noexcept {
|
||||
return glBindTexture(m_target, m_id);
|
||||
}
|
||||
|
||||
void texture::upload(
|
||||
GLint internal_fmt, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data) noexcept {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_internal_fmt = internal_fmt;
|
||||
m_pixel_fmt = format;
|
||||
m_pixel_type = type;
|
||||
|
||||
bind();
|
||||
return glTexImage2D(m_target, 0, internal_fmt, width, height, 0, format, type, data);
|
||||
}
|
||||
|
||||
void texture::resize(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
return upload(
|
||||
m_internal_fmt, static_cast<GLsizei>(width), static_cast<GLsizei>(height), m_pixel_fmt, m_pixel_type, nullptr);
|
||||
}
|
||||
|
||||
void texture::set_param(GLenum name, GLint param) noexcept {
|
||||
bind();
|
||||
|
||||
return glTexParameteri(m_target, name, param);
|
||||
}
|
||||
|
||||
void texture::gen_mipmaps() noexcept {
|
||||
bind();
|
||||
|
||||
return glGenerateMipmap(m_target);
|
||||
}
|
||||
80
src/graphics/opengl/texture_array.cpp
Normal file
80
src/graphics/opengl/texture_array.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "graphics/opengl/texture_array.hpp"
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
texture_array::s_ptr texture_array::make(GLenum target) noexcept {
|
||||
GLuint name = 0;
|
||||
|
||||
glCreateTextures(target, 1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<texture_array>(name, target);
|
||||
}
|
||||
|
||||
void texture_array::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void texture_array::bind(std::uint32_t unit) const noexcept {
|
||||
return glBindTextureUnit(unit, m_id);
|
||||
}
|
||||
|
||||
void texture_array::set_storage(GLenum internal_fmt,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t depth) noexcept {
|
||||
constexpr GLsizei mip_levels = 1; // only allocating storage for level 0, call gen_mipmaps()
|
||||
|
||||
glTextureStorage3D(m_id,
|
||||
mip_levels,
|
||||
internal_fmt,
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
static_cast<GLsizei>(depth));
|
||||
|
||||
m_internal_fmt = internal_fmt;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = depth;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void texture_array::upload(std::uint32_t x_offset,
|
||||
std::uint32_t y_offset,
|
||||
std::uint32_t z_offset,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t depth,
|
||||
GLenum format,
|
||||
GLenum type,
|
||||
const void* data,
|
||||
std::uint32_t mip_level) noexcept {
|
||||
return glTextureSubImage3D(m_id,
|
||||
static_cast<GLint>(mip_level),
|
||||
static_cast<GLint>(x_offset),
|
||||
static_cast<GLint>(y_offset),
|
||||
static_cast<GLint>(z_offset),
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
static_cast<GLsizei>(depth),
|
||||
format,
|
||||
type,
|
||||
data);
|
||||
}
|
||||
|
||||
void texture_array::gen_mipmaps() noexcept {
|
||||
return glGenerateTextureMipmap(m_id);
|
||||
}
|
||||
|
||||
void texture_array::set_param(GLenum name, GLint param) noexcept {
|
||||
return glTextureParameteri(m_id, name, param);
|
||||
}
|
||||
47
src/graphics/opengl/vertex_array.cpp
Normal file
47
src/graphics/opengl/vertex_array.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "graphics/opengl/vertex_array.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
vertex_array::s_ptr vertex_array::make() noexcept {
|
||||
GLuint name = 0;
|
||||
glGenVertexArrays(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<vertex_array>(name);
|
||||
}
|
||||
|
||||
void vertex_array::destroy() noexcept {
|
||||
if (m_id)
|
||||
glDeleteVertexArrays(1, &m_id);
|
||||
|
||||
return;
|
||||
}
|
||||
void vertex_array::bind() const noexcept {
|
||||
return glBindVertexArray(m_id);
|
||||
}
|
||||
|
||||
void vertex_array::enable_attrib(std::uint32_t index) noexcept {
|
||||
bind();
|
||||
return glEnableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
void vertex_array::disable_attrib(std::uint32_t index) noexcept {
|
||||
bind();
|
||||
return glDisableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
void vertex_array::set_binding_divisor(std::uint32_t binding, std::uint32_t divisor) noexcept {
|
||||
bind();
|
||||
return glVertexBindingDivisor(binding, divisor);
|
||||
}
|
||||
|
||||
void vertex_array::set_attrib_divisor(std::uint32_t attrib, std::uint32_t divisor) noexcept {
|
||||
bind();
|
||||
return glVertexAttribDivisor(attrib, divisor);
|
||||
}
|
||||
Reference in New Issue
Block a user