diff --git a/CMakeLists.txt b/CMakeLists.txt index 21ca183..73e1a79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.25) + option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_AUTHOR "Application author" "C. J. Howard") diff --git a/src/engine/gl/cube-map-layout.hpp b/src/engine/gl/cube-map-layout.hpp new file mode 100644 index 0000000..d0cbb46 --- /dev/null +++ b/src/engine/gl/cube-map-layout.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_CUBE_MAP_LAYOUT_HPP +#define ANTKEEPER_GL_CUBE_MAP_LAYOUT_HPP + +#include + +namespace gl { + +/// Cube map layout types. +enum class cube_map_layout: std::uint8_t +{ + /// Faces are stored consecutively in a vertical column. + column = 1, + + /// Faces are stored consecutively in a horizontal row. + row, + + /// Faces are stored in a vertical cross. + vertical_cross, + + /// Faces are stored in a horizontal cross. + horizontal_cross, + + /// Faces are stored in an equirectangular projection. + equirectangular, + + /// Faces are stored in a spherical projection. + spherical +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_CUBE_MAP_LAYOUT_HPP diff --git a/src/engine/gl/framebuffer.cpp b/src/engine/gl/framebuffer.cpp index 98fb3f5..7d19770 100644 --- a/src/engine/gl/framebuffer.cpp +++ b/src/engine/gl/framebuffer.cpp @@ -58,14 +58,20 @@ void framebuffer::attach(framebuffer_attachment_type attachment_type, texture_2d glBindFramebuffer(GL_FRAMEBUFFER, gl_framebuffer_id); GLenum gl_attachment = attachment_lut[static_cast(attachment_type)]; - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_2D, texture->gl_texture_id, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_2D, texture->m_gl_texture_id, 0); if (attachment_type == framebuffer_attachment_type::color) + { color_attachment = texture; + } else if (attachment_type == framebuffer_attachment_type::depth) + { depth_attachment = texture; + } else if (attachment_type == framebuffer_attachment_type::stencil) + { stencil_attachment = texture; + } if (!color_attachment) { diff --git a/src/engine/gl/opengl/gl-shader-variables.cpp b/src/engine/gl/opengl/gl-shader-variables.cpp index a91fbe4..e2b7034 100644 --- a/src/engine/gl/opengl/gl-shader-variables.cpp +++ b/src/engine/gl/opengl/gl-shader-variables.cpp @@ -451,7 +451,7 @@ void gl_shader_texture_1d::update(const texture_1d& value) const noexcept { // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); - glBindTexture(GL_TEXTURE_1D, value.gl_texture_id); + glBindTexture(GL_TEXTURE_1D, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); @@ -463,7 +463,7 @@ void gl_shader_texture_1d::update(const texture_1d& value, std::size_t index) co // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); - glBindTexture(GL_TEXTURE_1D, value.gl_texture_id); + glBindTexture(GL_TEXTURE_1D, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); @@ -475,7 +475,7 @@ void gl_shader_texture_1d::update(std::span values, std for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_1D, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -488,7 +488,7 @@ void gl_shader_texture_1d::update(std::span> v for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_1D, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -507,7 +507,7 @@ void gl_shader_texture_2d::update(const texture_2d& value) const noexcept { // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); - glBindTexture(GL_TEXTURE_2D, value.gl_texture_id); + glBindTexture(GL_TEXTURE_2D, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); @@ -519,7 +519,7 @@ void gl_shader_texture_2d::update(const texture_2d& value, std::size_t index) co // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); - glBindTexture(GL_TEXTURE_2D, value.gl_texture_id); + glBindTexture(GL_TEXTURE_2D, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); @@ -531,7 +531,7 @@ void gl_shader_texture_2d::update(std::span values, std for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_2D, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -544,7 +544,7 @@ void gl_shader_texture_2d::update(std::span> v for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_2D, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -563,7 +563,7 @@ void gl_shader_texture_3d::update(const texture_3d& value) const noexcept { // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); - glBindTexture(GL_TEXTURE_3D, value.gl_texture_id); + glBindTexture(GL_TEXTURE_3D, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); @@ -575,7 +575,7 @@ void gl_shader_texture_3d::update(const texture_3d& value, std::size_t index) co // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); - glBindTexture(GL_TEXTURE_3D, value.gl_texture_id); + glBindTexture(GL_TEXTURE_3D, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); @@ -587,7 +587,7 @@ void gl_shader_texture_3d::update(std::span values, std for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_3D, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -600,7 +600,7 @@ void gl_shader_texture_3d::update(std::span> v for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_3D, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -619,7 +619,7 @@ void gl_shader_texture_cube::update(const texture_cube& value) const noexcept { // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); - glBindTexture(GL_TEXTURE_CUBE_MAP, value.gl_texture_id); + glBindTexture(GL_TEXTURE_CUBE_MAP, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); @@ -631,7 +631,7 @@ void gl_shader_texture_cube::update(const texture_cube& value, std::size_t index // Bind texture to texture unit glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); - glBindTexture(GL_TEXTURE_CUBE_MAP, value.gl_texture_id); + glBindTexture(GL_TEXTURE_CUBE_MAP, value.m_gl_texture_id); // Pass texture unit index to shader glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); @@ -643,7 +643,7 @@ void gl_shader_texture_cube::update(std::span values, for (std::size_t i = 0; i < values.size(); ++i) { glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader @@ -656,7 +656,7 @@ void gl_shader_texture_cube::update(std::span(gl_texture_unit_indices[index + i])); - glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); + glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->m_gl_texture_id); } // Pass texture unit indices to shader diff --git a/src/engine/gl/texture-1d.cpp b/src/engine/gl/texture-1d.cpp index b3a89ae..5ae5744 100644 --- a/src/engine/gl/texture-1d.cpp +++ b/src/engine/gl/texture-1d.cpp @@ -22,10 +22,7 @@ namespace gl { texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): - texture(width, type, format, color_space, data) -{} - -texture_1d::~texture_1d() + texture(width, false, type, format, color_space, data) {} void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) diff --git a/src/engine/gl/texture-1d.hpp b/src/engine/gl/texture-1d.hpp index 213899d..b3d6e8b 100644 --- a/src/engine/gl/texture-1d.hpp +++ b/src/engine/gl/texture-1d.hpp @@ -30,11 +30,12 @@ namespace gl { class texture_1d: public texture { public: - /// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) explicit texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); - /// Destructs a 1D texture. - virtual ~texture_1d(); + [[nodiscard]] inline constexpr texture_type get_texture_type() const noexcept override + { + return texture_type::one_dimensional; + } /// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); diff --git a/src/engine/gl/texture-2d.cpp b/src/engine/gl/texture-2d.cpp index 8bab3e6..ef4912b 100644 --- a/src/engine/gl/texture-2d.cpp +++ b/src/engine/gl/texture-2d.cpp @@ -22,10 +22,7 @@ namespace gl { texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): - texture(width, height, type, format, color_space, data) -{} - -texture_2d::~texture_2d() + texture(width, height, false, type, format, color_space, data) {} void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) diff --git a/src/engine/gl/texture-2d.hpp b/src/engine/gl/texture-2d.hpp index f044c2f..cb216a0 100644 --- a/src/engine/gl/texture-2d.hpp +++ b/src/engine/gl/texture-2d.hpp @@ -30,14 +30,15 @@ namespace gl { class texture_2d: public texture { public: - /// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); - /// Destructs a 2D texture. - virtual ~texture_2d(); + [[nodiscard]] inline constexpr texture_type get_texture_type() const noexcept override + { + return texture_type::two_dimensional; + } /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) - virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); + void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) override; /** * Resizes the texture. @@ -49,7 +50,7 @@ public: void resize(std::uint16_t width, std::uint16_t height, const std::byte* data); /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping) - virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); + void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t) override; }; } // namespace gl diff --git a/src/engine/gl/texture-3d.cpp b/src/engine/gl/texture-3d.cpp index b4b0515..23fabf8 100644 --- a/src/engine/gl/texture-3d.cpp +++ b/src/engine/gl/texture-3d.cpp @@ -22,10 +22,7 @@ namespace gl { texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): - texture(width, height, depth, type, format, color_space, data) -{} - -texture_3d::~texture_3d() + texture(width, height, depth, false, type, format, color_space, data) {} void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) diff --git a/src/engine/gl/texture-3d.hpp b/src/engine/gl/texture-3d.hpp index 06a5e05..0fc8998 100644 --- a/src/engine/gl/texture-3d.hpp +++ b/src/engine/gl/texture-3d.hpp @@ -30,11 +30,12 @@ namespace gl { class texture_3d: public texture { public: - /// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); - /// Destructs a 3D texture. - virtual ~texture_3d(); + [[nodiscard]] inline constexpr texture_type get_texture_type() const noexcept override + { + return texture_type::three_dimensional; + } /// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); diff --git a/src/engine/gl/texture-cube.cpp b/src/engine/gl/texture-cube.cpp index b4a507a..33c249e 100644 --- a/src/engine/gl/texture-cube.cpp +++ b/src/engine/gl/texture-cube.cpp @@ -18,20 +18,91 @@ */ #include -#include namespace gl { -texture_cube::texture_cube(): - gl_texture_id(0), - face_size(0) +cube_map_layout texture_cube::infer_cube_map_layout(std::uint16_t w, std::uint16_t h) noexcept { - glGenTextures(1, &gl_texture_id); + if (h == w * 6) + { + return cube_map_layout::column; + } + else if (w == h * 6) + { + return cube_map_layout::row; + } + else if (w == (h / 4) * 3) + { + return cube_map_layout::vertical_cross; + } + else if (h == (w / 4) * 3) + { + return cube_map_layout::horizontal_cross; + } + else if (w == h * 2) + { + return cube_map_layout::equirectangular; + } + else if (w == h) + { + return cube_map_layout::spherical; + } + + return {}; } -texture_cube::~texture_cube() +std::uint16_t texture_cube::infer_cube_map_face_size(cube_map_layout layout, std::uint16_t w, std::uint16_t h) noexcept { - glDeleteTextures(1, &gl_texture_id); + switch (layout) + { + case cube_map_layout::column: + case cube_map_layout::spherical: + return w; + + case cube_map_layout::row: + return h; + + case cube_map_layout::vertical_cross: + return h / 4; + + case cube_map_layout::horizontal_cross: + case cube_map_layout::equirectangular: + return w / 4; + + default: + return 0; + } } -} // namespace gl \ No newline at end of file +texture_cube::texture_cube(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): + texture(width, height, true, type, format, color_space, data) +{ + resized(); +} + +void texture_cube::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) +{ + texture::resize(width, height, type, format, color_space, data); + resized(); +} + +void texture_cube::resize(std::uint16_t width, std::uint16_t height, const std::byte* data) +{ + texture::resize(width, height, get_pixel_type(), get_pixel_format(), get_color_space(), data); + resized(); +} + +void texture_cube::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r) +{ + texture::set_wrapping(wrap_s, wrap_t, wrap_r); +} + +void texture_cube::resized() +{ + const auto w = get_width(); + const auto h = get_height(); + const auto layout = infer_cube_map_layout(w, h); + m_face_size = infer_cube_map_face_size(layout, w, h); +} + +} // namespace gl diff --git a/src/engine/gl/texture-cube.hpp b/src/engine/gl/texture-cube.hpp index 5fc4d80..72e3a2e 100644 --- a/src/engine/gl/texture-cube.hpp +++ b/src/engine/gl/texture-cube.hpp @@ -20,36 +20,73 @@ #ifndef ANTKEEPER_GL_TEXTURE_CUBE_HPP #define ANTKEEPER_GL_TEXTURE_CUBE_HPP +#include +#include + namespace gl { /** * A cube texture which can be uploaded to shaders via shader inputs. */ -class texture_cube +class texture_cube: public texture { public: /** - * Creates a cube texture. + * Infers the layout of a cube map from its aspect ratio. + * + * @param w Width of the cube map, in pixels. + * @param h Height of the cube map, in pixels. + * + * @return Inferred cube map layout. */ - texture_cube(); + [[nodiscard]] static cube_map_layout infer_cube_map_layout(std::uint16_t w, std::uint16_t h) noexcept; /** - * Destroys a cube texture. + * Infers the edge length of a cube map face from its layout and resolution. + * + * @param layout Layout of the cube map. + * @param w Width of the cube map, in pixels. + * @param h Height of the cube map, in pixels. + * + * @return Edge length of the cube map faces, in pixels. */ - ~texture_cube(); - - /// Returns the linear size of a cube face, in pixels. - int get_face_size() const; + [[nodiscard]] static std::uint16_t infer_cube_map_face_size(cube_map_layout layout, std::uint16_t w, std::uint16_t h) noexcept; + + /// Constructs a cube texture. + texture_cube(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); + + [[nodiscard]] inline constexpr texture_type get_texture_type() const noexcept override + { + return texture_type::cube; + } - unsigned int gl_texture_id{0}; - int face_size{0}; + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) override; + + /** + * Resizes the texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. + * @param data Pointer to pixel data. + */ + void resize(std::uint16_t width, std::uint16_t height, const std::byte* data); + + /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping, gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r); + + /// Returns the edge length of the cube texture faces, in pixels. + [[nodiscard]] inline std::uint16_t get_face_size() const noexcept + { + return m_face_size; + } + +private: + void resized(); + + std::uint16_t m_face_size{}; }; -inline int texture_cube::get_face_size() const -{ - return face_size; -} - } // namespace gl #endif // ANTKEEPER_GL_TEXTURE_CUBE_HPP diff --git a/src/engine/gl/texture-type.hpp b/src/engine/gl/texture-type.hpp new file mode 100644 index 0000000..71320c9 --- /dev/null +++ b/src/engine/gl/texture-type.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_TYPE_HPP +#define ANTKEEPER_GL_TEXTURE_TYPE_HPP + +#include + +namespace gl { + +/// Texture types. +enum class texture_type: std::uint8_t +{ + /// 1D texture. + one_dimensional, + + /// 2D texture. + two_dimensional, + + /// 3D texture. + three_dimensional, + + /// Cube texture. + cube, +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_TYPE_HPP diff --git a/src/engine/gl/texture.cpp b/src/engine/gl/texture.cpp index 5fea1f3..676d683 100644 --- a/src/engine/gl/texture.cpp +++ b/src/engine/gl/texture.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -123,145 +125,161 @@ static constexpr GLenum mag_filter_lut[] = GL_LINEAR }; -texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): - gl_texture_target((depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D), - gl_texture_id(0), - dimensions({0, 0, 0}), - wrapping({texture_wrapping::repeat, texture_wrapping::repeat, texture_wrapping::repeat}), - filters({texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear}), - max_anisotropy(0.0f) +texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, bool cube, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { - glGenTextures(1, &gl_texture_id); + m_gl_texture_target = static_cast(cube ? GL_TEXTURE_CUBE_MAP : (depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D); + + glGenTextures(1, &m_gl_texture_id); resize(width, height, depth, type, format, color_space, data); - set_wrapping(wrapping[0], wrapping[1], wrapping[2]); - set_filters(std::get<0>(filters), std::get<1>(filters)); - set_max_anisotropy(max_anisotropy); + set_wrapping(m_wrapping[0], m_wrapping[1], m_wrapping[2]); + set_filters(std::get<0>(m_filters), std::get<1>(m_filters)); + set_max_anisotropy(m_max_anisotropy); } -texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): - texture(width, height, 0, type, format, color_space, data) +texture::texture(std::uint16_t width, std::uint16_t height, bool cube, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): + texture(width, height, 0, cube, type, format, color_space, data) {} -texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): - texture(width, 0, 0, type, format, color_space, data) +texture::texture(std::uint16_t width, bool cube, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): + texture(width, 0, 0, cube, type, format, color_space, data) {} texture::~texture() { - glDeleteTextures(1, &gl_texture_id); + glDeleteTextures(1, &m_gl_texture_id); } void texture::set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter) { - filters = {min_filter, mag_filter}; + m_filters = {min_filter, mag_filter}; - GLenum gl_min_filter = min_filter_lut[static_cast(min_filter)]; - GLenum gl_mag_filter = mag_filter_lut[static_cast(mag_filter)]; + GLenum gl_min_filter = min_filter_lut[std::to_underlying(min_filter)]; + GLenum gl_mag_filter = mag_filter_lut[std::to_underlying(mag_filter)]; - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); - glTexParameteri(gl_texture_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); + glBindTexture(m_gl_texture_target, m_gl_texture_id); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); } void texture::set_max_anisotropy(float anisotropy) { - max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy)); + m_max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy)); // Get the maximum supported anisotropy value float gl_max_texture_max_anisotropy; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy); // Lerp between 1.0 and GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT - float gl_max_anisotropy = 1.0f + max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f); + float gl_max_anisotropy = 1.0f + m_max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f); - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameterf(gl_texture_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); + glBindTexture(m_gl_texture_target, m_gl_texture_id); + glTexParameterf(m_gl_texture_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); } void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r) { - wrapping = {wrap_s, wrap_t, wrap_r}; + m_wrapping = {wrap_s, wrap_t, wrap_r}; - GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; - GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; - GLenum gl_wrap_r = wrapping_lut[static_cast(wrap_r)]; + GLenum gl_wrap_s = wrapping_lut[std::to_underlying(wrap_s)]; + GLenum gl_wrap_t = wrapping_lut[std::to_underlying(wrap_t)]; + GLenum gl_wrap_r = wrapping_lut[std::to_underlying(wrap_r)]; - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_R, gl_wrap_r); + glBindTexture(m_gl_texture_target, m_gl_texture_id); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_WRAP_R, gl_wrap_r); } void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t) { - std::get<0>(wrapping) = wrap_s; - std::get<1>(wrapping) = wrap_t; + std::get<0>(m_wrapping) = wrap_s; + std::get<1>(m_wrapping) = wrap_t; - GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; - GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; + GLenum gl_wrap_s = wrapping_lut[std::to_underlying(wrap_s)]; + GLenum gl_wrap_t = wrapping_lut[std::to_underlying(wrap_t)]; - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); + glBindTexture(m_gl_texture_target, m_gl_texture_id); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); } void texture::set_wrapping(gl::texture_wrapping wrap_s) { - std::get<0>(wrapping) = wrap_s; + std::get<0>(m_wrapping) = wrap_s; - GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + GLenum gl_wrap_s = wrapping_lut[std::to_underlying(wrap_s)]; - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glBindTexture(m_gl_texture_target, m_gl_texture_id); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); } void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { - dimensions = {width, height, depth}; - pixel_type = type; - pixel_format = format; - this->color_space = color_space; + m_dimensions = {width, height, depth}; + m_pixel_type = type; + m_pixel_format = format; + m_color_space = color_space; GLenum gl_internal_format; - if (color_space == gl::color_space::srgb) + if (m_color_space == gl::color_space::srgb) { - gl_internal_format = srgb_internal_format_lut[static_cast(format)][static_cast(type)]; + gl_internal_format = srgb_internal_format_lut[std::to_underlying(format)][std::to_underlying(type)]; } else { - gl_internal_format = linear_internal_format_lut[static_cast(format)][static_cast(type)]; + gl_internal_format = linear_internal_format_lut[std::to_underlying(format)][std::to_underlying(type)]; } - GLenum gl_format = pixel_format_lut[static_cast(format)]; - const GLint* gl_swizzle_mask = swizzle_mask_lut[static_cast(format)]; + GLenum gl_format = pixel_format_lut[std::to_underlying(format)]; + const GLint* gl_swizzle_mask = swizzle_mask_lut[std::to_underlying(format)]; - GLenum gl_type = pixel_type_lut[static_cast(type)]; + GLenum gl_type = pixel_type_lut[std::to_underlying(type)]; // Special cases for depth + stencil pixel formats if (gl_internal_format == GL_DEPTH24_STENCIL8) + { gl_type = GL_UNSIGNED_INT_24_8; + } else if (gl_internal_format == GL_DEPTH32F_STENCIL8) + { gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; - + } + glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(gl_texture_target, gl_texture_id); - if (depth) - glTexImage3D(gl_texture_target, 0, gl_internal_format, width, height, depth, 0, gl_format, gl_type, data); - else if (height) - glTexImage2D(gl_texture_target, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data); - else if (width) - glTexImage1D(gl_texture_target, 0, gl_internal_format, width, 0, gl_format, gl_type, data); + glBindTexture(m_gl_texture_target, m_gl_texture_id); - glGenerateMipmap(gl_texture_target); - glTexParameteriv(gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); + switch (m_gl_texture_target) + { + case GL_TEXTURE_1D: + glTexImage1D(m_gl_texture_target, 0, gl_internal_format, width, 0, gl_format, gl_type, data); + break; + + case GL_TEXTURE_2D: + glTexImage2D(m_gl_texture_target, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data); + break; + + case GL_TEXTURE_3D: + glTexImage3D(m_gl_texture_target, 0, gl_internal_format, width, height, depth, 0, gl_format, gl_type, data); + break; + + case GL_TEXTURE_CUBE_MAP: + update_cube_faces(gl_internal_format, gl_format, gl_type, data); + break; + + default: + break; + } + + glGenerateMipmap(m_gl_texture_target); + glTexParameteriv(m_gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); /// @TODO: remove this if (format == pixel_format::d) { - glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS); - glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS); + glTexParameteri(m_gl_texture_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); } } @@ -275,6 +293,184 @@ void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format resize(width, 0, 0, type, format, color_space, data); } +void texture::update_cube_faces(unsigned int gl_internal_format, unsigned int gl_format, unsigned int gl_type, const std::byte* data) +{ + const auto width = get_width(); + const auto height = get_height(); + const auto layout = texture_cube::infer_cube_map_layout(width, height); + const auto face_size = texture_cube::infer_cube_map_face_size(layout, width, height); + + std::size_t channel_count = 0; + switch (m_pixel_format) + { + case pixel_format::d: + case pixel_format::r: + channel_count = 1; + break; + + case pixel_format::ds: + case pixel_format::rg: + channel_count = 2; + break; + + case pixel_format::rgb: + case pixel_format::bgr: + channel_count = 3; + break; + + case pixel_format::rgba: + case pixel_format::bgra: + channel_count = 4; + break; + + default: + break; + } + + std::size_t channel_size = 0; + switch (m_pixel_type) + { + case pixel_type::int_8: + case pixel_type::uint_8: + channel_size = 1; + break; + + case pixel_type::int_16: + case pixel_type::uint_16: + case pixel_type::float_16: + channel_size = 2; + break; + + case pixel_type::int_32: + case pixel_type::uint_32: + case pixel_type::float_32: + channel_size = 4; + break; + + default: + break; + } + + const std::size_t pixel_stride = channel_count * channel_size; + const std::size_t row_stride = static_cast(face_size) * pixel_stride; + const std::size_t face_stride = static_cast(face_size) * row_stride; + + constexpr std::uint16_t vcross_offsets[6][2] = + { + {2, 2}, {0, 2}, + {1, 3}, {1, 1}, + {1, 0}, {1, 2} + }; + constexpr std::uint16_t hcross_offsets[6][2] = + { + {2, 1}, {0, 1}, + {1, 2}, {1, 0}, + {3, 1}, {1, 1} + }; + + std::vector face_buffer(face_stride); + + switch (layout) + { + case cube_map_layout::column: + for (std::uint16_t i = 0; i < 6; ++i) + { + const std::byte* face_data = data + face_stride * (5 - i); + + for (std::uint16_t y = 0; y < face_size; ++y) + { + for (std::uint16_t x = 0; x < face_size; ++x) + { + if (i < 2 || i > 3) + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, face_data + (face_size - y - 1) * row_stride + (face_size - x - 1) * pixel_stride, pixel_stride); + } + else + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, face_data + y * row_stride + x * pixel_stride, pixel_stride); + } + } + } + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl_internal_format, face_size, face_size, 0, gl_format, gl_type, face_buffer.data()); + } + break; + + case cube_map_layout::row: + for (std::uint16_t i = 0; i < 6; ++i) + { + for (std::uint16_t y = 0; y < face_size; ++y) + { + for (std::uint16_t x = 0; x < face_size; ++x) + { + if (i < 2 || i > 3) + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, data + row_stride * (face_size - y - 1) * 6 + row_stride * i + (face_size - x - 1) * pixel_stride, pixel_stride); + } + else + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, data + row_stride * y * 6 + row_stride * i + x * pixel_stride, pixel_stride); + } + } + } + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl_internal_format, face_size, face_size, 0, gl_format, gl_type, face_buffer.data()); + } + break; + + case cube_map_layout::vertical_cross: + for (std::uint16_t i = 0; i < 6; ++i) + { + const std::byte* face_data = data + vcross_offsets[i][1] * row_stride * face_size * 3 + vcross_offsets[i][0] * row_stride; + + for (std::uint16_t y = 0; y < face_size; ++y) + { + for (std::uint16_t x = 0; x < face_size; ++x) + { + if (i < 2 || i > 3) + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, face_data + (face_size - y - 1) * row_stride * 3 + (face_size - x - 1) * pixel_stride, pixel_stride); + } + else + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, face_data + y * row_stride * 3 + x * pixel_stride, pixel_stride); + } + } + } + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl_internal_format, face_size, face_size, 0, gl_format, gl_type, face_buffer.data()); + } + break; + + case cube_map_layout::horizontal_cross: + for (std::uint16_t i = 0; i < 6; ++i) + { + const std::byte* face_data = data + hcross_offsets[i][1] * row_stride * face_size * 4 + hcross_offsets[i][0] * row_stride; + + for (std::uint16_t y = 0; y < face_size; ++y) + { + for (std::uint16_t x = 0; x < face_size; ++x) + { + if (i < 2 || i > 3) + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, face_data + (face_size - y - 1) * row_stride * 4 + (face_size - x - 1) * pixel_stride, pixel_stride); + } + else + { + std::memcpy(face_buffer.data() + y * row_stride + x * pixel_stride, face_data + y * row_stride * 4 + x * pixel_stride, pixel_stride); + } + } + } + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl_internal_format, face_size, face_size, 0, gl_format, gl_type, face_buffer.data()); + } + break; + + default: + throw std::runtime_error("Unsupported cube map layout"); + } +} + } // namespace gl template <> @@ -286,7 +482,9 @@ std::unique_ptr resource_loader::load(::resource // Read image filename std::string image_filename; if (auto element = json_data->find("image"); element != json_data->end()) + { image_filename = element.value().get(); + } // Load image auto image = resource_manager.load<::image>(image_filename); @@ -297,9 +495,13 @@ std::unique_ptr resource_loader::load(::resource { std::string value = element.value().get(); if (value == "linear") + { color_space = gl::color_space::linear; + } else if (value == "srgb") + { color_space = gl::color_space::srgb; + } } // Read extension mode @@ -308,13 +510,21 @@ std::unique_ptr resource_loader::load(::resource { std::string value = element.value().get(); if (value == "clip") + { wrapping = gl::texture_wrapping::clip; + } else if (value == "extend") + { wrapping = gl::texture_wrapping::extend; + } else if (value == "repeat") + { wrapping = gl::texture_wrapping::repeat; + } else if (value == "mirrored_repeat") + { wrapping = gl::texture_wrapping::mirrored_repeat; + } } // Read interpolation mode @@ -338,7 +548,9 @@ std::unique_ptr resource_loader::load(::resource // Read max anisotropy float max_anisotropy = 0.0f; if (auto element = json_data->find("max_anisotropy"); element != json_data->end()) + { max_anisotropy = element.value().get(); + } // Determine pixel type gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; @@ -384,7 +596,9 @@ std::unique_ptr resource_loader::load(::resource // Read image filename std::string image_filename; if (auto element = json_data->find("image"); element != json_data->end()) + { image_filename = element.value().get(); + } // Load image auto image = resource_manager.load<::image>(image_filename); @@ -395,9 +609,13 @@ std::unique_ptr resource_loader::load(::resource { std::string value = element.value().get(); if (value == "linear") + { color_space = gl::color_space::linear; + } else if (value == "srgb") + { color_space = gl::color_space::srgb; + } } // Read extension mode @@ -406,13 +624,21 @@ std::unique_ptr resource_loader::load(::resource { std::string value = element.value().get(); if (value == "clip") + { wrapping = gl::texture_wrapping::clip; + } else if (value == "extend") + { wrapping = gl::texture_wrapping::extend; + } else if (value == "repeat") + { wrapping = gl::texture_wrapping::repeat; + } else if (value == "mirrored_repeat") + { wrapping = gl::texture_wrapping::mirrored_repeat; + } } // Read interpolation mode @@ -436,7 +662,9 @@ std::unique_ptr resource_loader::load(::resource // Read max anisotropy float max_anisotropy = 0.0f; if (auto element = json_data->find("max_anisotropy"); element != json_data->end()) + { max_anisotropy = element.value().get(); + } // Determine pixel type gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; @@ -472,3 +700,123 @@ std::unique_ptr resource_loader::load(::resource return texture; } + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + throw std::runtime_error("3D texture loading not yet supported"); +} + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + // Load JSON data + auto json_data = resource_loader::load(resource_manager, ctx); + + // Read image filename + std::string image_filename; + if (auto element = json_data->find("image"); element != json_data->end()) + { + image_filename = element.value().get(); + } + + // Load image + auto image = resource_manager.load<::image>(image_filename); + + // Read color space + gl::color_space color_space = gl::color_space::linear; + if (auto element = json_data->find("color_space"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + color_space = gl::color_space::linear; + } + else if (value == "srgb") + { + color_space = gl::color_space::srgb; + } + } + + // Read extension mode + gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; + if (auto element = json_data->find("extension"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "clip") + { + wrapping = gl::texture_wrapping::clip; + } + else if (value == "extend") + { + wrapping = gl::texture_wrapping::extend; + } + else if (value == "repeat") + { + wrapping = gl::texture_wrapping::repeat; + } + else if (value == "mirrored_repeat") + { + wrapping = gl::texture_wrapping::mirrored_repeat; + } + } + + // Read interpolation mode + gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; + gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; + if (auto element = json_data->find("interpolation"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + min_filter = gl::texture_min_filter::linear_mipmap_linear; + mag_filter = gl::texture_mag_filter::linear; + } + else if (value == "closest") + { + min_filter = gl::texture_min_filter::nearest_mipmap_nearest; + mag_filter = gl::texture_mag_filter::nearest; + } + } + + // Read max anisotropy + float max_anisotropy = 0.0f; + if (auto element = json_data->find("max_anisotropy"); element != json_data->end()) + { + max_anisotropy = element.value().get(); + } + + // Determine pixel type + gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; + + // Determine pixel format + gl::pixel_format format; + if (image->channel_count() == 1) + { + format = gl::pixel_format::r; + } + else if (image->channel_count() == 2) + { + format = gl::pixel_format::rg; + } + else if (image->channel_count() == 3) + { + format = gl::pixel_format::rgb; + } + else if (image->channel_count() == 4) + { + format = gl::pixel_format::rgba; + } + else + { + throw std::runtime_error(std::format("Texture image has unsupported number of channels ({})", image->channel_count())); + } + + // Create texture + auto texture = std::make_unique(image->width(), image->height(), type, format, color_space, image->data()); + texture->set_wrapping(wrapping, wrapping, wrapping); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + return texture; +} diff --git a/src/engine/gl/texture.hpp b/src/engine/gl/texture.hpp index 67e9677..4a1c581 100644 --- a/src/engine/gl/texture.hpp +++ b/src/engine/gl/texture.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -44,29 +45,10 @@ class gl_shader_texture_cube; class texture { public: - /** - * Constructs a texture. - * - * @param width Texture width, in pixels. - * @param height Texture height, in pixels. For 2D or 3D textures. - * @param depth Texture depth, in pixels. For 3D textures only. - * @param type Pixel component data type. - * @param format Pixel format. - * @param color_space Color space of the pixel data. - * @param data Pointer to pixel data. - * - * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. - */ - /// @{ - texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); - texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); - explicit texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); - /// @} - /** * Destructs a texture. */ - virtual ~texture() = 0; + virtual ~texture(); /** * Sets the texture filtering modes. @@ -83,37 +65,89 @@ public: */ void set_max_anisotropy(float anisotropy); + /// Returns the texture type. + [[nodiscard]] virtual constexpr texture_type get_texture_type() const noexcept = 0; + /// Returns the dimensions of the texture, in pixels. - const std::array& get_dimensions() const; + [[nodiscard]] inline const std::array& get_dimensions() const noexcept + { + return m_dimensions; + } /// Returns the width of the texture, in pixels. - const std::uint16_t& get_width() const; + [[nodiscard]] inline std::uint16_t get_width() const noexcept + { + return m_dimensions[0]; + } /// Returns the height of the texture, in pixels. - const std::uint16_t& get_height() const; + [[nodiscard]] inline std::uint16_t get_height() const noexcept + { + return m_dimensions[1]; + } /// Returns the depth of the texture, in pixels. - const std::uint16_t& get_depth() const; + [[nodiscard]] inline std::uint16_t get_depth() const noexcept + { + return m_dimensions[2]; + } /// Returns the pixel type enumeration. - const pixel_type& get_pixel_type() const; + [[nodiscard]] inline pixel_type get_pixel_type() const noexcept + { + return m_pixel_type; + } /// Returns the pixel format enumeration. - const pixel_format& get_pixel_format() const; + [[nodiscard]] inline pixel_format get_pixel_format() const noexcept + { + return m_pixel_format; + } /// Returns the color space enumeration. - const color_space& get_color_space() const; + [[nodiscard]] inline color_space get_color_space() const noexcept + { + return m_color_space; + } /// Returns the wrapping modes of the texture. - const std::array& get_wrapping() const; + [[nodiscard]] inline const std::array& get_wrapping() const noexcept + { + return m_wrapping; + } /// Returns the filtering modes of the texture. - const std::tuple& get_filters() const; + [[nodiscard]] inline const std::tuple& get_filters() const noexcept + { + return m_filters; + } /// Returns the maximum anisotropy. - float get_max_anisotropy() const; + [[nodiscard]] inline float get_max_anisotropy() const noexcept + { + return m_max_anisotropy; + } protected: + /** + * Constructs a texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. For 2D or 3D textures. + * @param depth Texture depth, in pixels. For 3D textures only. + * @param type Pixel component data type. + * @param format Pixel format. + * @param color_space Color space of the pixel data. + * @param data Pointer to pixel data. + * + * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. + */ + /// @{ + texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, bool cube = false, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); + texture(std::uint16_t width, std::uint16_t height, bool cube = false, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); + explicit texture(std::uint16_t width, bool cube = false, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); + /// @} + /** * Sets the texture wrapping modes. * @@ -147,73 +181,25 @@ protected: /// @} private: + void update_cube_faces(unsigned int gl_internal_format, unsigned int gl_format, unsigned int gl_type, const std::byte* data); + friend class framebuffer; friend class gl_shader_texture_1d; friend class gl_shader_texture_2d; friend class gl_shader_texture_3d; friend class gl_shader_texture_cube; - unsigned int gl_texture_target{0}; - unsigned int gl_texture_id{0}; - std::array dimensions{0, 0, 0}; - gl::pixel_type pixel_type{0}; - gl::pixel_format pixel_format{0}; - gl::color_space color_space{0}; - std::array wrapping; - std::tuple filters; - float max_anisotropy{0.0f}; + unsigned int m_gl_texture_target{}; + unsigned int m_gl_texture_id{}; + std::array m_dimensions{}; + gl::pixel_type m_pixel_type{}; + gl::pixel_format m_pixel_format{}; + gl::color_space m_color_space{}; + std::array m_wrapping{texture_wrapping::repeat, texture_wrapping::repeat, texture_wrapping::repeat}; + std::tuple m_filters{texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear}; + float m_max_anisotropy{}; }; -inline const std::array& texture::get_dimensions() const -{ - return dimensions; -} - -inline const std::uint16_t& texture::get_width() const -{ - return dimensions[0]; -} - -inline const std::uint16_t& texture::get_height() const -{ - return dimensions[1]; -} - -inline const std::uint16_t& texture::get_depth() const -{ - return dimensions[2]; -} - -inline const pixel_type& texture::get_pixel_type() const -{ - return pixel_type; -} - -inline const pixel_format& texture::get_pixel_format() const -{ - return pixel_format; -} - -inline const color_space& texture::get_color_space() const -{ - return color_space; -} - -inline const std::array& texture::get_wrapping() const -{ - return wrapping; -} - -inline const std::tuple& texture::get_filters() const -{ - return filters; -} - -inline float texture::get_max_anisotropy() const -{ - return max_anisotropy; -} - } // namespace gl #endif // ANTKEEPER_GL_TEXTURE_HPP diff --git a/src/engine/render/material.cpp b/src/engine/render/material.cpp index af000d5..b555011 100644 --- a/src/engine/render/material.cpp +++ b/src/engine/render/material.cpp @@ -199,9 +199,68 @@ static bool load_texture_2d_property(resource_manager& resource_manager, render: return true; } +static bool load_texture_3d_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json) +{ + // If JSON element is an array + if (json.is_array()) + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + variable->set(i, resource_manager.load(element.get())); + ++i; + } + + material.set_variable(key, variable); + } + else + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load texture + variable->set(resource_manager.load(json.get())); + + material.set_variable(key, variable); + } + + return true; +} + static bool load_texture_cube_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json) { - return false; + // If JSON element is an array + if (json.is_array()) + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + variable->set(i, resource_manager.load(element.get())); + ++i; + } + + material.set_variable(key, variable); + } + else + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load texture + variable->set(resource_manager.load(json.get())); + + material.set_variable(key, variable); + } + + return true; } template @@ -445,6 +504,10 @@ std::unique_ptr resource_loader::load(::reso { load_texture_2d_property(resource_manager, *material, key, value_element.value()); } + else if (type == "texture_3d") + { + load_texture_3d_property(resource_manager, *material, key, value_element.value()); + } else if (type == "texture_cube") { load_texture_cube_property(resource_manager, *material, key, value_element.value()); diff --git a/src/engine/scene/light-type.hpp b/src/engine/scene/light-type.hpp index 1d117eb..d80cd6c 100644 --- a/src/engine/scene/light-type.hpp +++ b/src/engine/scene/light-type.hpp @@ -40,7 +40,10 @@ enum class light_type: std::uint8_t point, /// Rectangle light. - rectangle + rectangle, + + /// Sky light. + sky }; } // namespace scene diff --git a/src/engine/scene/sky-light.cpp b/src/engine/scene/sky-light.cpp new file mode 100644 index 0000000..30f4ca0 --- /dev/null +++ b/src/engine/scene/sky-light.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace scene { + + +} // namespace scene diff --git a/src/engine/scene/sky-light.hpp b/src/engine/scene/sky-light.hpp new file mode 100644 index 0000000..9f19f94 --- /dev/null +++ b/src/engine/scene/sky-light.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_SKY_LIGHT_HPP +#define ANTKEEPER_SCENE_SKY_LIGHT_HPP + +#include +#include + +namespace scene { + +/** + * + */ +class sky_light: public light +{ +public: + [[nodiscard]] inline light_type get_light_type() const noexcept override + { + return light_type::sky; + } + + + +private: + +}; + +} // namespace scene + +#endif // ANTKEEPER_SCENE_SKY_LIGHT_HPP diff --git a/src/game/states/main-menu-state.cpp b/src/game/states/main-menu-state.cpp index 723d5f0..afc693a 100644 --- a/src/game/states/main-menu-state.cpp +++ b/src/game/states/main-menu-state.cpp @@ -129,8 +129,8 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - //ctx.state_machine.emplace(std::make_unique(ctx)); - //ctx.state_machine.emplace(std::make_unique(ctx)); + // ctx.state_machine.emplace(std::make_unique(ctx)); + // ctx.state_machine.emplace(std::make_unique(ctx)); // ctx.state_machine.emplace(std::make_unique(ctx)); ctx.state_machine.emplace(std::make_unique(ctx)); } diff --git a/src/game/states/nest-view-state.cpp b/src/game/states/nest-view-state.cpp index 61493ca..ad21b96 100644 --- a/src/game/states/nest-view-state.cpp +++ b/src/game/states/nest-view-state.cpp @@ -193,7 +193,7 @@ nest_view_state::nest_view_state(::game& ctx): larva_eid, [&](auto& component) { - component.object->set_translation({-10.0f, -1.5f, -10.0f}); + component.object->set_translation({5.0f, 0.0f, 5.0f}); } ); @@ -206,7 +206,20 @@ nest_view_state::nest_view_state(::game& ctx): suzanne_eid, [&](auto& component) { - component.object->set_translation({-13.0f, 0.5f, -6.0f}); + component.object->set_translation({0.0f, 0.0f, 0.0f}); + } + ); + + // Create sphere + auto sphere_eid = ctx.entity_registry->create(); + auto sphere_static_mesh = std::make_shared(ctx.resource_manager->load("sphere.mdl")); + ctx.entity_registry->emplace(sphere_eid, std::move(sphere_static_mesh), std::uint8_t{2}); + ctx.entity_registry->patch + ( + sphere_eid, + [&](auto& component) + { + component.object->set_translation({0.0f, 0.0f, 0.0f}); } );