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});
}
);