Browse Source

Upgrade OpenGL from 3.3 to 4.6. Abstract GL interface, closing #8.

master
C. J. Howard 5 months ago
parent
commit
628cbca31d
175 changed files with 10016 additions and 5538 deletions
  1. +1
    -1
      CMakeLists.txt
  2. +2
    -4
      README.md
  3. +14
    -14
      src/engine/app/sdl/sdl-input-manager.cpp
  4. +1
    -1
      src/engine/app/sdl/sdl-input-manager.hpp
  5. +47
    -34
      src/engine/app/sdl/sdl-window-manager.cpp
  6. +6
    -6
      src/engine/app/sdl/sdl-window-manager.hpp
  7. +32
    -32
      src/engine/app/sdl/sdl-window.cpp
  8. +18
    -13
      src/engine/app/sdl/sdl-window.hpp
  9. +11
    -3
      src/engine/app/window.hpp
  10. +2
    -2
      src/engine/color/aces.hpp
  11. +2
    -2
      src/engine/config.hpp.in
  12. +0
    -2
      src/engine/debug/log.cpp
  13. +23
    -24
      src/engine/debug/log.hpp
  14. +11
    -14
      src/engine/debug/log/log-events.hpp
  15. +8
    -5
      src/engine/debug/log/log-message-severity.hpp
  16. +2
    -4
      src/engine/debug/log/logger.cpp
  17. +14
    -11
      src/engine/debug/log/logger.hpp
  18. +32
    -46
      src/engine/geom/brep/brep-operations.cpp
  19. +53
    -0
      src/engine/gl/blend-factor.hpp
  20. +10
    -8
      src/engine/gl/blend-op.hpp
  21. +42
    -0
      src/engine/gl/clear-bits.hpp
  22. +46
    -0
      src/engine/gl/clear-value.hpp
  23. +52
    -0
      src/engine/gl/color-blend-equation.hpp
  24. +45
    -0
      src/engine/gl/color-component-bits.hpp
  25. +57
    -0
      src/engine/gl/compare-op.hpp
  26. +77
    -0
      src/engine/gl/cube-map.cpp
  27. +75
    -0
      src/engine/gl/cube-map.hpp
  28. +45
    -0
      src/engine/gl/cull-mode.hpp
  29. +42
    -0
      src/engine/gl/fill-mode.hpp
  30. +219
    -0
      src/engine/gl/format.hpp
  31. +44
    -0
      src/engine/gl/framebuffer-attachment.hpp
  32. +45
    -0
      src/engine/gl/framebuffer-usage-bits.hpp
  33. +85
    -50
      src/engine/gl/framebuffer.cpp
  34. +32
    -59
      src/engine/gl/framebuffer.hpp
  35. +10
    -15
      src/engine/gl/front-face.hpp
  36. +7
    -7
      src/engine/gl/image-flag.hpp
  37. +9
    -11
      src/engine/gl/image-view-flag.hpp
  38. +298
    -0
      src/engine/gl/image-view.cpp
  39. +292
    -0
      src/engine/gl/image-view.hpp
  40. +1064
    -0
      src/engine/gl/image.cpp
  41. +324
    -0
      src/engine/gl/image.hpp
  42. +42
    -0
      src/engine/gl/index-type.hpp
  43. +50
    -0
      src/engine/gl/logic-op.hpp
  44. +219
    -0
      src/engine/gl/opengl/gl-format-lut.hpp
  45. +33
    -36
      src/engine/gl/opengl/gl-shader-variables.cpp
  46. +1
    -1
      src/engine/gl/opengl/gl-shader-variables.hpp
  47. +62
    -0
      src/engine/gl/pipeline-color-blend-state.hpp
  48. +57
    -0
      src/engine/gl/pipeline-depth-stencil-state.hpp
  49. +40
    -0
      src/engine/gl/pipeline-input-assembly-state.hpp
  50. +73
    -0
      src/engine/gl/pipeline-rasterization-state.hpp
  51. +41
    -0
      src/engine/gl/pipeline-vertex-input-state.hpp
  52. +41
    -0
      src/engine/gl/pipeline-viewport-state.hpp
  53. +1494
    -0
      src/engine/gl/pipeline.cpp
  54. +435
    -0
      src/engine/gl/pipeline.hpp
  55. +66
    -0
      src/engine/gl/primitive-topology.hpp
  56. +39
    -0
      src/engine/gl/provoking-vertex-mode.hpp
  57. +0
    -220
      src/engine/gl/rasterizer.cpp
  58. +0
    -142
      src/engine/gl/rasterizer.hpp
  59. +48
    -0
      src/engine/gl/sampler-address-mode.hpp
  60. +9
    -15
      src/engine/gl/sampler-filter.hpp
  61. +39
    -0
      src/engine/gl/sampler-mipmap-mode.hpp
  62. +230
    -0
      src/engine/gl/sampler.cpp
  63. +274
    -0
      src/engine/gl/sampler.hpp
  64. +16
    -20
      src/engine/gl/scissor-region.hpp
  65. +1
    -25
      src/engine/gl/shader-object.cpp
  66. +1
    -25
      src/engine/gl/shader-program.cpp
  67. +1
    -2
      src/engine/gl/shader-program.hpp
  68. +3
    -3
      src/engine/gl/shader-template.cpp
  69. +42
    -0
      src/engine/gl/stencil-face-bits.hpp
  70. +56
    -0
      src/engine/gl/stencil-op-state.hpp
  71. +57
    -0
      src/engine/gl/stencil-op.hpp
  72. +0
    -38
      src/engine/gl/texture-1d.cpp
  73. +0
    -49
      src/engine/gl/texture-1d.hpp
  74. +0
    -43
      src/engine/gl/texture-2d.cpp
  75. +0
    -58
      src/engine/gl/texture-2d.hpp
  76. +0
    -38
      src/engine/gl/texture-3d.cpp
  77. +0
    -49
      src/engine/gl/texture-3d.hpp
  78. +0
    -110
      src/engine/gl/texture-cube.cpp
  79. +0
    -92
      src/engine/gl/texture-cube.hpp
  80. +181
    -811
      src/engine/gl/texture.cpp
  81. +216
    -182
      src/engine/gl/texture.hpp
  82. +295
    -73
      src/engine/gl/vertex-array.cpp
  83. +23
    -46
      src/engine/gl/vertex-array.hpp
  84. +0
    -72
      src/engine/gl/vertex-attribute.hpp
  85. +64
    -43
      src/engine/gl/vertex-buffer.cpp
  86. +81
    -18
      src/engine/gl/vertex-buffer.hpp
  87. +14
    -19
      src/engine/gl/vertex-input-attribute.hpp
  88. +43
    -0
      src/engine/gl/vertex-input-binding.hpp
  89. +39
    -0
      src/engine/gl/vertex-input-rate.hpp
  90. +53
    -0
      src/engine/gl/viewport.hpp
  91. +44
    -0
      src/engine/math/vector.hpp
  92. +1
    -4
      src/engine/render/material-variable.hpp
  93. +5
    -5
      src/engine/render/material.cpp
  94. +143
    -94
      src/engine/render/model.cpp
  95. +52
    -23
      src/engine/render/model.hpp
  96. +11
    -5
      src/engine/render/operation.hpp
  97. +11
    -6
      src/engine/render/pass.cpp
  98. +26
    -13
      src/engine/render/pass.hpp
  99. +150
    -160
      src/engine/render/passes/bloom-pass.cpp
  100. +24
    -20
      src/engine/render/passes/bloom-pass.hpp

+ 1
- 1
CMakeLists.txt View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.25)
cmake_minimum_required(VERSION 3.28)
option(APPLICATION_NAME "Application name" "Antkeeper")

+ 2
- 4
README.md View File

@ -1,6 +1,6 @@
# Antkeeper Source
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/ec1d9f614fdf4d5b8effa6b7b72b3d5e)](https://www.codacy.com/gh/antkeeper/antkeeper-source/dashboard?utm_source=github.com&utm_medium=referral&utm_content=antkeeper/antkeeper-source&utm_campaign=Badge_Grade)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/ec1d9f614fdf4d5b8effa6b7b72b3d5e)](https://app.codacy.com/gh/antkeeper/antkeeper-source/dashboard)
[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/antkeeper/antkeeper-source)](https://www.tickgit.com/browse?repo=github.com/antkeeper/antkeeper-source)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue)](https://docs.antkeeper.com/latest/index)
[![Discord](https://img.shields.io/discord/547138509610156036?logo=discord)](https://discord.gg/ptwHV4T)
@ -11,9 +11,7 @@ Head over to [antkeeper.com](https://antkeeper.com/) if you're interested in fol
## Building
Download the Antkeeper source code, build system, and all dependencies via the [Antkeeper superbuild](https://github.com/antkeeper/antkeeper-superbuild) repository:
git clone --recursive https://github.com/antkeeper/antkeeper-superbuild.git
Download the Antkeeper source code, build system, and all dependencies via the [Antkeeper superbuild](https://github.com/antkeeper/antkeeper-superbuild) repository.
Detailed configuration and build instructions can be found in the [README](https://github.com/antkeeper/antkeeper-superbuild/blob/master/README.md) of the superbuild repository.

+ 14
- 14
src/engine/app/sdl/sdl-input-manager.cpp View File

@ -30,15 +30,15 @@ namespace app {
sdl_input_manager::sdl_input_manager()
{
// Init SDL joystick and controller subsystems
debug::log::trace("Initializing SDL joystick and controller subsystems...");
debug::log_trace("Initializing SDL joystick and controller subsystems...");
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
{
debug::log::error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError());
debug::log_error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError());
throw std::runtime_error("Failed to initialize SDL joystick and controller subsytems");
}
else
{
debug::log::trace("Initialized SDL joystick and controller subsystems");
debug::log_trace("Initialized SDL joystick and controller subsystems");
}
// Register keyboard and mouse
@ -53,9 +53,9 @@ sdl_input_manager::sdl_input_manager()
sdl_input_manager::~sdl_input_manager()
{
// Quit SDL joystick and controller subsystems
debug::log::trace("Quitting SDL joystick and controller subsystems...");
debug::log_trace("Quitting SDL joystick and controller subsystems...");
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
debug::log::trace("Quit SDL joystick and controller subsystems...");
debug::log_trace("Quit SDL joystick and controller subsystems...");
}
void sdl_input_manager::update()
@ -80,14 +80,14 @@ void sdl_input_manager::update()
}
else if (status < 0)
{
debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
debug::log_error("Failed to peep SDL events: {}", SDL_GetError());
throw std::runtime_error("Failed to peep SDL events");
}
switch (event.type)
{
case SDL_QUIT:
debug::log::debug("Application quit requested");
debug::log_debug("Application quit requested");
this->m_event_dispatcher.dispatch<input::application_quit_event>({});
break;
@ -109,7 +109,7 @@ void sdl_input_manager::update()
}
else if (status < 0)
{
debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
debug::log_error("Failed to peep SDL events: {}", SDL_GetError());
throw std::runtime_error("Failed to peep SDL events");
}
@ -254,7 +254,7 @@ void sdl_input_manager::update()
if (auto it = m_gamepad_map.find(event.cdevice.which); it != m_gamepad_map.end())
{
// Gamepad reconnected
debug::log::info("Reconnected gamepad {}", event.cdevice.which);
debug::log_info("Reconnected gamepad {}", event.cdevice.which);
it->second->connect();
}
else
@ -267,7 +267,7 @@ void sdl_input_manager::update()
::uuid gamepad_uuid;
std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size());
debug::log::info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string());
debug::log_info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string());
// Allocate gamepad
auto& gamepad = m_gamepad_map[event.cdevice.which];
@ -283,7 +283,7 @@ void sdl_input_manager::update()
}
else
{
debug::log::error("Failed to connect gamepad {}: {}", event.cdevice.which, SDL_GetError());
debug::log_error("Failed to connect gamepad {}: {}", event.cdevice.which, SDL_GetError());
SDL_ClearError();
}
}
@ -303,7 +303,7 @@ void sdl_input_manager::update()
it->second->disconnect();
}
debug::log::info("Disconnected gamepad {}", event.cdevice.which);
debug::log_info("Disconnected gamepad {}", event.cdevice.which);
}
break;
@ -322,7 +322,7 @@ void sdl_input_manager::set_cursor_visible(bool visible)
{
if (SDL_ShowCursor((visible) ? SDL_ENABLE : SDL_DISABLE) < 0)
{
debug::log::error("Failed to set cursor visibility: \"{}\"", SDL_GetError());
debug::log_error("Failed to set cursor visibility: \"{}\"", SDL_GetError());
SDL_ClearError();
}
}
@ -331,7 +331,7 @@ void sdl_input_manager::set_relative_mouse_mode(bool enabled)
{
if (SDL_SetRelativeMouseMode((enabled) ? SDL_TRUE : SDL_FALSE) < 0)
{
debug::log::error("Failed to set relative mouse mode: \"{}\"", SDL_GetError());
debug::log_error("Failed to set relative mouse mode: \"{}\"", SDL_GetError());
SDL_ClearError();
}
}

+ 1
- 1
src/engine/app/sdl/sdl-input-manager.hpp View File

@ -41,7 +41,7 @@ public:
/**
* Destructs an SDL input manager.
*/
virtual ~sdl_input_manager();
~sdl_input_manager() override;
void update() override;
void set_cursor_visible(bool visible) override;

+ 47
- 34
src/engine/app/sdl/sdl-window-manager.cpp View File

@ -21,6 +21,7 @@
#include <engine/app/sdl/sdl-window.hpp>
#include <engine/debug/log.hpp>
#include <engine/config.hpp>
#include <engine/gl/pipeline.hpp>
#include <stdexcept>
namespace app {
@ -28,25 +29,25 @@ namespace app {
sdl_window_manager::sdl_window_manager()
{
// Init SDL events and video subsystems
debug::log::trace("Initializing SDL events and video subsystems...");
debug::log_trace("Initializing SDL events and video subsystems...");
if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0)
{
debug::log::fatal("Failed to initialize SDL events and video subsystems: {}", SDL_GetError());
debug::log_fatal("Failed to initialize SDL events and video subsystems: {}", SDL_GetError());
throw std::runtime_error("Failed to initialize SDL events and video subsystems");
}
debug::log::trace("Initialized SDL events and video subsystems");
debug::log_trace("Initialized SDL events and video subsystems");
// Query displays
const int display_count = SDL_GetNumVideoDisplays();
if (display_count < 1)
{
debug::log::warning("No displays detected: {}", SDL_GetError());
debug::log_warning("No displays detected: {}", SDL_GetError());
}
else
{
// Allocate displays
m_displays.resize(display_count);
debug::log::info("Display count: {}", display_count);
debug::log_info("Display count: {}", display_count);
for (int i = 0; i < display_count; ++i)
{
@ -56,25 +57,31 @@ sdl_window_manager::sdl_window_manager()
// Log display information
display& display = m_displays[i];
const auto display_resolution = display.get_bounds().size();
debug::log::info("Display {} name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", i, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi());
debug::log_info("Display {} name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", i, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi());
}
}
// Load OpenGL library
debug::log::trace("Loading OpenGL library...");
debug::log_trace("Loading OpenGL library...");
if (SDL_GL_LoadLibrary(nullptr) != 0)
{
debug::log::fatal("Failed to load OpenGL library: {}", SDL_GetError());
debug::log_fatal("Failed to load OpenGL library: {}", SDL_GetError());
throw std::runtime_error("Failed to load OpenGL library");
}
debug::log::trace("Loaded OpenGL library");
debug::log_trace("Loaded OpenGL library");
// Set OpenGL-related window creation hints
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config::opengl_version_major);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config::opengl_version_minor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
#if defined(DEBUG)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG | SDL_GL_CONTEXT_DEBUG_FLAG);
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config::opengl_min_red_size);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config::opengl_min_green_size);
@ -87,9 +94,9 @@ sdl_window_manager::sdl_window_manager()
sdl_window_manager::~sdl_window_manager()
{
// Quit SDL video subsystem
debug::log::trace("Quitting SDL video subsystem...");
debug::log_trace("Quitting SDL video subsystem...");
SDL_QuitSubSystem(SDL_INIT_VIDEO);
debug::log::trace("Quit SDL video subsystem");
debug::log_trace("Quit SDL video subsystem");
}
std::shared_ptr<window> sdl_window_manager::create_window
@ -136,7 +143,7 @@ void sdl_window_manager::update()
}
else if (status < 0)
{
debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
debug::log_error("Failed to peep SDL events: {}", SDL_GetError());
throw std::runtime_error("Failed to peep SDL events");
}
@ -159,10 +166,16 @@ void sdl_window_manager::update()
window->m_windowed_size = window->m_size;
}
SDL_GL_GetDrawableSize(internal_window, &window->m_viewport_size.x(), &window->m_viewport_size.y());
window->m_rasterizer->context_resized(window->m_viewport_size.x(), window->m_viewport_size.y());
// Change reported dimensions of graphics pipeline default framebuffer
window->m_graphics_pipeline->defaut_framebuffer_resized
(
static_cast<std::uint32_t>(window->m_viewport_size.x()),
static_cast<std::uint32_t>(window->m_viewport_size.y())
);
// Log window resized event
debug::log::debug("Window {} resized to {}x{}", event.window.windowID, event.window.data1, event.window.data2);
debug::log_debug("Window {} resized to {}x{}", event.window.windowID, event.window.data1, event.window.data2);
// Publish window resized event
window->m_resized_publisher.publish({window, window->m_size});
@ -184,7 +197,7 @@ void sdl_window_manager::update()
}
// Log window moved event
debug::log::debug("Window {} moved to ({}, {})", event.window.windowID, event.window.data1, event.window.data2);
debug::log_debug("Window {} moved to ({}, {})", event.window.windowID, event.window.data1, event.window.data2);
// Publish window moved event
window->m_moved_publisher.publish({window, window->m_position});
@ -198,7 +211,7 @@ void sdl_window_manager::update()
app::sdl_window* window = get_window(internal_window);
// Log window focus gained event
debug::log::debug("Window {} gained focus", event.window.windowID);
debug::log_debug("Window {} gained focus", event.window.windowID);
// Publish window focus gained event
window->m_focus_changed_publisher.publish({window, true});
@ -212,7 +225,7 @@ void sdl_window_manager::update()
app::sdl_window* window = get_window(internal_window);
// Log window focus lost event
debug::log::debug("Window {} lost focus", event.window.windowID);
debug::log_debug("Window {} lost focus", event.window.windowID);
// Publish window focus lost event
window->m_focus_changed_publisher.publish({window, false});
@ -229,7 +242,7 @@ void sdl_window_manager::update()
window->m_maximized = true;
// Log window focus gained event
debug::log::debug("Window {} maximized", event.window.windowID);
debug::log_debug("Window {} maximized", event.window.windowID);
// Publish window maximized event
window->m_maximized_publisher.publish({window});
@ -246,7 +259,7 @@ void sdl_window_manager::update()
window->m_maximized = false;
// Log window restored event
debug::log::debug("Window {} restored", event.window.windowID);
debug::log_debug("Window {} restored", event.window.windowID);
// Publish window restored event
window->m_restored_publisher.publish({window});
@ -260,7 +273,7 @@ void sdl_window_manager::update()
app::sdl_window* window = get_window(internal_window);
// Log window focus gained event
debug::log::debug("Window {} minimized", event.window.windowID);
debug::log_debug("Window {} minimized", event.window.windowID);
// Publish window minimized event
window->m_minimized_publisher.publish({window});
@ -274,7 +287,7 @@ void sdl_window_manager::update()
app::sdl_window* window = get_window(internal_window);
// Log window closed event
debug::log::debug("Window {} closed", event.window.windowID);
debug::log_debug("Window {} closed", event.window.windowID);
// Publish window closed event
window->m_closed_publisher.publish({window});
@ -299,7 +312,7 @@ void sdl_window_manager::update()
display.m_connected = true;
// Log display (re)connected event
debug::log::info("Reconnected display {}", event.display.display);
debug::log_info("Reconnected display {}", event.display.display);
// Publish display (re)connected event
display.m_connected_publisher.publish({&display});
@ -315,14 +328,14 @@ void sdl_window_manager::update()
// Log display info
const auto display_resolution = display.get_bounds().size();
debug::log::info("Connected display {}; name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", event.display.display, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi());
debug::log_info("Connected display {}; name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", event.display.display, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi());
// Publish display connected event
display.m_connected_publisher.publish({&display});
}
else
{
debug::log::error("Index of connected display ({}) out of range", event.display.display);
debug::log_error("Index of connected display ({}) out of range", event.display.display);
}
break;
@ -336,14 +349,14 @@ void sdl_window_manager::update()
display.m_connected = false;
// Log display disconnected event
debug::log::info("Disconnected display {}", event.display.display);
debug::log_info("Disconnected display {}", event.display.display);
// Publish display disconnected event
display.m_disconnected_publisher.publish({&display});
}
else
{
debug::log::error("Index of disconnected display ({}) out of range", event.display.display);
debug::log_error("Index of disconnected display ({}) out of range", event.display.display);
}
break;
@ -374,14 +387,14 @@ void sdl_window_manager::update()
}
// Log display orientation changed event
debug::log::info("Display {} orientation changed", event.display.display);
debug::log_info("Display {} orientation changed", event.display.display);
// Publish display orientation changed event
display.m_orientation_changed_publisher.publish({&display, display.get_orientation()});
}
else
{
debug::log::error("Index of orientation-changed display ({}) out of range", event.display.display);
debug::log_error("Index of orientation-changed display ({}) out of range", event.display.display);
}
break;
@ -422,7 +435,7 @@ void sdl_window_manager::update_display(int sdl_display_index)
SDL_DisplayMode sdl_display_mode;
if (SDL_GetDesktopDisplayMode(sdl_display_index, &sdl_display_mode) != 0)
{
debug::log::error("Failed to get mode of display {}: {}", sdl_display_index, SDL_GetError());
debug::log_error("Failed to get mode of display {}: {}", sdl_display_index, SDL_GetError());
SDL_ClearError();
sdl_display_mode = {0, 0, 0, 0, nullptr};
}
@ -431,7 +444,7 @@ void sdl_window_manager::update_display(int sdl_display_index)
const char* sdl_display_name = SDL_GetDisplayName(sdl_display_index);
if (!sdl_display_name)
{
debug::log::warning("Failed to get name of display {}: {}", sdl_display_index, SDL_GetError());
debug::log_warning("Failed to get name of display {}: {}", sdl_display_index, SDL_GetError());
SDL_ClearError();
sdl_display_name = nullptr;
}
@ -440,7 +453,7 @@ void sdl_window_manager::update_display(int sdl_display_index)
SDL_Rect sdl_display_bounds;
if (SDL_GetDisplayBounds(sdl_display_index, &sdl_display_bounds) != 0)
{
debug::log::warning("Failed to get bounds of display {}: {}", sdl_display_index, SDL_GetError());
debug::log_warning("Failed to get bounds of display {}: {}", sdl_display_index, SDL_GetError());
SDL_ClearError();
sdl_display_bounds = {0, 0, sdl_display_mode.w, sdl_display_mode.h};
}
@ -449,7 +462,7 @@ void sdl_window_manager::update_display(int sdl_display_index)
SDL_Rect sdl_display_usable_bounds;
if (SDL_GetDisplayUsableBounds(sdl_display_index, &sdl_display_usable_bounds) != 0)
{
debug::log::warning("Failed to get usable bounds of display {}: {}", sdl_display_index, SDL_GetError());
debug::log_warning("Failed to get usable bounds of display {}: {}", sdl_display_index, SDL_GetError());
SDL_ClearError();
sdl_display_usable_bounds = sdl_display_bounds;
}
@ -458,7 +471,7 @@ void sdl_window_manager::update_display(int sdl_display_index)
float sdl_display_dpi;
if (SDL_GetDisplayDPI(sdl_display_index, &sdl_display_dpi, nullptr, nullptr) != 0)
{
debug::log::warning("Failed to get DPI of display {}: {}", sdl_display_index, SDL_GetError());
debug::log_warning("Failed to get DPI of display {}: {}", sdl_display_index, SDL_GetError());
SDL_ClearError();
sdl_display_dpi = 0.0f;
}

+ 6
- 6
src/engine/app/sdl/sdl-window-manager.hpp View File

@ -44,12 +44,12 @@ public:
/**
* Destructs an SDL window manager.
*/
virtual ~sdl_window_manager();
~sdl_window_manager() override;
virtual void update();
void update() override;
/// @copydoc window::window()
[[nodiscard]] virtual std::shared_ptr<window> create_window
[[nodiscard]] std::shared_ptr<window> create_window
(
const std::string& title,
const math::ivec2& windowed_position,
@ -57,10 +57,10 @@ public:
bool maximized,
bool fullscreen,
bool v_sync
);
) override;
[[nodiscard]] virtual std::size_t get_display_count() const;
[[nodiscard]] virtual const display& get_display(std::size_t index) const;
[[nodiscard]] std::size_t get_display_count() const override;
[[nodiscard]] const display& get_display(std::size_t index) const override;
private:
sdl_window* get_window(SDL_Window* internal_window);

+ 32
- 32
src/engine/app/sdl/sdl-window.cpp View File

@ -20,7 +20,8 @@
#include <engine/app/sdl/sdl-window.hpp>
#include <engine/config.hpp>
#include <engine/debug/log.hpp>
#include <glad/glad.h>
#include <engine/gl/pipeline.hpp>
#include <glad/gl.h>
#include <stdexcept>
namespace app {
@ -47,7 +48,7 @@ sdl_window::sdl_window
}
// Create SDL window
debug::log::trace("Creating SDL window...");
debug::log_trace("Creating SDL window...");
m_internal_window = SDL_CreateWindow
(
title.c_str(),
@ -59,20 +60,20 @@ sdl_window::sdl_window
);
if (!m_internal_window)
{
debug::log::fatal("Failed to create SDL window: {}", SDL_GetError());
debug::log_fatal("Failed to create SDL window: {}", SDL_GetError());
throw std::runtime_error("Failed to create SDL window");
}
debug::log::trace("Created SDL window");
debug::log_trace("Created SDL window");
// Create OpenGL context
debug::log::trace("Creating OpenGL context...");
debug::log_trace("Creating OpenGL context...");
m_internal_context = SDL_GL_CreateContext(m_internal_window);
if (!m_internal_context)
{
debug::log::fatal("Failed to create OpenGL context: {}", SDL_GetError());
debug::log_fatal("Failed to create OpenGL context: {}", SDL_GetError());
throw std::runtime_error("Failed to create OpenGL context");
}
debug::log::trace("Created OpenGL context");
debug::log_trace("Created OpenGL context");
// Query OpenGL context info
int opengl_context_version_major = -1;
@ -93,7 +94,7 @@ sdl_window::sdl_window
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &opengl_context_stencil_size);
// Log OpenGL context info
debug::log::info
debug::log_info
(
"OpenGL context version: {}.{}; format: R{}G{}B{}A{}D{}S{}",
opengl_context_version_major,
@ -110,7 +111,7 @@ sdl_window::sdl_window
if (opengl_context_version_major != config::opengl_version_major ||
opengl_context_version_minor != config::opengl_version_minor)
{
debug::log::warning("Requested OpenGL context version {}.{} but got version {}.{}", config::opengl_version_major, config::opengl_version_minor, opengl_context_version_major, opengl_context_version_minor);
debug::log_warning("Requested OpenGL context version {}.{} but got version {}.{}", config::opengl_version_major, config::opengl_version_minor, opengl_context_version_major, opengl_context_version_minor);
}
// Compare OpenGL context format with requested format
@ -121,7 +122,7 @@ sdl_window::sdl_window
opengl_context_depth_size < config::opengl_min_depth_size ||
opengl_context_stencil_size < config::opengl_min_stencil_size)
{
debug::log::warning
debug::log_warning
(
"OpenGL context format (R{}G{}B{}A{}D{}S{}) does not meet minimum requested format (R{}G{}B{}A{}D{}S{})",
opengl_context_red_size,
@ -140,16 +141,16 @@ sdl_window::sdl_window
}
// Load OpenGL functions via GLAD
debug::log::trace("Loading OpenGL functions...");
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
debug::log_trace("Loading OpenGL functions...");
if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)))
{
debug::log::fatal("Failed to load OpenGL functions", SDL_GetError());
debug::log_fatal("Failed to load OpenGL functions", SDL_GetError());
throw std::runtime_error("Failed to load OpenGL functions");
}
debug::log::trace("Loaded OpenGL functions");
debug::log_trace("Loaded OpenGL functions");
// Log OpenGL information
debug::log::info
debug::log_info
(
"OpenGL vendor: {}; renderer: {}; version: {}; shading language version: {}",
reinterpret_cast<const char*>(glGetString(GL_VENDOR)),
@ -158,9 +159,11 @@ sdl_window::sdl_window
reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION))
);
// Fill window with color
//glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Allocate graphics pipeline
m_graphics_pipeline = std::make_unique<gl::pipeline>();
// Clear default framebuffer to black
m_graphics_pipeline->clear_attachments(gl::color_clear_bit, {{0.0f, 0.0f, 0.0f, 0.0f}});
swap_buffers();
// Enable or disable v-sync
@ -177,15 +180,12 @@ sdl_window::sdl_window
SDL_GetWindowMinimumSize(m_internal_window, &this->m_minimum_size.x(), &this->m_minimum_size.y());
SDL_GetWindowMaximumSize(m_internal_window, &this->m_maximum_size.x(), &this->m_maximum_size.y());
SDL_GL_GetDrawableSize(m_internal_window, &this->m_viewport_size.x(), &this->m_viewport_size.y());
// Allocate m_rasterizer
this->m_rasterizer = std::make_unique<gl::rasterizer>();
}
sdl_window::~sdl_window()
{
// Deallocate m_rasterizer
m_rasterizer.reset();
// Deallocate graphics pipeline
m_graphics_pipeline.reset();
// Destruct the OpenGL context
SDL_GL_DeleteContext(m_internal_context);
@ -246,37 +246,37 @@ void sdl_window::set_v_sync(bool v_sync)
{
if (v_sync)
{
debug::log::trace("Enabling adaptive v-sync...");
debug::log_trace("Enabling adaptive v-sync...");
if (SDL_GL_SetSwapInterval(-1) != 0)
{
debug::log::error("Failed to enable adaptive v-sync: {}", SDL_GetError());
debug::log::trace("Enabling synchronized v-sync...");
debug::log_error("Failed to enable adaptive v-sync: {}", SDL_GetError());
debug::log_trace("Enabling synchronized v-sync...");
if (SDL_GL_SetSwapInterval(1) != 0)
{
debug::log::error("Failed to enable synchronized v-sync: {}", SDL_GetError());
debug::log_error("Failed to enable synchronized v-sync: {}", SDL_GetError());
v_sync = false;
}
else
{
debug::log::debug("Enabled synchronized v-sync");
debug::log_debug("Enabled synchronized v-sync");
}
}
else
{
debug::log::debug("Enabled adaptive v-sync");
debug::log_debug("Enabled adaptive v-sync");
}
}
else
{
debug::log::trace("Disabling v-sync...");
debug::log_trace("Disabling v-sync...");
if (SDL_GL_SetSwapInterval(0) != 0)
{
debug::log::error("Failed to disable v-sync: {}", SDL_GetError());
debug::log_error("Failed to disable v-sync: {}", SDL_GetError());
v_sync = true;
}
else
{
debug::log::debug("Disabled v-sync");
debug::log_debug("Disabled v-sync");
}
}

+ 18
- 13
src/engine/app/sdl/sdl-window.hpp View File

@ -48,20 +48,25 @@ public:
sdl_window& operator=(sdl_window&&) = delete;
virtual ~sdl_window();
virtual void set_title(const std::string& title);
virtual void set_position(const math::ivec2& position);
virtual void set_size(const math::ivec2& size);
virtual void set_minimum_size(const math::ivec2& size);
virtual void set_maximum_size(const math::ivec2& size);
virtual void set_maximized(bool maximized);
virtual void set_fullscreen(bool fullscreen);
virtual void set_v_sync(bool v_sync);
virtual void make_current();
virtual void swap_buffers();
void set_title(const std::string& title) override;
void set_position(const math::ivec2& position) override;
void set_size(const math::ivec2& size) override;
void set_minimum_size(const math::ivec2& size) override;
void set_maximum_size(const math::ivec2& size) override;
void set_maximized(bool maximized) override;
void set_fullscreen(bool fullscreen) override;
void set_v_sync(bool v_sync) override;
void make_current() override;
void swap_buffers() override;
[[nodiscard]] inline virtual gl::rasterizer* get_rasterizer() noexcept
[[nodiscard]] inline const gl::pipeline& get_graphics_pipeline() const noexcept override
{
return m_rasterizer.get();
return *m_graphics_pipeline;
}
[[nodiscard]] inline gl::pipeline& get_graphics_pipeline() noexcept override
{
return *m_graphics_pipeline;
}
private:
@ -69,7 +74,7 @@ private:
SDL_Window* m_internal_window;
SDL_GLContext m_internal_context;
std::unique_ptr<gl::rasterizer> m_rasterizer;
std::unique_ptr<gl::pipeline> m_graphics_pipeline;
};
} // namespace app

+ 11
- 3
src/engine/app/window.hpp View File

@ -23,9 +23,14 @@
#include <engine/math/vector.hpp>
#include <engine/event/publisher.hpp>
#include <engine/app/window-events.hpp>
#include <engine/gl/rasterizer.hpp>
#include <string>
namespace gl {
class pipeline;
}
namespace app {
class window_manager;
@ -168,8 +173,11 @@ public:
return m_v_sync;
}
/// Returns the rasterizer associated with this window.
[[nodiscard]] virtual gl::rasterizer* get_rasterizer() noexcept = 0;
/// Returns the graphics pipeline associated with this window.
/// @{
[[nodiscard]] virtual const gl::pipeline& get_graphics_pipeline() const noexcept = 0;
[[nodiscard]] virtual gl::pipeline& get_graphics_pipeline() noexcept = 0;
/// @}
/// Returns the channel through which window closed events are published.
[[nodiscard]] inline event::channel<window_closed_event>& get_closed_channel() noexcept

+ 2
- 2
src/engine/color/aces.hpp View File

@ -79,11 +79,11 @@ template
/// ACES AP1 RRT saturation adjustment matrix.
template <class T>
constexpr math::mat3<T> aces_ap1_rrt_sat = aces_adjust_saturation(T{0.96}, ap1<T>.to_y);
constexpr math::mat3<T> aces_ap1_rrt_sat = aces_adjust_saturation(T{0.96}, aces_ap1<T>.to_y);
/// ACES AP1 ODT saturation adjustment matrix.
template <class T>
constexpr math::mat3<T> aces_ap1_odt_sat = aces_adjust_saturation(T{0.93}, ap1<T>.to_y);
constexpr math::mat3<T> aces_ap1_odt_sat = aces_adjust_saturation(T{0.93}, aces_ap1<T>.to_y);
/// @}

+ 2
- 2
src/engine/config.hpp.in View File

@ -68,10 +68,10 @@ inline constexpr std::size_t debug_log_archive_capacity = 5;
/// @{
/// OpenGL major version number, used when creating OpenGL contexts.
inline constexpr int opengl_version_major = 3;
inline constexpr int opengl_version_major = 4;
/// OpenGL minor version number, used when creating OpenGL contexts.
inline constexpr int opengl_version_minor = 3;
inline constexpr int opengl_version_minor = 6;
/// Minimum number of bits in the red channel of the color attachment of the OpenGL default framebuffer.
inline constexpr int opengl_min_red_size = 8;

+ 0
- 2
src/engine/debug/log.cpp View File

@ -20,7 +20,6 @@
#include <engine/debug/log.hpp>
namespace debug {
namespace log {
logger& default_logger() noexcept
{
@ -28,5 +27,4 @@ logger& default_logger() noexcept
return instance;
}
} // namespace log
} // namespace debug

+ 23
- 24
src/engine/debug/log.hpp View File

@ -21,7 +21,7 @@
#define ANTKEEPER_DEBUG_LOG_HPP
#include <engine/config.hpp>
#include <engine/debug/log/message-severity.hpp>
#include <engine/debug/log/log-message-severity.hpp>
#include <engine/debug/log/logger.hpp>
#include <source_location>
#include <string>
@ -34,10 +34,8 @@
namespace debug {
/**
* Debug message logging.
*/
namespace log {
/// @name Debug logging
/// @{
/**
* Returns the default logger.
@ -50,8 +48,8 @@ namespace log {
* @tparam Severity Message severity. A message will not log itself if @p Severity is less than the user-defined macro `ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY`.
* @tparam Args Types of arguments to be formatted.
*/
template <message_severity Severity, class... Args>
struct message
template <log_message_severity Severity, class... Args>
struct log_message
{
/**
* Formats and logs a message.
@ -62,14 +60,14 @@ struct message
* @param args Arguments to be formatted.
* @param location Source location from which the message was sent.
*/
message
log_message
(
[[maybe_unused]] std::string_view format,
[[maybe_unused]] Args&&... args,
[[maybe_unused]] std::source_location&& location = std::source_location::current()
)
{
if constexpr (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= static_cast<std::underlying_type_t<message_severity>>(Severity))
if constexpr (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= static_cast<std::underlying_type_t<log_message_severity>>(Severity))
{
default_logger().log(std::vformat(format, std::make_format_args(std::forward<Args>(args)...)), Severity, std::forward<std::source_location>(location));
}
@ -77,8 +75,8 @@ struct message
};
// Use class template argument deduction (CTAD) to capture source location as a default argument following variadic format arguments.
template <message_severity Severity, class... Args>
message(std::string_view, Args&&...) -> message<Severity, Args...>;
template <log_message_severity Severity, class... Args>
log_message(std::string_view, Args&&...) -> log_message<Severity, Args...>;
#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE)
/**
@ -87,11 +85,11 @@ message(std::string_view, Args&&...) -> message;
* @tparam Args Types of arguments to be formatted.
*/
template <class... Args>
using trace = message<message_severity::trace, Args...>;
using log_trace = log_message<log_message_severity::trace, Args...>;
#else
// Disable trace message logging.
template <class... Args>
inline void trace([[maybe_unused]] Args&&...) noexcept {};
inline void log_trace([[maybe_unused]] Args&&...) noexcept {};
#endif
#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_DEBUG)
@ -101,11 +99,11 @@ message(std::string_view, Args&&...) -> message;
* @tparam Args Types of arguments to be formatted.
*/
template <class... Args>
using debug = message<message_severity::debug, Args...>;
using log_debug = log_message<log_message_severity::debug, Args...>;
#else
// Disable debug message logging.
template <class... Args>
inline void debug([[maybe_unused]] Args&&...) noexcept {};
inline void log_debug([[maybe_unused]] Args&&...) noexcept {};
#endif
#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_INFO)
@ -115,11 +113,11 @@ message(std::string_view, Args&&...) -> message;
* @tparam Args Types of arguments to be formatted.
*/
template <class... Args>
using info = message<message_severity::info, Args...>;
using log_info = log_message<log_message_severity::info, Args...>;
#else
// Disable info message logging.
template <class... Args>
inline void info([[maybe_unused]] Args&&...) noexcept {};
inline void log_info([[maybe_unused]] Args&&...) noexcept {};
#endif
#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_WARNING)
@ -129,11 +127,11 @@ message(std::string_view, Args&&...) -> message;
* @tparam Args Types of arguments to be formatted.
*/
template <class... Args>
using warning = message<message_severity::warning, Args...>;
using log_warning = log_message<log_message_severity::warning, Args...>;
#else
// Disable warning message logging.
template <class... Args>
inline void warning([[maybe_unused]] Args&&...) noexcept {};
inline void log_warning([[maybe_unused]] Args&&...) noexcept {};
#endif
#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_ERROR)
@ -143,11 +141,11 @@ message(std::string_view, Args&&...) -> message;
* @tparam Args Types of arguments to be formatted.
*/
template <class... Args>
using error = message<message_severity::error, Args...>;
using log_error = log_message<log_message_severity::error, Args...>;
#else
// Disable error message logging.
template <class... Args>
inline void error([[maybe_unused]] Args&&...) noexcept {};
inline void log_error([[maybe_unused]] Args&&...) noexcept {};
#endif
#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL)
@ -157,14 +155,15 @@ message(std::string_view, Args&&...) -> message;
* @tparam Args Types of arguments to be formatted.
*/
template <class... Args>
using fatal = message<message_severity::fatal, Args...>;
using log_fatal = log_message<log_message_severity::fatal, Args...>;
#else
// Disable fatal error message logging.
template <class... Args>
inline void fatal([[maybe_unused]] Args&&...) noexcept {};
inline void log_fatal([[maybe_unused]] Args&&...) noexcept {};
#endif
} // namespace log
/// @}
} // namespace debug
#endif // ANTKEEPER_DEBUG_LOG_HPP

src/engine/debug/log/event.hpp → src/engine/debug/log/log-events.hpp View File

@ -17,32 +17,29 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_DEBUG_LOG_EVENT_HPP
#define ANTKEEPER_DEBUG_LOG_EVENT_HPP
#ifndef ANTKEEPER_DEBUG_LOG_EVENTS_HPP
#define ANTKEEPER_DEBUG_LOG_EVENTS_HPP
#include <engine/debug/log/message-severity.hpp>
#include <engine/debug/log/log-message-severity.hpp>
#include <chrono>
#include <source_location>
#include <string>
#include <thread>
namespace debug {
namespace log {
class logger;
/**
* Debug logging events.
*/
namespace event {
/// @name Debug logging
/// @{
/**
* Event generated when a message has been logged.
*/
struct message_logged
struct message_logged_event
{
/// Logger which received the message.
log::logger* logger;
debug::logger* logger;
/// Time at which the message was sent.
std::chrono::time_point<std::chrono::system_clock> time;
@ -54,14 +51,14 @@ struct message_logged
std::source_location location;
/// Severity of the message.
message_severity severity;
log_message_severity severity;
/// Message contents.
std::string message;
};
} // namespace event
} // namespace log
/// @}
} // namespace debug
#endif // ANTKEEPER_DEBUG_LOG_EVENT_HPP
#endif // ANTKEEPER_DEBUG_LOG_EVENTS_HPP

src/engine/debug/log/message-severity.hpp → src/engine/debug/log/log-message-severity.hpp View File

@ -22,6 +22,11 @@
#include <cstdint>
namespace debug {
/// @name Debug logging
/// @{
/// Trace message severity level.
#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE 0
@ -40,11 +45,8 @@
/// Fatal error message severity level.
#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL 5
namespace debug {
namespace log {
/// Log message severity levels.
enum class message_severity: std::uint8_t
enum class log_message_severity: std::uint8_t
{
/// Trace message severity.
trace = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE,
@ -65,7 +67,8 @@ enum class message_severity: std::uint8_t
fatal = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL,
};
} // namespace log
/// @}
} // namespace debug
#endif // ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_HPP

+ 2
- 4
src/engine/debug/log/logger.cpp View File

@ -22,12 +22,11 @@
#include <utility>
namespace debug {
namespace log {
void logger::log(std::string&& message, message_severity severity, std::source_location&& location)
void logger::log(std::string&& message, log_message_severity severity, std::source_location&& location)
{
// Generate message logged event
message_logged_publisher.publish
m_message_logged_publisher.publish
(
{
this,
@ -40,5 +39,4 @@ void logger::log(std::string&& message, message_severity severity, std::source_l
);
}
} // namespace log
} // namespace debug

+ 14
- 11
src/engine/debug/log/logger.hpp View File

@ -17,17 +17,19 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_DEBUG_LOG_LOGGER_HPP
#define ANTKEEPER_DEBUG_LOG_LOGGER_HPP
#ifndef ANTKEEPER_DEBUG_LOGGER_HPP
#define ANTKEEPER_DEBUG_LOGGER_HPP
#include <engine/debug/log/message-severity.hpp>
#include <engine/debug/log/event.hpp>
#include <engine/debug/log/log-message-severity.hpp>
#include <engine/debug/log/log-events.hpp>
#include <engine/event/publisher.hpp>
#include <source_location>
#include <string>
namespace debug {
namespace log {
/// @name Debug logging
/// @{
/**
* Generates an event each time a message logged.
@ -45,21 +47,22 @@ public:
void log
(
std::string&& message,
message_severity severity = message_severity::info,
log_message_severity severity = log_message_severity::info,
std::source_location&& location = std::source_location::current()
);
/// Returns the channel through which message logged events are published.
[[nodiscard]] inline ::event::channel<event::message_logged>& get_message_logged_channel() noexcept
[[nodiscard]] inline ::event::channel<message_logged_event>& get_message_logged_channel() noexcept
{
return message_logged_publisher.channel();
return m_message_logged_publisher.channel();
}
private:
::event::publisher<event::message_logged> message_logged_publisher;
::event::publisher<message_logged_event> m_message_logged_publisher;
};
} // namespace log
/// @}
} // namespace debug
#endif // ANTKEEPER_DEBUG_LOG_LOGGER_HPP
#endif // ANTKEEPER_DEBUG_LOGGER_HPP

+ 32
- 46
src/engine/geom/brep/brep-operations.cpp View File

@ -19,7 +19,7 @@
#include <engine/geom/brep/brep-operations.hpp>
#include <engine/math/vector.hpp>
#include <engine/render/vertex-attribute.hpp>
#include <engine/render/vertex-attribute-location.hpp>
#include <engine/debug/log.hpp>
#include <algorithm>
#include <cmath>
@ -156,40 +156,36 @@ std::unique_ptr generate_model(const brep_mesh& mesh, std::shared
auto& bounds = model->get_bounds();
bounds = {math::fvec3::infinity(), -math::fvec3::infinity()};
// Get model VBO and VAO
auto& vbo = model->get_vertex_buffer();
auto& vao = model->get_vertex_array();
// Build vertex format
std::size_t vertex_size = 0;
gl::vertex_attribute position_attribute;
// Construct model VAO
std::size_t vertex_stride = 0;
std::vector<gl::vertex_input_attribute> vertex_attributes;
gl::vertex_input_attribute position_attribute{};
if (vertex_positions)
{
position_attribute.buffer = vbo.get();
position_attribute.offset = vertex_size;
position_attribute.type = gl::vertex_attribute_type::float_32;
position_attribute.components = 3;
position_attribute.location = render::vertex_attribute_location::position;
position_attribute.binding = 0;
position_attribute.format = gl::format::r32g32b32_sfloat;
position_attribute.offset = 0;
vertex_attributes.emplace_back(position_attribute);
vertex_size += position_attribute.components * sizeof(float);
vertex_stride += 3 * sizeof(float);
}
gl::vertex_attribute normal_attribute;
gl::vertex_input_attribute normal_attribute{};
if (vertex_normals)
{
normal_attribute.buffer = vbo.get();
normal_attribute.offset = vertex_size;
normal_attribute.type = gl::vertex_attribute_type::float_32;
normal_attribute.components = 3;
normal_attribute.location = render::vertex_attribute_location::normal;
normal_attribute.binding = 0;
normal_attribute.format = gl::format::r32g32b32_sfloat;
normal_attribute.offset = static_cast<std::uint32_t>(vertex_stride);
vertex_attributes.emplace_back(normal_attribute);
vertex_size += normal_attribute.components * sizeof(float);
vertex_stride += 3 * sizeof(float);
}
position_attribute.stride = vertex_size;
normal_attribute.stride = vertex_size;
auto& vao = model->get_vertex_array();
vao = std::make_unique<gl::vertex_array>(vertex_attributes);
// Interleave vertex data
std::vector<std::byte> vertex_data(mesh.faces().size() * 3 * vertex_size);
std::vector<std::byte> vertex_data(mesh.faces().size() * 3 * vertex_stride);
if (vertex_positions)
{
std::byte* v = vertex_data.data() + position_attribute.offset;
@ -199,7 +195,7 @@ std::unique_ptr generate_model(const brep_mesh& mesh, std::shared
{
const auto& position = (*vertex_positions)[loop->vertex()->index()];
std::memcpy(v, position.data(), sizeof(float) * 3);
v += position_attribute.stride;
v += vertex_stride;
// Extend model bounds
bounds.extend(position);
@ -215,36 +211,26 @@ std::unique_ptr generate_model(const brep_mesh& mesh, std::shared
{
const auto& normal = (*vertex_normals)[loop->vertex()->index()];
std::memcpy(v, normal.data(), sizeof(float) * 3);
v += normal_attribute.stride;
v += vertex_stride;
}
}
}
// Resize model VBO and upload interleaved vertex data
vbo->resize(vertex_data.size(), vertex_data);
// Free interleaved vertex data
vertex_data.clear();
// Bind vertex attributes to VAO
if (vertex_positions)
{
vao->bind(render::vertex_attribute::position, position_attribute);
}
if (vertex_normals)
{
vao->bind(render::vertex_attribute::normal, normal_attribute);
}
// Construct model VBO
auto& vbo = model->get_vertex_buffer();
vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data);
model->set_vertex_offset(0);
model->set_vertex_stride(vertex_stride);
// Create material group
model->get_groups().resize(1);
render::model_group& model_group = model->get_groups().front();
model_group.id = "default";
model_group.id = {};
model_group.material = material;
model_group.drawing_mode = gl::drawing_mode::triangles;
model_group.start_index = 0;
model_group.index_count = static_cast<std::uint32_t>(mesh.faces().size() * 3);
model_group.primitive_topology = gl::primitive_topology::triangle_list;
model_group.first_vertex = 0;
model_group.vertex_count = static_cast<std::uint32_t>(mesh.faces().size() * 3);
return model;
}

+ 53
- 0
src/engine/gl/blend-factor.hpp View File

@ -0,0 +1,53 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_BLEND_FACTOR_HPP
#define ANTKEEPER_GL_BLEND_FACTOR_HPP
#include <cstdint>
namespace gl {
/// Source and destination color and alpha blending factors.
enum class blend_factor: std::uint8_t
{
zero,
one,
src_color,
one_minus_src_color,
dst_color,
one_minus_dst_color,
src_alpha,
one_minus_src_alpha,
dst_alpha,
one_minus_dst_alpha,
constant_color,
one_minus_constant_color,
constant_alpha,
one_minus_constant_alpha,
src_alpha_saturate,
src1_color,
one_minus_src1_color,
src1_alpha,
one_minus_src1_alpha
};
} // namespace gl
#endif // ANTKEEPER_GL_BLEND_FACTOR_HPP

src/engine/gl/texture-wrapping.hpp → src/engine/gl/blend-op.hpp View File

@ -17,21 +17,23 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_WRAPPING_HPP
#define ANTKEEPER_GL_TEXTURE_WRAPPING_HPP
#ifndef ANTKEEPER_GL_BLEND_OP_HPP
#define ANTKEEPER_GL_BLEND_OP_HPP
#include <cstdint>
namespace gl {
enum class texture_wrapping: std::uint8_t
/// Framebuffer blending operations.
enum class blend_op: std::uint8_t
{
clip,
extend,
repeat,
mirrored_repeat
add,
subtract,
reverse_subtract,
min,
max,
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_WRAPPING_HPP
#endif // ANTKEEPER_GL_BLEND_OP_HPP

+ 42
- 0
src/engine/gl/clear-bits.hpp View File

@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_CLEAR_BITS_HPP
#define ANTKEEPER_GL_CLEAR_BITS_HPP
#include <cstdint>
namespace gl {
/// Clear mask bits.
enum: std::uint8_t
{
/// Indicates the color buffer should be cleared.
color_clear_bit = 0b001,
/// Indicates the depth buffer should be cleared.
depth_clear_bit = 0b010,
/// Indicates the stencil buffer should be cleared.
stencil_clear_bit = 0b100,
};
} // namespace gl
#endif // ANTKEEPER_GL_CLEAR_BITS_HPP

+ 46
- 0
src/engine/gl/clear-value.hpp View File

@ -0,0 +1,46 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_CLEAR_VALUE_HPP
#define ANTKEEPER_GL_CLEAR_VALUE_HPP
#include <array>
#include <cstdint>
namespace gl {
/**
* Clear value.
*/
struct clear_value
{
/// Color clear values.
std::array<float, 4> color{};
/// Depth clear value.
float depth{};
/// Stencil clear value.
std::uint32_t stencil{};
};
} // namespace gl
#endif // ANTKEEPER_GL_CLEAR_VALUE_HPP

+ 52
- 0
src/engine/gl/color-blend-equation.hpp View File

@ -0,0 +1,52 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_COLOR_BLEND_EQUATION_HPP
#define ANTKEEPER_GL_COLOR_BLEND_EQUATION_HPP
#include <engine/gl/blend-factor.hpp>
#include <engine/gl/blend-op.hpp>
namespace gl {
/// Color blend factors and operations.
struct color_blend_equation
{
/// Selects which blend factor is used to determine the RGB source factors.
blend_factor src_color_blend_factor;
/// Selects which blend factor is used to determine the RGB destination factors.
blend_factor dst_color_blend_factor;
/// Selects which blend operation is used to calculate the RGB values to write to the color attachment.
blend_op color_blend_op;
/// Selects which blend factor is used to determine the alpha source factor.
blend_factor src_alpha_blend_factor;
/// Selects which blend factor is used to determine the alpha destination factor.
blend_factor dst_alpha_blend_factor;
/// Selects which blend operation is used to calculate the alpha values to write to the color attachment.
blend_op alpha_blend_op;
};
} // namespace gl
#endif // ANTKEEPER_GL_COLOR_BLEND_EQUATION_HPP

+ 45
- 0
src/engine/gl/color-component-bits.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_COLOR_COMPONENT_BITS_HPP
#define ANTKEEPER_GL_COLOR_COMPONENT_BITS_HPP
#include <cstdint>
namespace gl {
/// Bits controlling which components are written to the framebuffer.
enum: std::uint8_t
{
/// Indicates that the R value is written to the color attachment for the appropriate sample.
color_component_r_bit = 0b0001,
/// Indicates that the G value is written to the color attachment for the appropriate sample.
color_component_g_bit = 0b0010,
/// Indicates that the B value is written to the color attachment for the appropriate sample.
color_component_b_bit = 0b0100,
/// Indicates that the A value is written to the color attachment for the appropriate sample.
color_component_a_bit = 0b1000,
};
} // namespace gl
#endif // ANTKEEPER_GL_COLOR_COMPONENT_BITS_HPP

+ 57
- 0
src/engine/gl/compare-op.hpp View File

@ -0,0 +1,57 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_COMPARE_OP_HPP
#define ANTKEEPER_GL_COMPARE_OP_HPP
#include <cstdint>
namespace gl {
/// Comparison operators.
enum class compare_op: std::uint8_t
{
/// Comparison always evaluates `false`.
never,
/// Comparison evaluates `reference` < `test`.
less,
/// Comparison evaluates `reference` == `test`.
equal,
/// Comparison evaluates `reference` <= `test`.
less_or_equal,
/// Comparison evaluates `reference` > `test`.
greater,
/// Comparison evaluates `reference` != `test`.
not_equal,
/// Comparison evaluates `reference` >= `test`.
greater_or_equal,
/// Comparison always evaluates `true`.
always
};
} // namespace gl
#endif // ANTKEEPER_GL_COMPARE_OP_HPP

+ 77
- 0
src/engine/gl/cube-map.cpp View File

@ -0,0 +1,77 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/cube-map.hpp>
namespace gl {
cube_map_layout infer_cube_map_layout(std::uint32_t width, std::uint32_t height) noexcept
{
if (width == height / 6)
{
return cube_map_layout::column;
}
else if (width == height * 6)
{
return cube_map_layout::row;
}
else if (width == (height / 4) * 3)
{
return cube_map_layout::vertical_cross;
}
else if (width == (height * 4) / 3)
{
return cube_map_layout::horizontal_cross;
}
else if (width == height * 2)
{
return cube_map_layout::equirectangular;
}
else if (width == height)
{
return cube_map_layout::spherical;
}
return cube_map_layout::unknown;
}
std::uint32_t infer_cube_map_face_width(std::uint32_t width, std::uint32_t height, cube_map_layout layout) noexcept
{
switch (layout)
{
case cube_map_layout::column:
case cube_map_layout::spherical:
return width;
case cube_map_layout::row:
return height;
case cube_map_layout::vertical_cross:
return height / 4;
case cube_map_layout::horizontal_cross:
case cube_map_layout::equirectangular:
return width / 4;
default:
return 0;
}
}
} // namespace gl

+ 75
- 0
src/engine/gl/cube-map.hpp View File

@ -0,0 +1,75 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_CUBE_MAP_HPP
#define ANTKEEPER_GL_CUBE_MAP_HPP
#include <cstdint>
namespace gl {
/// Cube map layouts.
enum class cube_map_layout: std::uint8_t
{
/// Unknown layout.
unknown,
/// Faces are stored consecutively in a single column.
column,
/// Faces are stored consecutively in a single 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
};
/**
* Infers the layout of a cube map from its dimensions.
*
* @param width Cube map width.
* @param height Cube map height.
*
* @return Inferred cube map layout.
*/
[[nodiscard]] cube_map_layout infer_cube_map_layout(std::uint32_t width, std::uint32_t height) noexcept;
/**
* Infers the width of a cube map face from its dimensons and layout.
*
* @param width Cube map width.
* @param height Cube map height.
* @param layout Cube map layout.
*
* @return Inferred cube map face width.
*/
[[nodiscard]] std::uint32_t infer_cube_map_face_width(std::uint32_t width, std::uint32_t height, cube_map_layout layout) noexcept;
} // namespace gl
#endif // ANTKEEPER_GL_CUBE_MAP_HPP

+ 45
- 0
src/engine/gl/cull-mode.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_CULL_MODE_HPP
#define ANTKEEPER_GL_CULL_MODE_HPP
#include <cstdint>
namespace gl {
/// Triangle culling mode.
enum class cull_mode: std::uint8_t
{
/// No triangles are discarded.
none,
/// Front-facing triangles are discarded.
front,
/// Back-facing triangles are discarded.
back,
/// All triangles are discarded.
front_and_back
};
} // namespace gl
#endif // ANTKEEPER_GL_CULL_MODE_HPP

+ 42
- 0
src/engine/gl/fill-mode.hpp View File

@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_FILL_MODE_HPP
#define ANTKEEPER_GL_FILL_MODE_HPP
#include <cstdint>
namespace gl {
/// Polygon rasterization mode.
enum class fill_mode: std::uint8_t
{
/// Polygons are filled.
fill,
/// Polygons edges are drawn as line segments.
line,
/// Polygons vertices are drawn as points.
point
};
} // namespace gl
#endif // ANTKEEPER_GL_FILL_MODE_HPP

+ 219
- 0
src/engine/gl/format.hpp View File

@ -0,0 +1,219 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_FORMAT_HPP
#define ANTKEEPER_GL_FORMAT_HPP
#include <cstdint>
namespace gl {
/// Image and vertex formats.
enum class format: std::uint32_t
{
undefined,
r4g4_unorm_pack8,
r4g4b4a4_unorm_pack16,
b4g4r4a4_unorm_pack16,
r5g6b5_unorm_pack16,
b5g6r5_unorm_pack16,
r5g5b5a1_unorm_pack16,
b5g5r5a1_unorm_pack16,
a1r5g5b5_unorm_pack16,
r8_unorm,
r8_snorm,
r8_uscaled,
r8_sscaled,
r8_uint,
r8_sint,
r8_srgb,
r8g8_unorm,
r8g8_snorm,
r8g8_uscaled,
r8g8_sscaled,
r8g8_uint,
r8g8_sint,
r8g8_srgb,
r8g8b8_unorm,
r8g8b8_snorm,
r8g8b8_uscaled,
r8g8b8_sscaled,
r8g8b8_uint,
r8g8b8_sint,
r8g8b8_srgb,
b8g8r8_unorm,
b8g8r8_snorm,
b8g8r8_uscaled,
b8g8r8_sscaled,
b8g8r8_uint,
b8g8r8_sint,
b8g8r8_srgb,
r8g8b8a8_unorm,
r8g8b8a8_snorm,
r8g8b8a8_uscaled,
r8g8b8a8_sscaled,
r8g8b8a8_uint,
r8g8b8a8_sint,
r8g8b8a8_srgb,
b8g8r8a8_unorm,
b8g8r8a8_snorm,
b8g8r8a8_uscaled,
b8g8r8a8_sscaled,
b8g8r8a8_uint,
b8g8r8a8_sint,
b8g8r8a8_srgb,
a8b8g8r8_unorm_pack32,
a8b8g8r8_snorm_pack32,
a8b8g8r8_uscaled_pack32,
a8b8g8r8_sscaled_pack32,
a8b8g8r8_uint_pack32,
a8b8g8r8_sint_pack32,
a8b8g8r8_srgb_pack32,
a2r10g10b10_unorm_pack32,
a2r10g10b10_snorm_pack32,
a2r10g10b10_uscaled_pack32,
a2r10g10b10_sscaled_pack32,
a2r10g10b10_uint_pack32,
a2r10g10b10_sint_pack32,
a2b10g10r10_unorm_pack32,
a2b10g10r10_snorm_pack32,
a2b10g10r10_uscaled_pack32,
a2b10g10r10_sscaled_pack32,
a2b10g10r10_uint_pack32,
a2b10g10r10_sint_pack32,
r16_unorm,
r16_snorm,
r16_uscaled,
r16_sscaled,
r16_uint,
r16_sint,
r16_sfloat,
r16g16_unorm,
r16g16_snorm,
r16g16_uscaled,
r16g16_sscaled,
r16g16_uint,
r16g16_sint,
r16g16_sfloat,
r16g16b16_unorm,
r16g16b16_snorm,
r16g16b16_uscaled,
r16g16b16_sscaled,
r16g16b16_uint,
r16g16b16_sint,
r16g16b16_sfloat,
r16g16b16a16_unorm,
r16g16b16a16_snorm,
r16g16b16a16_uscaled,
r16g16b16a16_sscaled,
r16g16b16a16_uint,
r16g16b16a16_sint,
r16g16b16a16_sfloat,
r32_uint,
r32_sint,
r32_sfloat,
r32g32_uint,
r32g32_sint,
r32g32_sfloat,
r32g32b32_uint,
r32g32b32_sint,
r32g32b32_sfloat,
r32g32b32a32_uint,
r32g32b32a32_sint,
r32g32b32a32_sfloat,
r64_uint,
r64_sint,
r64_sfloat,
r64g64_uint,
r64g64_sint,
r64g64_sfloat,
r64g64b64_uint,
r64g64b64_sint,
r64g64b64_sfloat,
r64g64b64a64_uint,
r64g64b64a64_sint,
r64g64b64a64_sfloat,
b10g11r11_ufloat_pack32,
e5b9g9r9_ufloat_pack32,
d16_unorm,
x8_d24_unorm_pack32,
d32_sfloat,
s8_uint,
d16_unorm_s8_uint,
d24_unorm_s8_uint,
d32_sfloat_s8_uint,
bc1_rgb_unorm_block,
bc1_rgb_srgb_block,
bc1_rgba_unorm_block,
bc1_rgba_srgb_block,
bc2_unorm_block,
bc2_srgb_block,
bc3_unorm_block,
bc3_srgb_block,
bc4_unorm_block,
bc4_snorm_block,
bc5_unorm_block,
bc5_snorm_block,
bc6h_ufloat_block,
bc6h_sfloat_block,
bc7_unorm_block,
bc7_srgb_block,
etc2_r8g8b8_unorm_block,
etc2_r8g8b8_srgb_block,
etc2_r8g8b8a1_unorm_block,
etc2_r8g8b8a1_srgb_block,
etc2_r8g8b8a8_unorm_block,
etc2_r8g8b8a8_srgb_block,
eac_r11_unorm_block,
eac_r11_snorm_block,
eac_r11g11_unorm_block,
eac_r11g11_snorm_block,
astc_4x4_unorm_block,
astc_4x4_srgb_block,
astc_5x4_unorm_block,
astc_5x4_srgb_block,
astc_5x5_unorm_block,
astc_5x5_srgb_block,
astc_6x5_unorm_block,
astc_6x5_srgb_block,
astc_6x6_unorm_block,
astc_6x6_srgb_block,
astc_8x5_unorm_block,
astc_8x5_srgb_block,
astc_8x6_unorm_block,
astc_8x6_srgb_block,
astc_8x8_unorm_block,
astc_8x8_srgb_block,
astc_10x5_unorm_block,
astc_10x5_srgb_block,
astc_10x6_unorm_block,
astc_10x6_srgb_block,
astc_10x8_unorm_block,
astc_10x8_srgb_block,
astc_10x10_unorm_block,
astc_10x10_srgb_block,
astc_12x10_unorm_block,
astc_12x10_srgb_block,
astc_12x12_unorm_block,
astc_12x12_srgb_block
};
} // namespace gl
#endif // ANTKEEPER_GL_FORMAT_HPP

+ 44
- 0
src/engine/gl/framebuffer-attachment.hpp View File

@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_FRAMEBUFFER_ATTACHMENT_HPP
#define ANTKEEPER_GL_FRAMEBUFFER_ATTACHMENT_HPP
#include <engine/gl/image-view.hpp>
#include <cstdint>
#include <memory>
namespace gl {
/// Framebuffer attachment.
struct framebuffer_attachment
{
/// Attachment usage bit mask.
std::uint8_t usage_mask{};
/// Attached image view.
std::shared_ptr<gl::image_view> image_view;
/// Mip level of attached image view.
std::uint32_t level{};
};
} // namespace gl
#endif // ANTKEEPER_GL_FRAMEBUFFER_ATTACHMENT_HPP

+ 45
- 0
src/engine/gl/framebuffer-usage-bits.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_FRAMEBUFFER_USAGE_BITS_HPP
#define ANTKEEPER_GL_FRAMEBUFFER_USAGE_BITS_HPP
#include <cstdint>
namespace gl {
/// Framebuffer attachment usage bits.
enum: std::uint8_t
{
/// Framebuffer color attachment.
color_attachment_bit = 0b001,
/// Framebuffer depth attachment.
depth_attachment_bit = 0b010,
/// Framebuffer stencil attachment.
stencil_attachment_bit = 0b100,
/// Framebuffer depth/stencil attachment.
depth_stencil_attachment_bits = 0b110,
};
} // namespace gl
#endif // ANTKEEPER_GL_FRAMEBUFFER_USAGE_BITS_HPP

+ 85
- 50
src/engine/gl/framebuffer.cpp View File

@ -18,73 +18,108 @@
*/
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/texture.hpp>
#include <glad/glad.h>
#include <glad/gl.h>
#include <stdexcept>
namespace gl {
static constexpr GLenum attachment_lut[] =
framebuffer::framebuffer(std::span<const framebuffer_attachment> attachments, std::uint32_t width, std::uint32_t height)
{
GL_COLOR_ATTACHMENT0,
GL_DEPTH_ATTACHMENT,
GL_STENCIL_ATTACHMENT
};
framebuffer::framebuffer(int width, int height):
m_dimensions{width, height}
{
glGenFramebuffers(1, &m_gl_framebuffer_id);
}
framebuffer::framebuffer():
framebuffer(0, 0)
{}
framebuffer::~framebuffer()
{
if (m_gl_framebuffer_id)
{
glDeleteFramebuffers(1, &m_gl_framebuffer_id);
}
}
void framebuffer::resize(const std::array<int, 2>& dimensions)
{
m_dimensions = dimensions;
}
void framebuffer::attach(framebuffer_attachment_type attachment_type, texture* texture, std::uint8_t level)
{
glBindFramebuffer(GL_FRAMEBUFFER, m_gl_framebuffer_id);
m_dimensions = {width, height};
m_attachments.assign(attachments.begin(), attachments.end());
// Generate framebuffer
glCreateFramebuffers(1, &m_gl_named_framebuffer);
GLenum gl_attachment = attachment_lut[static_cast<std::size_t>(attachment_type)];
glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, texture->m_gl_texture_id, level);
GLenum gl_color_attachment = GL_COLOR_ATTACHMENT0;
std::vector<GLenum> gl_draw_buffers;
if (attachment_type == framebuffer_attachment_type::color)
// Attach textures to framebuffer
for (const auto& attachment: m_attachments)
{
m_color_attachment = texture;
if (attachment.usage_mask & gl::color_attachment_bit)
{
glNamedFramebufferTexture
(
m_gl_named_framebuffer,
gl_color_attachment,
attachment.image_view->m_gl_texture_name,
static_cast<GLuint>(attachment.level)
);
gl_draw_buffers.emplace_back(gl_color_attachment);
++gl_color_attachment;
}
if (attachment.usage_mask & gl::depth_attachment_bit)
{
if (attachment.usage_mask & gl::stencil_attachment_bit)
{
glNamedFramebufferTexture
(
m_gl_named_framebuffer,
GL_DEPTH_STENCIL_ATTACHMENT,
attachment.image_view->m_gl_texture_name,
static_cast<GLuint>(attachment.level)
);
}
else
{
glNamedFramebufferTexture
(
m_gl_named_framebuffer,
GL_DEPTH_ATTACHMENT,
attachment.image_view->m_gl_texture_name,
static_cast<GLuint>(attachment.level)
);
}
}
else if (attachment.usage_mask & gl::stencil_attachment_bit)
{
glNamedFramebufferTexture
(
m_gl_named_framebuffer,
GL_STENCIL_ATTACHMENT,
attachment.image_view->m_gl_texture_name,
static_cast<GLuint>(attachment.level)
);
}
}
else if (attachment_type == framebuffer_attachment_type::depth)
// Specify read and draw buffers
if (!gl_draw_buffers.empty())
{
m_depth_attachment = texture;
glNamedFramebufferReadBuffer(m_gl_named_framebuffer, GL_COLOR_ATTACHMENT0);
glNamedFramebufferDrawBuffers
(
m_gl_named_framebuffer,
static_cast<GLsizei>(gl_draw_buffers.size()),
gl_draw_buffers.data()
);
}
else if (attachment_type == framebuffer_attachment_type::stencil)
else
{
m_stencil_attachment = texture;
glNamedFramebufferReadBuffer(m_gl_named_framebuffer, GL_NONE);
glNamedFramebufferDrawBuffer(m_gl_named_framebuffer, GL_NONE);
}
if (!m_color_attachment)
if (glCheckNamedFramebufferStatus(m_gl_named_framebuffer, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
throw std::runtime_error("OpenGL framebuffer incomplete.");
}
else
}
framebuffer::~framebuffer()
{
if (m_gl_named_framebuffer)
{
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDeleteFramebuffers(1, &m_gl_named_framebuffer);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void framebuffer::resize(std::uint32_t width, std::uint32_t height)
{
m_dimensions = {width, height};
}
} // namespace gl

+ 32
- 59
src/engine/gl/framebuffer.hpp View File

@ -20,100 +20,73 @@
#ifndef ANTKEEPER_GL_FRAMEBUFFER_HPP
#define ANTKEEPER_GL_FRAMEBUFFER_HPP
#include <engine/gl/framebuffer-attachment.hpp>
#include <engine/gl/framebuffer-usage-bits.hpp>
#include <array>
#include <cstdint>
#include <span>
#include <vector>
namespace gl {
class rasterizer;
class texture;
enum class framebuffer_attachment_type: std::uint8_t
{
color,
depth,
stencil
};
/**
*
*/
class framebuffer
{
public:
/**
* Creates a framebuffer.
* Constructs a framebuffer.
*
* @param attachments Framebuffer attachments.
* @param width Width of the framebuffer.
* @param height Height of the framebuffer.
*/
framebuffer(int width, int height);
framebuffer();
framebuffer(std::span<const framebuffer_attachment> attachments, std::uint32_t width, std::uint32_t height);
/// Destroys a framebuffer.
~framebuffer();
/**
* Resizes the framebuffer. Note: This does not resize any attached textures.
* Resizes the framebuffer.
*
* @param width New width of the framebuffer.
* @param height New height of the framebuffer.
*/
void resize(const std::array<int, 2>& dimensions);
/**
* Attaches a color, depth, or stencil texture to the framebuffer.
*
* @param attachment_type Type of attachment.
* @param texture Texture to attach.
* @param level Mip level of the texture to attach.
* @warning Does not resize framebuffer attachments.
*/
void attach(framebuffer_attachment_type attachment_type, texture* texture, std::uint8_t level = 0);
void resize(std::uint32_t width, std::uint32_t height);
/// Returns the dimensions of the framebuffer, in pixels.
[[nodiscard]] inline const std::array<int, 2>& get_dimensions() const noexcept
/// Returns the framebuffer attachments.
[[nodiscard]] inline constexpr const std::vector<framebuffer_attachment>& attachments() const noexcept
{
return m_dimensions;
return m_attachments;
}
/// Returns the attached color texture, if any.
/// @{
[[nodiscard]] inline const texture* get_color_attachment() const noexcept
/// Returns the dimensions of the framebuffer.
[[nodiscard]] inline constexpr const std::array<std::uint32_t, 2>& dimensions() const noexcept
{
return m_color_attachment;
}
[[nodiscard]] inline texture* get_color_attachment() noexcept
{
return m_color_attachment;
return m_dimensions;
}
/// @}
/// Returns the attached depth texture, if any.
/// @{
[[nodiscard]] inline const texture* get_depth_attachment() const noexcept
/// Returns the width of the framebuffer.
[[nodiscard]] inline constexpr std::uint32_t width() const noexcept
{
return m_depth_attachment;
return m_dimensions[0];
}
[[nodiscard]] inline texture* get_depth_attachment() noexcept
{
return m_depth_attachment;
}
/// @}
/// Returns the attached stencil texture, if any.
/// @{
[[nodiscard]] inline const texture* get_stencil_attachment() const noexcept
{
return m_stencil_attachment;
}
[[nodiscard]] inline texture* get_stencil_attachment() noexcept
/// Returns the height of the framebuffer.
[[nodiscard]] inline constexpr std::uint32_t height() const noexcept
{
return m_stencil_attachment;
return m_dimensions[1];
}
/// @}
private:
friend class rasterizer;
friend class pipeline;
unsigned int m_gl_framebuffer_id{0};
std::array<int, 2> m_dimensions{0, 0};
texture* m_color_attachment{nullptr};
texture* m_depth_attachment{nullptr};
texture* m_stencil_attachment{nullptr};
std::vector<framebuffer_attachment> m_attachments;
std::array<std::uint32_t, 2> m_dimensions{0, 0};
unsigned int m_gl_named_framebuffer{0};
};
} // namespace gl

src/engine/gl/drawing-mode.hpp → src/engine/gl/front-face.hpp View File

@ -17,28 +17,23 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_DRAWING_MODE_HPP
#define ANTKEEPER_GL_DRAWING_MODE_HPP
#ifndef ANTKEEPER_GL_FRONT_FACE_HPP
#define ANTKEEPER_GL_FRONT_FACE_HPP
#include <cstdint>
namespace gl {
enum class drawing_mode: std::uint8_t
/// Polygon front-facing orientation.
enum class front_face: std::uint8_t
{
points,
line_strip,
line_loop,
lines,
line_strip_adjacency,
lines_adjacency,
triangle_strip,
triangle_fan,
triangles,
triangle_strip_adjacency,
triangles_adjacency
/// Triangle with positive area is considered front-facing.
counter_clockwise,
/// Triangle with negative area is considered front-facing.
clockwise
};
} // namespace gl
#endif // ANTKEEPER_GL_DRAWING_MODE_HPP
#endif // ANTKEEPER_GL_FRONT_FACE_HPP

src/engine/gl/element-array-type.hpp → src/engine/gl/image-flag.hpp View File

@ -17,20 +17,20 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP
#define ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP
#ifndef ANTKEEPER_GL_IMAGE_FLAG_HPP
#define ANTKEEPER_GL_IMAGE_FLAG_HPP
#include <cstdint>
namespace gl {
enum class element_array_type: std::uint8_t
/// Image flags.
enum class image_flag: std::uint8_t
{
uint_8,
uint_16,
uint_32
/// Cube map view compatible image.
cube_compatible = 0b00000001
};
} // namespace gl
#endif // ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP
#endif // ANTKEEPER_GL_IMAGE_FLAG_HPP

src/engine/gl/transfer-function.hpp → src/engine/gl/image-view-flag.hpp View File

@ -17,25 +17,23 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TRANSFER_FUNCTION_HPP
#define ANTKEEPER_GL_TRANSFER_FUNCTION_HPP
#ifndef ANTKEEPER_GL_IMAGE_VIEW_FLAG_HPP
#define ANTKEEPER_GL_IMAGE_VIEW_FLAG_HPP
#include <cstdint>
namespace gl {
/**
* Texture sampling transfer function.
*/
enum class transfer_function: std::uint8_t
/// Image flags.
enum class image_view_flag: std::uint8_t
{
/// Linear transfer function.
linear,
/// Image array view.
array = 0b00000001,
/// sRGB transfer function.
srgb
/// Cube map view.
cube = 0b00000010
};
} // namespace gl
#endif // ANTKEEPER_GL_TRANSFER_FUNCTION_HPP
#endif // ANTKEEPER_GL_IMAGE_VIEW_FLAG_HPP

+ 298
- 0
src/engine/gl/image-view.cpp View File

@ -0,0 +1,298 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/image-view.hpp>
#include <engine/gl/opengl/gl-format-lut.hpp>
#include <stdexcept>
#include <glad/gl.h>
namespace gl {
image_view::image_view
(
std::shared_ptr<gl::image> image,
std::uint8_t dimensionality,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer,
std::uint32_t array_layer_count,
std::uint8_t flags
)
{
if (!image)
{
throw std::invalid_argument("Image view has null image.");
}
if (format == gl::format::undefined)
{
format = image->get_format();
}
const auto format_index = std::to_underlying(format);
const auto gl_internal_format = gl_format_lut[format_index][0];
if (!gl_internal_format)
{
throw std::invalid_argument("Image view has unsupported format.");
}
if (!mip_level_count)
{
throw std::invalid_argument("Image view has zero mip levels.");
}
if (first_mip_level + mip_level_count > image->get_mip_levels())
{
throw std::out_of_range("Image view mip range out of image mip range.");
}
if (!array_layer_count)
{
throw std::invalid_argument("Image view has zero array layers.");
}
if (first_array_layer + array_layer_count > image->get_array_layers())
{
throw std::out_of_range("Image view array layer range out of image array layer range.");
}
if (dimensionality != image->get_dimensionality())
{
throw std::invalid_argument("Image view dimensionality must match image dimensionality.");
}
if (flags & std::to_underlying(image_view_flag::cube))
{
if (!image->is_cube_compatible())
{
throw std::invalid_argument("Cube image views must be constructed from cube-compatible images.");
}
if (array_layer_count % 6 != 0)
{
throw std::invalid_argument("Cube image views array layer count must be a multiple of 6.");
}
}
m_image = image;
m_dimensionality = dimensionality;
m_format = format;
m_first_mip_level = first_mip_level;
m_mip_level_count = mip_level_count;
m_first_array_layer = first_array_layer;
m_array_layer_count = array_layer_count;
m_flags = flags;
unsigned int gl_target = 0;
switch (dimensionality)
{
case 1:
gl_target = is_array() ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D;
break;
case 2:
if (is_cube())
{
gl_target = is_array() ? GL_TEXTURE_CUBE_MAP_ARRAY : GL_TEXTURE_CUBE_MAP;
}
else
{
gl_target = is_array() ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
}
break;
case 3:
gl_target = GL_TEXTURE_3D;
break;
default:
break;
}
glGenTextures(1, &m_gl_texture_name);
glTextureView
(
m_gl_texture_name,
gl_target,
m_image->m_gl_texture_name,
gl_internal_format,
m_first_mip_level,
m_mip_level_count,
m_first_array_layer,
m_array_layer_count
);
}
image_view::~image_view()
{
glDeleteTextures(1, &m_gl_texture_name);
}
image_view_1d::image_view_1d
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer
):
image_view
(
image,
1,
format,
first_mip_level,
mip_level_count,
first_array_layer,
1,
0
)
{}
image_view_1d_array::image_view_1d_array
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer,
std::uint32_t array_layer_count
):
image_view
(
image,
1,
format,
first_mip_level,
mip_level_count,
first_array_layer,
array_layer_count,
std::to_underlying(image_view_flag::array)
)
{}
image_view_2d::image_view_2d
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer
):
image_view
(
image,
2,
format,
first_mip_level,
mip_level_count,
first_array_layer,
1,
0
)
{}
image_view_2d_array::image_view_2d_array
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer,
std::uint32_t array_layer_count
):
image_view
(
image,
2,
format,
first_mip_level,
mip_level_count,
first_array_layer,
array_layer_count,
std::to_underlying(image_view_flag::array)
)
{}
image_view_3d::image_view_3d
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count
):
image_view
(
image,
3,
format,
first_mip_level,
mip_level_count,
0,
1,
0
)
{}
image_view_cube::image_view_cube
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer
):
image_view
(
image,
2,
format,
first_mip_level,
mip_level_count,
first_array_layer,
6,
std::to_underlying(image_view_flag::cube)
)
{}
image_view_cube_array::image_view_cube_array
(
std::shared_ptr<gl::image> image,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer,
std::uint32_t array_layer_count
):
image_view
(
image,
2,
format,
first_mip_level,
mip_level_count,
first_array_layer,
array_layer_count,
std::to_underlying(image_view_flag::array) | std::to_underlying(image_view_flag::cube)
)
{}
} // namespace gl

+ 292
- 0
src/engine/gl/image-view.hpp View File

@ -0,0 +1,292 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_IMAGE_VIEW_HPP
#define ANTKEEPER_GL_IMAGE_VIEW_HPP
#include <engine/gl/image.hpp>
#include <engine/gl/image-view-flag.hpp>
#include <cstdint>
#include <memory>
namespace gl {
/**
* Image view.
*/
class image_view
{
public:
/// Destructs an image view.
virtual ~image_view() = 0;
image_view(const image_view&) = delete;
image_view(image_view&&) = delete;
image_view& operator=(const image_view&) = delete;
image_view& operator=(image_view&&) = delete;
/// Returns the image on which the view was created.
[[nodiscard]] inline constexpr const std::shared_ptr<image>& get_image() const noexcept
{
return m_image;
}
/// Returns the format and type used to interpret texel blocks of the image.
[[nodiscard]] inline constexpr format get_format() const noexcept
{
return m_format;
}
/// Returns the first mipmap level accessible to the view.
[[nodiscard]] inline constexpr std::uint32_t get_first_mip_level() const noexcept
{
return m_first_mip_level;
}
/// Returns the number of mipmap levels accessible to the view.
[[nodiscard]] inline constexpr std::uint32_t get_mip_level_count() const noexcept
{
return m_mip_level_count;
}
/// Returns the first array layer accessible to the view.
[[nodiscard]] inline constexpr std::uint32_t get_first_array_layer() const noexcept
{
return m_first_array_layer;
}
/// Returns the number of array layers accessible to the view.
[[nodiscard]] inline constexpr std::uint32_t get_array_layer_count() const noexcept
{
return m_array_layer_count;
}
/// Returns the dimensionality of the image view.
[[nodiscard]] inline constexpr std::uint8_t get_dimensionality() const noexcept
{
return m_dimensionality;
}
/// Returns `true` if the image view is 1D, `false` otherwise.
[[nodiscard]] inline constexpr bool is_1d() const noexcept
{
return m_dimensionality == 1;
}
/// Returns `true` if the image view is 2D, `false` otherwise.
[[nodiscard]] inline constexpr bool is_2d() const noexcept
{
return m_dimensionality == 2;
}
/// Returns `true` if the image view is 3D, `false` otherwise.
[[nodiscard]] inline constexpr bool is_3d() const noexcept
{
return m_dimensionality == 3;
}
/// Returns `true` if the image view is an array view, `false` otherwise.
[[nodiscard]] inline constexpr bool is_array() const noexcept
{
return m_flags & std::to_underlying(image_view_flag::array);
}
/// Returns `true` if the image view is a cube map view, `false` otherwise.
[[nodiscard]] inline constexpr bool is_cube() const noexcept
{
return m_flags & std::to_underlying(image_view_flag::cube);
}
protected:
/**
* Constructs an image view from an image.
*
* @param image Image on which the view will be created.
* @param dimensionality Image view dimensionality, on `[1, 3]`.
* @param format Format and type used to interpret texel blocks of the image. If gl::format::undefined, the format will be set to the format of the image.
* @param first_mip_level First mipmap level accessible to the view.
* @param mip_level_count Number of mipmap levels accessible to the view.
* @param first_array_layer First array layer accessible to the view.
* @param array_layer Number of array layers accessible to the view.
* @param flags Image view flags.
*
* @except std::invalid_argument Image view has null image.
* @except std::invalid_argument Image view has unsupported format.
* @except std::invalid_argument Image view has zero mip levels.
* @except std::out_of_range Image view mip range out of image mip range.
* @except std::invalid_argument Image view has zero array layers.
* @except std::out_of_range Image view array layer range out of image array layer range.
* @except std::invalid_argument Image view dimensionality must match image dimensionality.
* @except std::invalid_argument Cube image views must be constructed from cube-compatible images.
* @except std::invalid_argument Cube image views array layer count must be a multiple of 6.
*/
image_view
(
std::shared_ptr<gl::image> image,
std::uint8_t dimensionality,
gl::format format,
std::uint32_t first_mip_level,
std::uint32_t mip_level_count,
std::uint32_t first_array_layer,
std::uint32_t array_layer_count,
std::uint8_t flags
);
private:
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 m_gl_texture_name{0};
std::shared_ptr<image> m_image;
std::uint8_t m_dimensionality{0};
format m_format{format::undefined};
std::uint32_t m_first_mip_level{0};
std::uint32_t m_mip_level_count{0};
std::uint32_t m_first_array_layer{0};
std::uint32_t m_array_layer_count{0};
std::uint8_t m_flags{0};
};
/**
* 1D image view.
*/
class image_view_1d: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_1d
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1,
std::uint32_t first_array_layer = 0
);
};
/**
* 1D image array view.
*/
class image_view_1d_array: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_1d_array
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1,
std::uint32_t first_array_layer = 0,
std::uint32_t array_layer_count = 1
);
};
/**
* 2D image view.
*/
class image_view_2d: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_2d
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1,
std::uint32_t first_array_layer = 0
);
};
/**
* 2D image array view.
*/
class image_view_2d_array: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_2d_array
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1,
std::uint32_t first_array_layer = 0,
std::uint32_t array_layer_count = 1
);
};
/**
* 3D image view.
*/
class image_view_3d: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_3d
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1
);
};
/**
* Cube image view.
*/
class image_view_cube: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_cube
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1,
std::uint32_t first_array_layer = 0
);
};
/**
* Cube image array view.
*/
class image_view_cube_array: public image_view
{
public:
/// @copydoc image_view::image_view
image_view_cube_array
(
std::shared_ptr<gl::image> image,
gl::format format = gl::format::undefined,
std::uint32_t first_mip_level = 0,
std::uint32_t mip_level_count = 1,
std::uint32_t first_array_layer = 0,
std::uint32_t array_layer_count = 6
);
};
} // namespace gl
#endif // ANTKEEPER_GL_IMAGE_VIEW_HPP

+ 1064
- 0
src/engine/gl/image.cpp
File diff suppressed because it is too large
View File


+ 324
- 0
src/engine/gl/image.hpp View File

@ -0,0 +1,324 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_IMAGE_HPP
#define ANTKEEPER_GL_IMAGE_HPP
#include <engine/gl/format.hpp>
#include <engine/gl/image-flag.hpp>
#include <array>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <span>
namespace gl {
/**
*
*/
class image
{
public:
/// Destructs an image.
virtual ~image() = 0;
image(const image&) = delete;
image(image&&) = delete;
image& operator=(const image&) = delete;
image& operator=(image&&) = delete;
/**
* Reads pixel data from the image.
*
* @param mip_level Level-of-detail number. Level `0` is the base image level. Level `n` is the nth mipmap reduction image.
* @param offset_x Texel offset in the X-direction.
* @param offset_y Texel offset in the Y-direction.
* @param offset_z Texel offset in the Z-direction.
* @param width Width of the subimage.
* @param height Height of the subimage.
* @param depth Depth of the subimage.
* @param format Format of the image data.
* @param data Buffer into which image data will be read.
*
* @except std::out_of_range Image read operation mip level out of range.
* @except std::invalid_argument Image read operation used unsupported format.
*/
void read
(
std::uint32_t mip_level,
std::uint32_t offset_x,
std::uint32_t offset_y,
std::uint32_t offset_z,
std::uint32_t width,
std::uint32_t height,
std::uint32_t depth,
gl::format format,
std::span<std::byte> data
) const;
/**
* Writes pixel data to the image.
*
* @param mip_level Level-of-detail number. Level `0` is the base image level. Level `n` is the nth mipmap reduction image.
* @param offset_x Texel offset in the X-direction.
* @param offset_y Texel offset in the Y-direction.
* @param offset_z Texel offset in the Z-direction.
* @param width Width of the subimage.
* @param height Height of the subimage.
* @param depth Depth of the subimage.
* @param format Format of the image data.
* @param data Image data to write.
*
* @except std::out_of_range Image write operation mip level out of range.
* @except std::invalid_argument Image write operation used unsupported format.
* @except std::out_of_range Image write operation exceeded image bounds.
*/
void write
(
std::uint32_t mip_level,
std::uint32_t offset_x,
std::uint32_t offset_y,
std::uint32_t offset_z,
std::uint32_t width,
std::uint32_t height,
std::uint32_t depth,
gl::format format,
std::span<const std::byte> data
);
/**
* Copies pixel data from this image into another the image.
*
* @param src_mip_level Source image level-of-detail number. Level `0` is the base image level. Level `n` is the nth mipmap reduction image.
* @param src_x Source image texel offset in the X-direction.
* @param src_y Source image texel offset in the Y-direction.
* @param src_z Source image texel offset in the Z-direction.
* @param dst_image Destination image.
* @param dst_mip_level Destination image level-of-detail number. Level `0` is the base image level. Level `n` is the nth mipmap reduction image.
* @param dst_x Destination image texel offset in the X-direction.
* @param dst_y Destination image texel offset in the Y-direction.
* @param dst_z Destination image texel offset in the Z-direction.
* @param width Width of the subimage to copy.
* @param height Height of the subimage to copy.
* @param depth Depth of the subimage to copy.
*/
void copy
(
std::uint32_t src_mip_level,
std::uint32_t src_x,
std::uint32_t src_y,
std::uint32_t src_z,
image& dst_image,
std::uint32_t dst_mip_level,
std::uint32_t dst_x,
std::uint32_t dst_y,
std::uint32_t dst_z,
std::uint32_t width,
std::uint32_t height,
std::uint32_t depth
) const;
/**
* Generates mip subimages.
*/
void generate_mipmaps();
/// Returns the dimensionality of the image.
[[nodiscard]] inline constexpr std::uint8_t get_dimensionality() const noexcept
{
return m_dimensionality;
}
/// Returns `true` if the image is 1D, `false` otherwise.
[[nodiscard]] inline constexpr bool is_1d() const noexcept
{
return m_dimensionality == 1;
}
/// Returns `true` if the image is 2D, `false` otherwise.
[[nodiscard]] inline constexpr bool is_2d() const noexcept
{
return m_dimensionality == 2;
}
/// Returns `true` if the image is 3D, `false` otherwise.
[[nodiscard]] inline constexpr bool is_3d() const noexcept
{
return m_dimensionality == 3;
}
/// Returns the format and type of the texel blocks contained in the image.
[[nodiscard]] inline constexpr format get_format() const noexcept
{
return m_format;
}
/// Returns the dimensions of the image.
[[nodiscard]] inline constexpr const std::array<std::uint32_t, 3>& get_dimensions() const noexcept
{
return m_dimensions;
}
/// Returns the number of levels of detail available for minified sampling of the image.
[[nodiscard]] inline constexpr std::uint32_t get_mip_levels() const noexcept
{
return m_mip_levels;
}
/// Returns the number of layers in the image.
[[nodiscard]] inline constexpr std::uint32_t get_array_layers() const noexcept
{
return m_array_layers;
}
/// Returns the image flags.
[[nodiscard]] inline constexpr std::uint8_t get_flags() const noexcept
{
return m_flags;
}
/// Returns `true` if the image is cube map compatible, `false` otherwise.
[[nodiscard]] inline constexpr bool is_cube_compatible() const noexcept
{
return m_flags & std::to_underlying(image_flag::cube_compatible);
}
protected:
/**
* Constructs an image.
*
* @param dimensionality Image dimensionality, on `[1, 3]`.
* @param format Format and type of the texel blocks that will be contained in the image.
* @param width Width of the image.
* @param height Height of the image.
* @param depth Depth of the image.
* @param mip_levels Number of levels of detail available for minified sampling of the image.
* @param array_layers Number of layers in the image.
* @param flags Image flags.
*
* @except std::invalid_argument Image constructed with unsupported format.
* @except std::invalid_argument Image dimensions must be nonzero.
* @except std::invalid_argument Image mip levels must be nonzero.
* @except std::out_of_range Image mip levels exceed `1 + log2(max(width, height, depth))`.
* @except std::invalid_argument Image array layers must be nonzero.
* @except std::invalid_argument 1D image must have a height and depth of `1`.
* @except std::invalid_argument 2D image must have a depth of `1`.
* @except std::invalid_argument 3D image arrays not supported.
* @except std::invalid_argument Cube compatible image must be 2D.
* @except std::invalid_argument Cube compatible image width and height must be equal.
* @except std::invalid_argument Cube compatible image array layers must be a multiple of 6.
*/
image
(
std::uint8_t dimensionality,
gl::format format,
std::uint32_t width,
std::uint32_t height,
std::uint32_t depth,
std::uint32_t mip_levels,
std::uint32_t array_layers,
std::uint32_t flags
);
private:
unsigned int m_gl_texture_target{0};
unsigned int m_gl_texture_name{0};
std::uint8_t m_dimensionality{0};
format m_format{format::undefined};
std::array<std::uint32_t, 3> m_dimensions{0, 0, 0};
std::uint32_t m_mip_levels{0};
std::uint32_t m_array_layers{0};
std::uint8_t m_flags{0};
friend class image_view;
};
/**
* 1D image.
*/
class image_1d: public image
{
public:
/// @copydoc image::image
image_1d
(
gl::format format,
std::uint32_t width,
std::uint32_t mip_levels = 1,
std::uint32_t array_layers = 1,
std::uint32_t flags = 0
);
};
/**
* 2D image.
*/
class image_2d: public image
{
public:
/// @copydoc image::image
image_2d
(
gl::format format,
std::uint32_t width,
std::uint32_t height,
std::uint32_t mip_levels = 1,
std::uint32_t array_layers = 1,
std::uint32_t flags = 0
);
};
/**
* 3D image.
*/
class image_3d: public image
{
public:
/// @copydoc image::image
image_3d
(
gl::format format,
std::uint32_t width,
std::uint32_t height,
std::uint32_t depth,
std::uint32_t mip_levels = 1,
std::uint32_t flags = 0
);
};
/**
* Cube-compatible 2D image.
*/
class image_cube: public image_2d
{
public:
/// @copydoc image_2d::image_2d
image_cube
(
gl::format format,
std::uint32_t width,
std::uint32_t mip_levels = 1,
std::uint32_t array_layers = 6
);
};
} // namespace gl
#endif // ANTKEEPER_GL_IMAGE_HPP

+ 42
- 0
src/engine/gl/index-type.hpp View File

@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_INDEX_TYPE_HPP
#define ANTKEEPER_GL_INDEX_TYPE_HPP
#include <cstdint>
namespace gl {
/// Index types for index buffers.
enum class index_type: std::uint8_t
{
/// 8-bit unsigned integer values.
uint8,
/// 16-bit unsigned integer values.
uint16,
/// 32-bit unsigned integer values.
uint32
};
} // namespace gl
#endif // ANTKEEPER_GL_INDEX_TYPE_HPP

+ 50
- 0
src/engine/gl/logic-op.hpp View File

@ -0,0 +1,50 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_LOGIC_OP_HPP
#define ANTKEEPER_GL_LOGIC_OP_HPP
#include <cstdint>
namespace gl {
/// Framebuffer logical operations.
enum class logic_op: std::uint8_t
{
bitwise_clear,
bitwise_and,
bitwise_and_reverse,
bitwise_copy,
bitwise_and_inverted,
bitwise_no_op,
bitwise_xor,
bitwise_or,
bitwise_nor,
bitwise_equivalent,
bitwise_invert,
bitwise_or_reverse,
bitwise_copy_inverted,
bitwise_or_inverted,
bitwise_nand,
bitwise_set,
};
} // namespace gl
#endif // ANTKEEPER_GL_LOGIC_OP_HPP

+ 219
- 0
src/engine/gl/opengl/gl-format-lut.hpp View File

@ -0,0 +1,219 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_GL_FORMAT_LUT_HPP
#define ANTKEEPER_GL_GL_FORMAT_LUT_HPP
#include <glad/gl.h>
namespace gl {
/// Maps gl::format to OpenGL internal format, base format, and pixel type.
constexpr GLenum gl_format_lut[][3] =
{
{0 , 0 , 0 }, // undefined
{0 , GL_RG , 0 }, // r4g4_unorm_pack8
{GL_RGBA4 , GL_RGBA , GL_UNSIGNED_SHORT_4_4_4_4 }, // r4g4b4a4_unorm_pack16
{GL_RGBA4 , GL_BGRA , GL_UNSIGNED_SHORT_4_4_4_4 }, // b4g4r4a4_unorm_pack16
{GL_RGB565 , GL_RGB , GL_UNSIGNED_SHORT_5_6_5 }, // r5g6b5_unorm_pack16
{GL_RGB565 , GL_BGR , GL_UNSIGNED_SHORT_5_6_5 }, // b5g6r5_unorm_pack16
{GL_RGB5_A1 , GL_RGBA , GL_UNSIGNED_SHORT_5_5_5_1 }, // r5g5b5a1_unorm_pack16
{GL_RGB5_A1 , GL_BGRA , GL_UNSIGNED_SHORT_5_5_5_1 }, // b5g5r5a1_unorm_pack16
{GL_RGB5_A1 , GL_BGRA , GL_UNSIGNED_SHORT_1_5_5_5_REV }, // a1r5g5b5_unorm_pack16
{GL_R8 , GL_RED , GL_UNSIGNED_BYTE }, // r8_unorm
{GL_R8_SNORM , GL_RED , GL_BYTE }, // r8_snorm
{0 , GL_RED , GL_UNSIGNED_BYTE }, // r8_uscaled
{0 , GL_RED , GL_BYTE }, // r8_sscaled
{GL_R8UI , GL_RED_INTEGER , GL_UNSIGNED_BYTE }, // r8_uint
{GL_R8I , GL_RED_INTEGER , GL_BYTE }, // r8_sint
{0 , GL_RED , GL_UNSIGNED_BYTE }, // r8_srgb
{GL_RG8 , GL_RG , GL_UNSIGNED_BYTE }, // r8g8_unorm
{GL_RG8_SNORM , GL_RG , GL_BYTE }, // r8g8_snorm
{0 , GL_RED , GL_UNSIGNED_BYTE }, // r8g8_uscaled
{0 , GL_RED , GL_BYTE }, // r8g8_sscaled
{GL_RG8UI , GL_RG_INTEGER , GL_UNSIGNED_BYTE }, // r8g8_uint
{GL_RG8I , GL_RG_INTEGER , GL_BYTE }, // r8g8_sint
{0 , GL_RG , GL_UNSIGNED_BYTE }, // r8g8_srgb
{GL_RGB8 , GL_RGB , GL_UNSIGNED_BYTE }, // r8g8b8_unorm
{GL_RGB8_SNORM , GL_RGB , GL_BYTE }, // r8g8b8_snorm
{0 , GL_RED , GL_UNSIGNED_BYTE }, // r8g8b8_uscaled
{0 , GL_RED , GL_BYTE }, // r8g8b8_sscaled
{GL_RGB8UI , GL_RGB_INTEGER , GL_UNSIGNED_BYTE }, // r8g8b8_uint
{GL_RGB8I , GL_RGB_INTEGER , GL_BYTE }, // r8g8b8_sint
{GL_SRGB8 , GL_RGB , GL_UNSIGNED_BYTE }, // r8g8b8_srgb
{GL_RGB8 , GL_BGR , GL_UNSIGNED_BYTE }, // b8g8r8_unorm
{GL_RGB8_SNORM , GL_BGR , GL_BYTE }, // b8g8r8_snorm
{0 , GL_BGR , GL_UNSIGNED_BYTE }, // b8g8r8_uscaled
{0 , GL_BGR , GL_BYTE }, // b8g8r8_sscaled
{GL_RGB8UI , GL_BGR_INTEGER , GL_UNSIGNED_BYTE }, // b8g8r8_uint
{GL_RGB8I , GL_BGR_INTEGER , GL_BYTE }, // b8g8r8_sint
{GL_SRGB8 , GL_BGR , GL_UNSIGNED_BYTE }, // b8g8r8_srgb
{GL_RGBA8 , GL_RGBA , GL_UNSIGNED_BYTE }, // r8g8b8a8_unorm
{GL_RGBA8_SNORM , GL_RGBA , GL_BYTE }, // r8g8b8a8_snorm
{0 , GL_RGBA , GL_UNSIGNED_BYTE }, // r8g8b8a8_uscaled
{0 , GL_RGBA , GL_BYTE }, // r8g8b8a8_sscaled
{GL_RGBA8UI , GL_RGBA_INTEGER , GL_UNSIGNED_BYTE }, // r8g8b8a8_uint
{GL_RGBA8I , GL_RGBA_INTEGER , GL_BYTE }, // r8g8b8a8_sint
{GL_SRGB8_ALPHA8 , GL_RGBA , GL_UNSIGNED_BYTE }, // r8g8b8a8_srgb
{GL_RGBA8 , GL_BGRA , GL_UNSIGNED_BYTE }, // b8g8r8a8_unorm
{GL_RGBA8_SNORM , GL_BGRA , GL_BYTE }, // b8g8r8a8_snorm
{0 , GL_BGRA , GL_UNSIGNED_BYTE }, // b8g8r8a8_uscaled
{0 , GL_BGRA , GL_BYTE }, // b8g8r8a8_sscaled
{GL_RGBA8UI , GL_BGRA_INTEGER , GL_UNSIGNED_BYTE }, // b8g8r8a8_uint
{GL_RGBA8I , GL_BGRA_INTEGER , GL_BYTE }, // b8g8r8a8_sint
{GL_SRGB8_ALPHA8 , GL_BGRA , GL_UNSIGNED_BYTE }, // b8g8r8a8_srgb
{GL_RGBA8 , GL_RGBA , GL_UNSIGNED_INT_8_8_8_8_REV }, // a8b8g8r8_unorm_pack32
{GL_RGBA8_SNORM , GL_RGBA , 0 }, // a8b8g8r8_snorm_pack32
{0 , GL_RGBA , GL_UNSIGNED_INT_8_8_8_8_REV }, // a8b8g8r8_uscaled_pack32
{0 , GL_RGBA , 0 }, // a8b8g8r8_sscaled_pack32
{GL_RGBA8UI , GL_RGBA_INTEGER , GL_UNSIGNED_INT_8_8_8_8_REV }, // a8b8g8r8_uint_pack32
{GL_RGBA8I , GL_RGBA_INTEGER , 0 }, // a8b8g8r8_sint_pack32
{GL_SRGB8_ALPHA8 , GL_RGBA , GL_UNSIGNED_INT_8_8_8_8_REV }, // a8b8g8r8_srgb_pack32
{GL_RGB10_A2 , GL_BGRA , GL_UNSIGNED_INT_2_10_10_10_REV }, // a2r10g10b10_unorm_pack32
{0 , GL_BGRA , 0 }, // a2r10g10b10_snorm_pack32
{0 , GL_BGRA , GL_UNSIGNED_INT_2_10_10_10_REV }, // a2r10g10b10_uscaled_pack32
{0 , GL_BGRA , 0 }, // a2r10g10b10_sscaled_pack32
{GL_RGB10_A2UI , GL_BGRA_INTEGER , GL_UNSIGNED_INT_2_10_10_10_REV }, // a2r10g10b10_uint_pack32
{0 , GL_BGRA_INTEGER , 0 }, // a2r10g10b10_sint_pack32
{GL_RGB10_A2 , GL_RGBA , GL_UNSIGNED_INT_2_10_10_10_REV }, // a2b10g10r10_unorm_pack32
{0 , GL_RGBA , 0 }, // a2b10g10r10_snorm_pack32
{0 , GL_RGBA , GL_UNSIGNED_INT_2_10_10_10_REV }, // a2b10g10r10_uscaled_pack32
{0 , GL_RGBA , 0 }, // a2b10g10r10_sscaled_pack32
{GL_RGB10_A2UI , GL_RGBA_INTEGER , GL_UNSIGNED_INT_2_10_10_10_REV }, // a2b10g10r10_uint_pack32
{0 , GL_RGBA_INTEGER , 0 }, // a2b10g10r10_sint_pack32
{GL_R16 , GL_RED , GL_UNSIGNED_SHORT }, // r16_unorm
{GL_R16_SNORM , GL_RED , GL_SHORT }, // r16_snorm
{0 , GL_RED , GL_UNSIGNED_SHORT }, // r16_uscaled
{0 , GL_RED , GL_SHORT }, // r16_sscaled
{GL_R16UI , GL_RED_INTEGER , GL_UNSIGNED_SHORT }, // r16_uint
{GL_R16I , GL_RED_INTEGER , GL_SHORT }, // r16_sint
{GL_R16F , GL_RED , GL_HALF_FLOAT }, // r16_sfloat
{GL_RG16 , GL_RG , GL_UNSIGNED_SHORT }, // r16g16_unorm
{GL_RG16_SNORM , GL_RG , GL_SHORT }, // r16g16_snorm
{0 , GL_RG , GL_UNSIGNED_SHORT }, // r16g16_uscaled
{0 , GL_RG , GL_SHORT }, // r16g16_sscaled
{GL_RG16UI , GL_RG_INTEGER , GL_UNSIGNED_SHORT }, // r16g16_uint
{GL_RG16I , GL_RG_INTEGER , GL_SHORT }, // r16g16_sint
{GL_RG16F , GL_RG , GL_HALF_FLOAT }, // r16g16_sfloat
{GL_RGB16 , GL_RGB , GL_UNSIGNED_SHORT }, // r16g16b16_unorm
{GL_RGB16_SNORM , GL_RGB , GL_SHORT }, // r16g16b16_snorm
{0 , GL_RGB , GL_UNSIGNED_SHORT }, // r16g16b16_uscaled
{0 , GL_RGB , GL_SHORT }, // r16g16b16_sscaled
{GL_RGB16UI , GL_RGB_INTEGER , GL_UNSIGNED_SHORT }, // r16g16b16_uint
{GL_RGB16I , GL_RGB_INTEGER , GL_SHORT }, // r16g16b16_sint
{GL_RGB16F , GL_RGB , GL_HALF_FLOAT }, // r16g16b16_sfloat
{GL_RGBA16 , GL_RGBA , GL_UNSIGNED_SHORT }, // r16g16b16a16_unorm
{GL_RGBA16_SNORM , GL_RGBA , GL_SHORT }, // r16g16b16a16_snorm
{0 , GL_RGBA , GL_UNSIGNED_SHORT }, // r16g16b16a16_uscaled
{0 , GL_RGBA , GL_SHORT }, // r16g16b16a16_sscaled
{GL_RGBA16UI , GL_RGBA_INTEGER , GL_UNSIGNED_SHORT }, // r16g16b16a16_uint
{GL_RGBA16I , GL_RGBA_INTEGER , GL_SHORT }, // r16g16b16a16_sint
{GL_RGBA16F , GL_RGBA , GL_HALF_FLOAT }, // r16g16b16a16_sfloat
{GL_R32UI , GL_RED_INTEGER , GL_UNSIGNED_INT }, // r32_uint
{GL_R32I , GL_RED_INTEGER , GL_INT }, // r32_sint
{GL_R32F , GL_RED , GL_FLOAT }, // r32_sfloat
{GL_RG32UI , GL_RG_INTEGER , GL_UNSIGNED_INT }, // r32g32_uint
{GL_RG32I , GL_RG_INTEGER , GL_INT }, // r32g32_sint
{GL_RG32F , GL_RG , GL_FLOAT }, // r32g32_sfloat
{GL_RGB32UI , GL_RGB_INTEGER , GL_UNSIGNED_INT }, // r32g32b32_uint
{GL_RGB32I , GL_RGB_INTEGER , GL_INT }, // r32g32b32_sint
{GL_RGB32F , GL_RGB , GL_FLOAT }, // r32g32b32_sfloat
{GL_RGBA32UI , GL_RGBA_INTEGER , GL_UNSIGNED_INT }, // r32g32b32a32_uint
{GL_RGBA32I , GL_RGBA_INTEGER , GL_INT }, // r32g32b32a32_sint
{GL_RGBA32F , GL_RGBA , GL_FLOAT }, // r32g32b32a32_sfloat
{0 , GL_RED_INTEGER , 0 }, // r64_uint
{0 , GL_RED_INTEGER , 0 }, // r64_sint
{0 , GL_RED , GL_DOUBLE }, // r64_sfloat
{0 , GL_RG_INTEGER , 0 }, // r64g64_uint
{0 , GL_RG_INTEGER , 0 }, // r64g64_sint
{0 , GL_RG , GL_DOUBLE }, // r64g64_sfloat
{0 , GL_RGB_INTEGER , 0 }, // r64g64b64_uint
{0 , GL_RGB_INTEGER , 0 }, // r64g64b64_sint
{0 , GL_RGB , GL_DOUBLE }, // r64g64b64_sfloat
{0 , GL_RGBA_INTEGER , 0 }, // r64g64b64a64_uint
{0 , GL_RGBA_INTEGER , 0 }, // r64g64b64a64_sint
{0 , GL_RGBA , GL_DOUBLE }, // r64g64b64a64_sfloat
{GL_R11F_G11F_B10F , GL_BGR , GL_UNSIGNED_INT_10F_11F_11F_REV }, // b10g11r11_ufloat_pack32
{GL_RGB9_E5 , GL_BGR , GL_UNSIGNED_INT_5_9_9_9_REV }, // e5b9g9r9_ufloat_pack32
{GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, // d16_unorm
{GL_DEPTH_COMPONENT24 , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT }, // x8_d24_unorm_pack32
{GL_DEPTH_COMPONENT32F , GL_DEPTH_COMPONENT, GL_FLOAT }, // d32_sfloat
{GL_STENCIL_INDEX8 , GL_STENCIL_INDEX , GL_UNSIGNED_BYTE }, // s8_uint
{0 , GL_DEPTH_STENCIL , 0 }, // d16_unorm_s8_uint
{GL_DEPTH24_STENCIL8 , GL_DEPTH_STENCIL , GL_UNSIGNED_INT_24_8 }, // d24_unorm_s8_uint
{GL_DEPTH32F_STENCIL8 , GL_DEPTH_STENCIL , GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // d32_sfloat_s8_uint
{0 , GL_RGB , 0 }, // bc1_rgb_unorm_block,
{0 , GL_RGB , 0 }, // bc1_rgb_srgb_block,
{0 , GL_RGBA , 0 }, // bc1_rgba_unorm_block,
{0 , GL_RGBA , 0 }, // bc1_rgba_srgb_block,
{0 , GL_RGBA , 0 }, // bc2_unorm_block,
{0 , GL_RGBA , 0 }, // bc2_srgb_block,
{0 , GL_RGBA , 0 }, // bc3_unorm_block,
{0 , GL_RGBA , 0 }, // bc3_srgb_block,
{GL_COMPRESSED_RED_RGTC1 , GL_RED , 0 }, // bc4_unorm_block,
{GL_COMPRESSED_SIGNED_RED_RGTC1 , GL_RED , 0 }, // bc4_snorm_block,
{GL_COMPRESSED_RG_RGTC2 , GL_RG , 0 }, // bc5_unorm_block,
{GL_COMPRESSED_SIGNED_RG_RGTC2 , GL_RG , 0 }, // bc5_snorm_block,
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT , GL_RGB , 0 }, // bc6h_ufloat_block,
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT , GL_RGB , 0 }, // bc6h_sfloat_block,
{GL_COMPRESSED_RGBA_BPTC_UNORM , GL_RGBA , 0 }, // bc7_unorm_block,
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM , GL_RGBA , 0 }, // bc7_srgb_block,
{GL_COMPRESSED_RGB8_ETC2 , GL_RGB , 0 }, // etc2_r8g8b8_unorm_block,
{GL_COMPRESSED_SRGB8_ETC2 , GL_RGB , 0 }, // etc2_r8g8b8_srgb_block,
{GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 , GL_RGBA , 0 }, // etc2_r8g8b8a1_unorm_block,
{GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_RGBA , 0 }, // etc2_r8g8b8a1_srgb_block,
{GL_COMPRESSED_RGBA8_ETC2_EAC , GL_RGBA , 0 }, // etc2_r8g8b8a8_unorm_block,
{GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC , GL_RGBA , 0 }, // etc2_r8g8b8a8_srgb_block,
{GL_COMPRESSED_R11_EAC , GL_RED , 0 }, // eac_r11_unorm_block,
{GL_COMPRESSED_SIGNED_R11_EAC , GL_RED , 0 }, // eac_r11_snorm_block,
{GL_COMPRESSED_RG11_EAC , GL_RG , 0 }, // eac_r11g11_unorm_block,
{GL_COMPRESSED_SIGNED_RG11_EAC , GL_RG , 0 }, // eac_r11g11_snorm_block,
{0 , GL_RGBA , 0 }, // astc_4x4_unorm_block,
{0 , GL_RGBA , 0 }, // astc_4x4_srgb_block,
{0 , GL_RGBA , 0 }, // astc_5x4_unorm_block,
{0 , GL_RGBA , 0 }, // astc_5x4_srgb_block,
{0 , GL_RGBA , 0 }, // astc_5x5_unorm_block,
{0 , GL_RGBA , 0 }, // astc_5x5_srgb_block,
{0 , GL_RGBA , 0 }, // astc_6x5_unorm_block,
{0 , GL_RGBA , 0 }, // astc_6x5_srgb_block,
{0 , GL_RGBA , 0 }, // astc_6x6_unorm_block,
{0 , GL_RGBA , 0 }, // astc_6x6_srgb_block,
{0 , GL_RGBA , 0 }, // astc_8x5_unorm_block,
{0 , GL_RGBA , 0 }, // astc_8x5_srgb_block,
{0 , GL_RGBA , 0 }, // astc_8x6_unorm_block,
{0 , GL_RGBA , 0 }, // astc_8x6_srgb_block,
{0 , GL_RGBA , 0 }, // astc_8x8_unorm_block,
{0 , GL_RGBA , 0 }, // astc_8x8_srgb_block,
{0 , GL_RGBA , 0 }, // astc_10x5_unorm_block,
{0 , GL_RGBA , 0 }, // astc_10x5_srgb_block,
{0 , GL_RGBA , 0 }, // astc_10x6_unorm_block,
{0 , GL_RGBA , 0 }, // astc_10x6_srgb_block,
{0 , GL_RGBA , 0 }, // astc_10x8_unorm_block,
{0 , GL_RGBA , 0 }, // astc_10x8_srgb_block,
{0 , GL_RGBA , 0 }, // astc_10x10_unorm_block,
{0 , GL_RGBA , 0 }, // astc_10x10_srgb_block,
{0 , GL_RGBA , 0 }, // astc_12x10_unorm_block,
{0 , GL_RGBA , 0 }, // astc_12x10_srgb_block,
{0 , GL_RGBA , 0 }, // astc_12x12_unorm_block,
{0 , GL_RGBA , 0 } // astc_12x12_srgb_block
};
} // namespace gl
#endif // ANTKEEPER_GL_GL_FORMAT_LUT_HPP

+ 33
- 36
src/engine/gl/opengl/gl-shader-variables.cpp View File

@ -18,10 +18,7 @@
*/
#include <engine/gl/opengl/gl-shader-variables.hpp>
#include <engine/gl/texture-1d.hpp>
#include <engine/gl/texture-2d.hpp>
#include <engine/gl/texture-3d.hpp>
#include <engine/gl/texture-cube.hpp>
#include <engine/gl/texture.hpp>
#include <numeric>
namespace gl {
@ -450,8 +447,8 @@ gl_shader_texture_1d::gl_shader_texture_1d(std::size_t size, GLint gl_uniform_lo
void gl_shader_texture_1d::update(const texture_1d& value) const noexcept
{
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices.front()));
glBindTexture(GL_TEXTURE_1D, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
@ -462,8 +459,8 @@ void gl_shader_texture_1d::update(const texture_1d& value, std::size_t index) co
const GLint gl_texture_index = gl_texture_unit_indices[index];
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_index));
glBindTexture(GL_TEXTURE_1D, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_index), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_index), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
@ -474,8 +471,8 @@ void gl_shader_texture_1d::update(std::span values, std
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_1D, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -487,8 +484,8 @@ void gl_shader_texture_1d::update(std::span> v
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_1D, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -506,8 +503,8 @@ gl_shader_texture_2d::gl_shader_texture_2d(std::size_t size, GLint gl_uniform_lo
void gl_shader_texture_2d::update(const texture_2d& value) const noexcept
{
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices.front()));
glBindTexture(GL_TEXTURE_2D, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
@ -518,8 +515,8 @@ void gl_shader_texture_2d::update(const texture_2d& value, std::size_t index) co
const GLint gl_texture_index = gl_texture_unit_indices[index];
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_index));
glBindTexture(GL_TEXTURE_2D, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_index), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_index), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
@ -530,8 +527,8 @@ void gl_shader_texture_2d::update(std::span values, std
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_2D, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -543,8 +540,8 @@ void gl_shader_texture_2d::update(std::span> v
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_2D, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -562,8 +559,8 @@ gl_shader_texture_3d::gl_shader_texture_3d(std::size_t size, GLint gl_uniform_lo
void gl_shader_texture_3d::update(const texture_3d& value) const noexcept
{
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices.front()));
glBindTexture(GL_TEXTURE_3D, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
@ -574,8 +571,8 @@ void gl_shader_texture_3d::update(const texture_3d& value, std::size_t index) co
const GLint gl_texture_index = gl_texture_unit_indices[index];
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_index));
glBindTexture(GL_TEXTURE_3D, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_index), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_index), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
@ -586,8 +583,8 @@ void gl_shader_texture_3d::update(std::span values, std
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_3D, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -599,8 +596,8 @@ void gl_shader_texture_3d::update(std::span> v
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_3D, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -618,8 +615,8 @@ gl_shader_texture_cube::gl_shader_texture_cube(std::size_t size, GLint gl_unifor
void gl_shader_texture_cube::update(const texture_cube& value) const noexcept
{
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices.front()));
glBindTexture(GL_TEXTURE_CUBE_MAP, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices.front()), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
@ -630,8 +627,8 @@ void gl_shader_texture_cube::update(const texture_cube& value, std::size_t index
const GLint gl_texture_index = gl_texture_unit_indices[index];
// Bind texture to texture unit
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_index));
glBindTexture(GL_TEXTURE_CUBE_MAP, value.m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_index), value.get_image_view() ? value.get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_index), value.get_sampler() ? value.get_sampler()->m_gl_named_sampler : 0);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
@ -642,8 +639,8 @@ void gl_shader_texture_cube::update(std::span values,
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader
@ -655,8 +652,8 @@ void gl_shader_texture_cube::update(std::span
// Bind textures
for (std::size_t i = 0; i < values.size(); ++i)
{
glActiveTexture(GL_TEXTURE0 + static_cast<GLenum>(gl_texture_unit_indices[index + i]));
glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->m_gl_texture_id);
glBindTextureUnit(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_image_view() ? values[i]->get_image_view()->m_gl_texture_name : 0);
glBindSampler(static_cast<GLuint>(gl_texture_unit_indices[index + i]), values[i]->get_sampler() ? values[i]->get_sampler()->m_gl_named_sampler : 0);
}
// Pass texture unit indices to shader

+ 1
- 1
src/engine/gl/opengl/gl-shader-variables.hpp View File

@ -21,7 +21,7 @@
#define ANTKEEPER_GL_GL_SHADER_VARIABLES_HPP
#include <engine/gl/shader-variable.hpp>
#include <glad/glad.h>
#include <glad/gl.h>
#include <vector>
namespace gl {

+ 62
- 0
src/engine/gl/pipeline-color-blend-state.hpp View File

@ -0,0 +1,62 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_COLOR_BLEND_STATE_HPP
#define ANTKEEPER_GL_PIPELINE_COLOR_BLEND_STATE_HPP
#include <engine/gl/logic-op.hpp>
#include <engine/gl/color-blend-equation.hpp>
#include <array>
#include <cstdlib>
namespace gl {
/// Pipeline color blend state.
struct pipeline_color_blend_state
{
/// Controls whether to apply logical operations.
bool logic_op_enabled{false};
/// Selects which logical operation to apply.
gl::logic_op logic_op{gl::logic_op::bitwise_copy};
/// Controls whether blending is enabled for the corresponding color attachment.
bool blend_enabled{false};
/// Color blend factors and operations.
gl::color_blend_equation color_blend_equation
{
blend_factor::one,
blend_factor::zero,
blend_op::add,
blend_factor::one,
blend_factor::zero,
blend_op::add
};
/// Bitmask indicating which of the RGBA components are enabled for writing.
std::uint8_t color_write_mask{0b1111};
/// RGBA components of the blend constant that are used in blending, depending on the blend factor.
std::array<float, 4> blend_constants{0.0f, 0.0f, 0.0f, 0.0f};
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_COLOR_BLEND_STATE_HPP

+ 57
- 0
src/engine/gl/pipeline-depth-stencil-state.hpp View File

@ -0,0 +1,57 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_DEPTH_STENCIL_STATE_HPP
#define ANTKEEPER_GL_PIPELINE_DEPTH_STENCIL_STATE_HPP
#include <engine/gl/compare-op.hpp>
#include <engine/gl/stencil-op-state.hpp>
#include <cstdlib>
namespace gl {
/// Pipeline depth/stencil state.
struct pipeline_depth_stencil_state
{
/// `true` if depth testing is enabled, `false` otherwise.
bool depth_test_enabled{true};
/**
* `true` if depth writes are enabled when depth testing is enabled, `false` otherwise.
*
* @note Depth writes are always disabled when depth testing is disabled.
*/
bool depth_write_enabled{true};
/// Comparison operator to use in the depth comparison step of the depth test.
compare_op depth_compare_op{compare_op::less};
/// `true` if stencil testing is enabled, `false` otherwise.
bool stencil_test_enabled{false};
/// Stencil testing parameters for front faces.
stencil_op_state stencil_front;
/// Stencil testing parameters for back faces.
stencil_op_state stencil_back;
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_DEPTH_STENCIL_STATE_HPP

+ 40
- 0
src/engine/gl/pipeline-input-assembly-state.hpp View File

@ -0,0 +1,40 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_INPUT_ASSEMBLY_STATE_HPP
#define ANTKEEPER_GL_PIPELINE_INPUT_ASSEMBLY_STATE_HPP
#include <engine/gl/primitive-topology.hpp>
#include <cstdlib>
namespace gl {
/// Pipeline input assembly state.
struct pipeline_input_assembly_state
{
/// Primitive topology.
primitive_topology topology{primitive_topology::triangle_list};
/// Controls whether a special vertex index value is treated as restarting the assembly of primitives.
bool primitive_restart_enabled{false};
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_INPUT_ASSEMBLY_STATE_HPP

+ 73
- 0
src/engine/gl/pipeline-rasterization-state.hpp View File

@ -0,0 +1,73 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_RASTERIZATION_STATE_HPP
#define ANTKEEPER_GL_PIPELINE_RASTERIZATION_STATE_HPP
#include <engine/gl/front-face.hpp>
#include <engine/gl/cull-mode.hpp>
#include <engine/gl/fill-mode.hpp>
#include <engine/gl/provoking-vertex-mode.hpp>
#include <cstdlib>
namespace gl {
/// Pipeline rasterization state.
struct pipeline_rasterization_state
{
/// `true` if rasterizer discard should be enabled, `false` otherwise.
bool rasterizer_discard_enabled{false};
/// Polygon rasterization mode.
gl::fill_mode fill_mode{gl::fill_mode::fill};
/// Triangle culling mode.
gl::cull_mode cull_mode{gl::cull_mode::back};
/// Polygon front-facing orientation.
gl::front_face front_face{gl::front_face::counter_clockwise};
/// `true` if depth bias should be enabled, `false` otherwise.
bool depth_bias_enabled{false};
/// Depth bias constant factor.
float depth_bias_constant_factor{0.0f};
/// Depth bias slope factor.
float depth_bias_slope_factor{0.0f};
/// `true` if depth clamp should be enabled, `false` otherwise.
bool depth_clamp_enabled{false};
/// `true` if scissor testing should be enabled, `false` otherwise.
bool scissor_test_enabled{false};
/// Vertex to be used as the source of data for flat-shaded varyings.
gl::provoking_vertex_mode provoking_vertex_mode{gl::provoking_vertex_mode::last};
/// Diameter of rasterized points.
float point_size{1.0f};
/// Width of rasterized line segments.
float line_width{1.0f};
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_RASTERIZATION_STATE_HPP

+ 41
- 0
src/engine/gl/pipeline-vertex-input-state.hpp View File

@ -0,0 +1,41 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_VERTEX_INPUT_STATE_HPP
#define ANTKEEPER_GL_PIPELINE_VERTEX_INPUT_STATE_HPP
#include <engine/gl/vertex-input-attribute.hpp>
#include <engine/gl/vertex-input-binding.hpp>
#include <vector>
namespace gl {
/// Pipeline input assembly state.
struct pipeline_vertex_input_state
{
/// Vertex bindings.
std::vector<vertex_input_binding> vertex_bindings;
/// Vertex attributes.
std::vector<vertex_input_attribute> vertex_attributes;
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_VERTEX_INPUT_STATE_HPP

+ 41
- 0
src/engine/gl/pipeline-viewport-state.hpp View File

@ -0,0 +1,41 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_VIEWPORT_STATE_HPP
#define ANTKEEPER_GL_PIPELINE_VIEWPORT_STATE_HPP
#include <engine/gl/viewport.hpp>
#include <engine/gl/scissor-region.hpp>
#include <vector>
namespace gl {
/// Pipeline viewport state.
struct pipeline_viewport_state
{
/// Active viewports.
std::vector<viewport> viewports;
/// Active scissor regions.
std::vector<scissor_region> scissors;
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_VIEWPORT_STATE_HPP

+ 1494
- 0
src/engine/gl/pipeline.cpp
File diff suppressed because it is too large
View File


+ 435
- 0
src/engine/gl/pipeline.hpp View File

@ -0,0 +1,435 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PIPELINE_HPP
#define ANTKEEPER_GL_PIPELINE_HPP
#include <engine/gl/pipeline-viewport-state.hpp>
#include <engine/gl/pipeline-rasterization-state.hpp>
#include <engine/gl/pipeline-depth-stencil-state.hpp>
#include <engine/gl/pipeline-input-assembly-state.hpp>
#include <engine/gl/pipeline-vertex-input-state.hpp>
#include <engine/gl/pipeline-color-blend-state.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/gl/clear-value.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/clear-bits.hpp>
#include <engine/gl/stencil-face-bits.hpp>
#include <array>
#include <cstdint>
#include <span>
namespace app { class sdl_window_manager; }
namespace gl {
/**
* Graphics pipeline interface.
*/
class pipeline
{
public:
/**
* Constructs a pipeline.
*/
pipeline();
/// @name Vertex input state
/// @{
/**
* Sets the vertex input.
*
* @param vertex_bindings Vertex bindings.
* @param vertex_attributes Vertex attributes.
*/
// void set_vertex_input(std::span<const vertex_input_binding> vertex_bindings, std::span<const vertex_input_attribute> vertex_attributes);
void bind_framebuffer(const gl::framebuffer* framebuffer);
void bind_shader_program(const gl::shader_program* shader_program);
/**
* Binds a vertex array.
*
* @param array Vertex array to bind.
*/
void bind_vertex_array(const vertex_array* array);
/**
* Binds vertex buffers.
*
* @param first_binding Index of the first vertex input binding.
* @param buffers Sequence of buffers to bind.
* @param offsets Sequence of byte offsets into each buffer.
* @param strides Sequence of byte strides between consecutive elements within each buffer.
*/
void bind_vertex_buffers(std::uint32_t first_binding, std::span<const vertex_buffer* const> buffers, std::span<const std::size_t> offsets, std::span<const std::size_t> strides);
/// @}
/// @name Input assembly state
/// @{
/**
* Sets the primitive topology to use for drawing.
*
* @param topology Primitive topology to use for drawing.
*/
void set_primitive_topology(primitive_topology topology);
/**
* Controls whether a special vertex index value is treated as restarting the assembly of primitives.
*
* @param enabled `true` if a special vertex index value should restart the assembly of primitives, `false` otherwise.
*/
void set_primitive_restart_enabled(bool enabled);
/// @}
/// @name Viewport state
/// @{
/**
* Sets one or more viewports.
*
* @param first_viewport Index of the first viewport to set.
* @param viewports Sequence of viewports.
*
* @except std::out_of_range Viewport index out of range.
*
* @warning Currently only a single viewport is supported.
*/
void set_viewport(std::uint32_t first_viewport, std::span<const viewport> viewports);
/**
* Sets one or more scissor regions.
*
* @param first_scissor Index of the first scissor region to set.
* @param scissors Sequence of scissor regions.
*
* @except std::out_of_range Scissor region index out of range.
*
* @warning Currently only a single scissor region is supported.
*/
void set_scissor(std::uint32_t first_scissor, std::span<const scissor_region> scissors);
/// @}
/// @name Rasterizer state
/// @{
/**
* Controls whether primitives are discarded before the rasterization stage.
*
* @param enabled `true` if primitives should be discarded before the rasterization stage, `false` otherwise.
*/
void set_rasterizer_discard_enabled(bool enabled);
/**
* Sets the polygon rasterization mode.
*
* @param mode Polygon rasterization mode.
*/
void set_fill_mode(fill_mode mode);
/**
* Sets the triangle culling mode.
*
* @param mode Triangle culling mode.
*/
void set_cull_mode(cull_mode mode);
/**
* Sets the front-facing triangle orientation.
*
* @param face Front-facing triangle orientation.
*/
void set_front_face(front_face face);
/**
* Controls whether to bias fragment depth values.
*
* @param enabled `true` if fragment depth values should be biased, `false` otherwise.
*/
void set_depth_bias_enabled(bool enabled);
/**
* Sets depth bias factors.
*
* @param constant_factor Scalar factor controlling the constant depth value added to each fragment.
* @param slope_factor Scalar factor applied to a fragment's slope in depth bias calculations.
*/
void set_depth_bias_factors(float constant_factor, float slope_factor);
/**
* Controls whether depth clamping is enabled.
*
* @param enabled `true` if depth clamping should be enabled, `false` otherwise.
*/
void set_depth_clamp_enabled(bool enabled);
/**
* Enables or disables scissor testing.
*
* @param enabled `true` if scissor testing should be enabled, `false` otherwise.
*/
void set_scissor_test_enabled(bool enabled);
/**
* Sets the vertex to be used as the source of data for flat-shaded varyings.
*
* @param mode Provoking vertex mode.
*/
void set_provoking_vertex_mode(provoking_vertex_mode mode);
/**
* Sets the the diameter of rasterized points.
*
* @param size Point size.
*/
void set_point_size(float size);
/**
* Sets the width of rasterized lines.
*
* @param width Width of rasterized line segments.
*/
void set_line_width(float width);
/// @}
/// @name Depth/stencil state
/// @{
/**
* Controls whether depth testing is enabled.
*
* @param enabled `true` if depth testing should be enabled, `false` otherwise.
*/
void set_depth_test_enabled(bool enabled);
/**
* Controls whether depth writes are enabled.
*
* @param enabled `true` if depth writes should be enabled when depth testing is enabled, `false` otherwise.
*
* @note Depth writes are always disabled when depth testing is disabled.
*/
void set_depth_write_enabled(bool enabled);
/**
* Sets the depth comparison operator.
*
* @param compare_op Comparison operator to use in the depth comparison step of the depth test.
*/
void set_depth_compare_op(gl::compare_op compare_op);
/**
* Controls whether stencil testing is enabled.
*
* @param enabled `true` if stencil testing should be enabled, `false` otherwise.
*/
void set_stencil_test_enabled(bool enabled);
/**
* Sets the stencil operations.
*
* @param face_mask Bit mask specifying the set of stencil states for which to update the stencil operation.
* @param fail_op Action performed on samples that fail the stencil test.
* @param pass_op Action performed on samples that pass both the depth and stencil tests.
* @param depth_fail_op Action performed on samples that pass the stencil test and fail the depth test.
* @param compare_op Comparison operator used in the stencil test.
*/
void set_stencil_op(std::uint8_t face_mask, stencil_op fail_op, stencil_op pass_op, stencil_op depth_fail_op, gl::compare_op compare_op);
/**
* Sets the stencil compare mask.
*
* @param face_mask Bit mask specifying the set of stencil states for which to update the compare mask.
* @param compare_mask New value to use as the stencil compare mask.
*/
void set_stencil_compare_mask(std::uint8_t face_mask, std::uint32_t compare_mask);
/**
* Sets the stencil reference value.
*
* @param face_mask Bit mask specifying the set of stencil states for which to update the reference value.
* @param reference New value to use as the stencil reference value.
*/
void set_stencil_reference(std::uint8_t face_mask, std::uint32_t reference);
/**
* Sets the stencil write mask.
*
* @param face_mask Bit mask specifying the set of stencil states for which to update the write mask.
* @param write_mask New value to use as the stencil write mask.
*/
void set_stencil_write_mask(std::uint8_t face_mask, std::uint32_t write_mask);
/// @}
/// @name Color blend state
/// @{
/**
* Controls whether whether logical operations are enabled.
*
* @param enabled `true` if logical operations should be enabled, `false` otherwise.
*/
void set_logic_op_enabled(bool enabled);
/**
* Selects which logical operation to apply.
*
* @param logic_op Logical operation to apply.
*/
void set_logic_op(gl::logic_op logic_op);
/**
* Controls whether blending is enabled for the corresponding color attachment.
*
* @param enabled `true` if color blending should be enabled, `false` otherwise.
*/
void set_color_blend_enabled(bool enabled);
/**
* Sets the color blend factors and operations.
*
* @param equation Color blend factors and operations.
*/
void set_color_blend_equation(const color_blend_equation& equation);
/**
* Sets the color write mask.
*
* @param mask Bitmask indicating which of the RGBA components are enabled for writing.
*/
void set_color_write_mask(std::uint8_t mask);
/**
* Sets the values of the blend constants.
*
* @param blend_constants RGBA components of the blend constant that are used in blending, depending on the blend factor.
*/
void set_blend_constants(const std::array<float, 4>& blend_constants);
/// @}
/// @name Drawing
/// @{
/**
* Draws primitives.
*
* @param vertex_count Number of vertices to draw.
* @param instance_count Number of instances to draw.
* @param first_vertex Index of the first vertex to draw.
* @param first_instance Instance ID of the first instance to draw. (WARNING: not currently supported)
*
* @warning @p first_instance currently not supported.
*/
void draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance);
/**
* Draws primitives with indexed vertices.
*
* @param index_count Number of vertices to draw.
* @param instance_count Number of instances to draw.
* @param first_index Base index within the index buffer.
* @param vertex_offset Value added to the vertex index before indexing into the vertex buffer.
* @param first_instance Instance ID of the first instance to draw. (WARNING: not currently supported)
*
* @warning @p first_instance currently not supported.
*/
void draw_indexed(std::uint32_t index_count, std::uint32_t instance_count, std::uint32_t first_index, std::int32_t vertex_offset, std::uint32_t first_instance);
/// @}
/// @name Clear
/// @{
/**
* Clears the color, depth, or stencil buffers of current attachments.
*
* @param mask Bit mask indicating which buffers should be cleared.
* @param value Color, depth, and stencil values with which to fill the cleared attachments.
*/
void clear_attachments(std::uint8_t mask, const clear_value& value);
/// @}
/// @name Limitations
/// @{
/// Returns the dimensions of the default framebuffer.
[[nodiscard]] inline constexpr const std::array<std::uint32_t, 2>& get_default_framebuffer_dimensions() const noexcept
{
return m_default_framebuffer_dimensions;
}
/// Returns the maximum number of supported viewports.
[[nodiscard]] inline constexpr std::uint32_t get_max_viewports() const noexcept
{
return m_max_viewports;
}
/// Returns the maximum supported degree of sampler anisotropy.
[[nodiscard]] inline constexpr float get_max_sampler_anisotropy() const noexcept
{
return m_max_sampler_anisotropy;
}
/// @}
private:
friend class app::sdl_window_manager;
/// Changes the reported dimensions of the default framebuffer.
void defaut_framebuffer_resized(std::uint32_t width, std::uint32_t height) noexcept;
void fetch_vertex_input_state();
void fetch_input_assembly_state();
void fetch_viewport_state();
void fetch_rasterization_state();
void fetch_depth_stencil_state();
void fetch_color_blend_state();
void fetch_clear_value();
std::uint32_t m_max_viewports{1};
float m_max_sampler_anisotropy{0.0f};
std::array<std::uint32_t, 2> m_default_framebuffer_dimensions{0, 0};
pipeline_vertex_input_state m_vertex_input_state;
pipeline_input_assembly_state m_input_assembly_state;
pipeline_viewport_state m_viewport_state;
pipeline_rasterization_state m_rasterization_state;
pipeline_depth_stencil_state m_depth_stencil_state;
pipeline_color_blend_state m_color_blend_state;
clear_value m_clear_value;
const framebuffer* m_framebuffer{};
const shader_program* m_shader_program{};
const vertex_array* m_vertex_array{};
};
} // namespace gl
#endif // ANTKEEPER_GL_PIPELINE_HPP

+ 66
- 0
src/engine/gl/primitive-topology.hpp View File

@ -0,0 +1,66 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PRIMITIVE_TOPOLOGY_HPP
#define ANTKEEPER_GL_PRIMITIVE_TOPOLOGY_HPP
#include <cstdint>
namespace gl {
/// Primitive topologies.
enum class primitive_topology: std::uint8_t
{
/// Separate point primitives.
point_list,
/// Separate line primitives.
line_list,
/// Connected line primitives with consecutive lines sharing a vertex.
line_strip,
/// Separate triangle primitives.
triangle_list,
/// Connected triangle primitives with consecutive triangles sharing an edge.
triangle_strip,
/// Connected triangle primitives with all triangles sharing a common vertex.
triangle_fan,
/// Separate line primitives with adjacency.
line_list_with_adjacency,
/// Connected line primitives with adjacency, with consecutive primitives sharing three vertices.
line_strip_with_adjacency,
/// Separate triangle primitives with adjacency.
triangle_list_with_adjacency,
/// Connected triangle primitives with adjacency, with consecutive triangles sharing an edge.
triangle_strip_with_adjacency,
/// Separate patch primitives.
patch_list
};
} // namespace gl
#endif // ANTKEEPER_GL_PRIMITIVE_TOPOLOGY_HPP

+ 39
- 0
src/engine/gl/provoking-vertex-mode.hpp View File

@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_PROVOKING_VERTEX_MODE_HPP
#define ANTKEEPER_GL_PROVOKING_VERTEX_MODE_HPP
#include <cstdint>
namespace gl {
/// Vertex to be used as the source of data for flat-shaded varyings.
enum class provoking_vertex_mode: std::uint8_t
{
/// Provoking vertex is the first non-adjacency vertex.
first,
/// Provoking vertex is the last non-adjacency vertex.
last
};
} // namespace gl
#endif // ANTKEEPER_GL_PROVOKING_VERTEX_MODE_HPP

+ 0
- 220
src/engine/gl/rasterizer.cpp View File

@ -1,220 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/vertex-array.hpp>
#include <glad/glad.h>
#include <engine/debug/log.hpp>
#include <stdexcept>
namespace gl {
static constexpr GLenum drawing_mode_lut[] =
{
GL_POINTS,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_LINES,
GL_LINE_STRIP_ADJACENCY,
GL_LINES_ADJACENCY,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_TRIANGLES,
GL_TRIANGLE_STRIP_ADJACENCY,
GL_TRIANGLES_ADJACENCY
};
static constexpr GLenum element_array_type_lut[] =
{
GL_UNSIGNED_BYTE,
GL_UNSIGNED_SHORT,
GL_UNSIGNED_INT
};
rasterizer::rasterizer():
bound_vao(nullptr),
bound_shader_program(nullptr)
{
// Determine dimensions of default framebuffer
GLint scissor_box[4] = {0, 0, 0, 0};
glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
// Setup default framebuffer
default_framebuffer = std::make_unique<framebuffer>();
default_framebuffer->m_gl_framebuffer_id = 0;
default_framebuffer->m_dimensions = {scissor_box[2], scissor_box[3]};
// Bind default framebuffer
bound_framebuffer = default_framebuffer.get();
// Enable seamless filtering across cubemap faces
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
if (GLAD_GL_ARB_clip_control)
{
// Improve depth buffer precision by setting depth range to `[0, 1]`.
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
}
else
{
debug::log::error("glClipControl not supported");
throw std::runtime_error("glClipControl not supported");
}
// glClipControl alternative for OpenGL < v4.5 (need to adjust shadow scale-bias matrix too)
// glDepthRange(-1.0f, 1.0f);
// Set clear depth to `0` for reversed depth
glClearDepth(0.0f);
glDisable(GL_MULTISAMPLE);
dummy_vao = std::make_unique<gl::vertex_array>();
}
rasterizer::~rasterizer()
{}
void rasterizer::context_resized(int width, int height)
{
default_framebuffer->m_dimensions = {width, height};
}
void rasterizer::use_framebuffer(const gl::framebuffer& framebuffer)
{
if (bound_framebuffer != &framebuffer)
{
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.m_gl_framebuffer_id);
bound_framebuffer = &framebuffer;
}
}
void rasterizer::set_clear_color(float r, float g, float b, float a)
{
glClearColor(r, g, b, a);
}
void rasterizer::set_clear_depth(float depth)
{
glClearDepth(depth);
}
void rasterizer::set_clear_stencil(int s)
{
glClearStencil(s);
}
void rasterizer::clear_framebuffer(bool color, bool depth, bool stencil)
{
GLbitfield mask = 0;
if (color)
mask |= GL_COLOR_BUFFER_BIT;
if (depth)
mask |= GL_DEPTH_BUFFER_BIT;
if (stencil)
mask |= GL_STENCIL_BUFFER_BIT;
glClear(mask);
}
void rasterizer::set_viewport(int x, int y, int width, int height)
{
glViewport(x, y, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
void rasterizer::use_program(const shader_program& program)
{
if (bound_shader_program != &program)
{
glUseProgram(program.gl_program_id);
bound_shader_program = &program;
}
}
void rasterizer::draw_arrays(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count)
{
GLenum gl_mode = drawing_mode_lut[static_cast<std::size_t>(mode)];
if (bound_vao != &vao)
{
glBindVertexArray(vao.gl_array_id);
bound_vao = &vao;
}
glDrawArrays(gl_mode, static_cast<GLint>(offset), static_cast<GLsizei>(count));
}
void rasterizer::draw_arrays(drawing_mode mode, std::size_t offset, std::size_t count)
{
GLenum gl_mode = drawing_mode_lut[static_cast<std::size_t>(mode)];
if (bound_vao != dummy_vao.get())
{
glBindVertexArray(dummy_vao->gl_array_id);
bound_vao = dummy_vao.get();
}
glDrawArrays(gl_mode, static_cast<GLint>(offset), static_cast<GLsizei>(count));
}
void rasterizer::draw_arrays_instanced(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, std::size_t instance_count)
{
GLenum gl_mode = drawing_mode_lut[static_cast<std::size_t>(mode)];
if (bound_vao != &vao)
{
glBindVertexArray(vao.gl_array_id);
bound_vao = &vao;
}
glDrawArraysInstanced(gl_mode, static_cast<GLint>(offset), static_cast<GLsizei>(count), static_cast<GLsizei>(instance_count));
}
void rasterizer::draw_elements(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type)
{
GLenum gl_mode = drawing_mode_lut[static_cast<std::size_t>(mode)];
GLenum gl_type = element_array_type_lut[static_cast<std::size_t>(type)];
if (bound_vao != &vao)
{
glBindVertexArray(vao.gl_array_id);
bound_vao = &vao;
}
glDrawElements(gl_mode, static_cast<GLsizei>(count), gl_type, reinterpret_cast<const GLvoid*>(offset));
}
void rasterizer::draw_elements(drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type)
{
GLenum gl_mode = drawing_mode_lut[static_cast<std::size_t>(mode)];
GLenum gl_type = element_array_type_lut[static_cast<std::size_t>(type)];
if (bound_vao != dummy_vao.get())
{
glBindVertexArray(dummy_vao->gl_array_id);
bound_vao = dummy_vao.get();
}
glDrawElements(gl_mode, static_cast<GLsizei>(count), gl_type, reinterpret_cast<const GLvoid*>(offset));
}
} // namespace gl

+ 0
- 142
src/engine/gl/rasterizer.hpp View File

@ -1,142 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_RASTERIZER_HPP
#define ANTKEEPER_GL_RASTERIZER_HPP
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/element-array-type.hpp>
#include <cstddef>
#include <memory>
namespace gl {
class framebuffer;
class vertex_array;
class shader_program;
/**
* Interface to the OpenGL state and drawing functions.
*/
class rasterizer
{
public:
/**
* Creates a rasterizer. Warning: This must be called after an OpenGL context has been created.
*/
rasterizer();
/// Destroys a rasterizer.
~rasterizer();
/**
* This should be called when the window associated with the OpenGL context is resized, and will effectively changed the reported dimensions of the default framebuffer.
*/
void context_resized(int width, int height);
/**
* Sets the active framebuffer.
*
* @param framebuffer Framebuffer to use.
*/
void use_framebuffer(const framebuffer& framebuffer);
/**
* Sets the color to be used when the color buffer of a framebuffer is cleared.
*
* @param r Red color component.
* @param g Green color component.
* @param b Blue color component.
* @param a Alpha color component.
*/
void set_clear_color(float r, float g, float b, float a);
/**
* Sets the depth value to be used when the depth buffer of a framebuffer is cleared.
*
* @param depth Depth value.
*/
void set_clear_depth(float depth);
/**
* Sets the stencil value to be used when the stencil buffer of a framebuffer is cleared.
*
* @param s Stencil value.
*/
void set_clear_stencil(int s);
/**
* Clears the buffers attached to a framebuffer.
*
* @param color Specifies whether the color buffer should be cleared.
* @param depth Specifies whether the depth buffer should be cleared.
* @param stencil Specifies whether the stencil buffer should be cleared.
*/
void clear_framebuffer(bool color, bool depth, bool stencil);
/**
* Sets the active viewport.
*
* @param x X-coordinate of the viewport.
* @param y Y-coordinate of the viewport.
* @param width Width of the viewport.
* @param height Height of the viewport.
*/
void set_viewport(int x, int y, int width, int height);
/**
* Binds a shader program.
*
* @param program Shader program to bind.
*/
void use_program(const shader_program& program);
/**
*
*/
void draw_arrays(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count);
void draw_arrays(drawing_mode mode, std::size_t offset, std::size_t count);
void draw_arrays_instanced(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, std::size_t instance_count);
/**
*
*/
void draw_elements(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type);
void draw_elements(drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type);
/**
* Returns the default framebuffer associated with the OpenGL context of a window.
*/
[[nodiscard]] inline const framebuffer& get_default_framebuffer() const noexcept
{
return *default_framebuffer;
}
private:
std::unique_ptr<framebuffer> default_framebuffer;
std::unique_ptr<vertex_array> dummy_vao;
const framebuffer* bound_framebuffer{nullptr};
const vertex_array* bound_vao{nullptr};
const shader_program* bound_shader_program{nullptr};
};
} // namespace gl
#endif // ANTKEEPER_GL_RASTERIZER_HPP

+ 48
- 0
src/engine/gl/sampler-address-mode.hpp View File

@ -0,0 +1,48 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_SAMPLER_ADDRESS_MODE_HPP
#define ANTKEEPER_GL_SAMPLER_ADDRESS_MODE_HPP
#include <cstdint>
namespace gl {
/// Behaviors of sampling with texture coordinates outside an image.
enum class sampler_address_mode: std::uint8_t
{
/// Repeat wrap mode.
repeat,
/// Mirrored repeat wrap mode.
mirrored_repeat,
/// Clamp to edge wrap mode.
clamp_to_edge,
/// Clamp to border wrap mode.
clamp_to_border,
/// Mirror clamp to edge wrap mode.
mirror_clamp_to_edge
};
} // namespace gl
#endif // ANTKEEPER_GL_SAMPLER_ADDRESS_MODE_HPP

src/engine/gl/texture-type.hpp → src/engine/gl/sampler-filter.hpp View File

@ -17,29 +17,23 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_TYPE_HPP
#define ANTKEEPER_GL_TEXTURE_TYPE_HPP
#ifndef ANTKEEPER_GL_SAMPLER_FILTER_HPP
#define ANTKEEPER_GL_SAMPLER_FILTER_HPP
#include <cstdint>
namespace gl {
/// Texture types.
enum class texture_type: std::uint8_t
/// Filters used for texture lookups.
enum class sampler_filter: std::uint8_t
{
/// 1D texture.
one_dimensional,
/// Nearest filtering.
nearest,
/// 2D texture.
two_dimensional,
/// 3D texture.
three_dimensional,
/// Cube texture.
cube,
/// Linear filtering.
linear
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_TYPE_HPP
#endif // ANTKEEPER_GL_SAMPLER_FILTER_HPP

+ 39
- 0
src/engine/gl/sampler-mipmap-mode.hpp View File

@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_SAMPLER_MIPMAP_MODE_HPP
#define ANTKEEPER_GL_SAMPLER_MIPMAP_MODE_HPP
#include <cstdint>
namespace gl {
/// Mipmap modes used for texture lookups.
enum class sampler_mipmap_mode: std::uint8_t
{
/// Nearest filtering.
nearest,
/// Linear filtering.
linear
};
} // namespace gl
#endif // ANTKEEPER_GL_SAMPLER_MIPMAP_MODE_HPP

+ 230
- 0
src/engine/gl/sampler.cpp View File

@ -0,0 +1,230 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/sampler.hpp>
#include <glad/gl.h>
namespace gl {
namespace
{
static constexpr GLenum mag_filter_lut[] =
{
GL_NEAREST, // sampler_filter::nearest
GL_LINEAR // sampler_filter::linear
};
static constexpr GLenum min_filter_lut[][2] =
{
{
GL_NEAREST_MIPMAP_NEAREST, // sampler_filter::nearest, sampler_mipmap_mode::nearest
GL_NEAREST_MIPMAP_LINEAR // sampler_filter::nearest, sampler_mipmap_mode::linear
},
{
GL_LINEAR_MIPMAP_NEAREST, // sampler_filter::linear, sampler_mipmap_mode::nearest
GL_LINEAR_MIPMAP_LINEAR // sampler_filter::linear, sampler_mipmap_mode::linear
},
};
static constexpr GLenum wrap_lut[] =
{
GL_REPEAT, // sampler_address_mode::repeat
GL_MIRRORED_REPEAT, // sampler_address_mode::mirrored_repeat
GL_CLAMP_TO_EDGE, // sampler_address_mode::clamp_to_edge
GL_CLAMP_TO_BORDER, // sampler_address_mode::clamp_to_border
GL_MIRROR_CLAMP_TO_EDGE // sampler_address_mode::mirror_clamp_to_edge
};
static constexpr GLenum compare_func_lut[] =
{
GL_NEVER, // compare_op::never
GL_LESS, // compare_op::less
GL_EQUAL, // compare_op::equal
GL_LEQUAL, // compare_op::less_or_equal
GL_GREATER, // compare_op::greater
GL_NOTEQUAL, // compare_op::not_equal
GL_GEQUAL, // compare_op::greater_or_equal
GL_ALWAYS // compare_op::always
};
}
sampler::sampler
(
sampler_filter mag_filter,
sampler_filter min_filter,
sampler_mipmap_mode mipmap_mode,
sampler_address_mode address_mode_u,
sampler_address_mode address_mode_v,
sampler_address_mode address_mode_w,
float mip_lod_bias,
float max_anisotropy,
bool compare_enabled,
gl::compare_op compare_op,
float min_lod,
float max_lod,
const std::array<float, 4>& border_color
)
{
glCreateSamplers(1, &m_gl_named_sampler);
set_mag_filter(mag_filter);
set_min_filter(min_filter);
set_mipmap_mode(mipmap_mode);
set_address_mode_u(address_mode_u);
set_address_mode_v(address_mode_v);
set_address_mode_w(address_mode_w);
set_mip_lod_bias(mip_lod_bias);
set_max_anisotropy(max_anisotropy);
set_compare_enabled(compare_enabled);
set_compare_op(compare_op);
set_min_lod(min_lod);
set_max_lod(max_lod);
set_border_color(border_color);
}
sampler::~sampler()
{
glDeleteSamplers(1, &m_gl_named_sampler);
}
void sampler::set_mag_filter(sampler_filter filter)
{
if (m_mag_filter != filter)
{
m_mag_filter = filter;
const auto gl_mag_filter = mag_filter_lut[std::to_underlying(m_mag_filter)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_MAG_FILTER, gl_mag_filter);
}
}
void sampler::set_min_filter(sampler_filter filter)
{
if (m_min_filter != filter)
{
m_min_filter = filter;
const auto gl_min_filter = min_filter_lut[std::to_underlying(m_min_filter)][std::to_underlying(m_mipmap_mode)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_MIN_FILTER, gl_min_filter);
}
}
void sampler::set_mipmap_mode(sampler_mipmap_mode mode)
{
if (m_mipmap_mode != mode)
{
m_mipmap_mode = mode;
const auto gl_min_filter = min_filter_lut[std::to_underlying(m_min_filter)][std::to_underlying(m_mipmap_mode)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_MIN_FILTER, gl_min_filter);
}
}
void sampler::set_address_mode_u(sampler_address_mode mode)
{
if (m_address_mode_u != mode)
{
m_address_mode_u = mode;
const auto gl_wrap_s = wrap_lut[std::to_underlying(m_address_mode_u)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_WRAP_S, gl_wrap_s);
}
}
void sampler::set_address_mode_v(sampler_address_mode mode)
{
if (m_address_mode_v != mode)
{
m_address_mode_v = mode;
const auto gl_wrap_t = wrap_lut[std::to_underlying(m_address_mode_v)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_WRAP_T, gl_wrap_t);
}
}
void sampler::set_address_mode_w(sampler_address_mode mode)
{
if (m_address_mode_w != mode)
{
m_address_mode_w = mode;
const auto gl_wrap_r = wrap_lut[std::to_underlying(m_address_mode_w)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_WRAP_R, gl_wrap_r);
}
}
void sampler::set_mip_lod_bias(float bias)
{
if (m_mip_lod_bias != bias)
{
m_mip_lod_bias = bias;
glSamplerParameterf(m_gl_named_sampler, GL_TEXTURE_LOD_BIAS, m_mip_lod_bias);
}
}
void sampler::set_max_anisotropy(float anisotropy)
{
if (m_max_anisotropy != anisotropy)
{
m_max_anisotropy = anisotropy;
glSamplerParameterf(m_gl_named_sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_max_anisotropy);
}
}
void sampler::set_compare_enabled(bool enabled)
{
if (m_compare_enabled != enabled)
{
m_compare_enabled = enabled;
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_COMPARE_MODE, (m_compare_enabled) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
}
}
void sampler::set_compare_op(gl::compare_op op)
{
if (m_compare_op != op)
{
m_compare_op = op;
const auto gl_compare_func = compare_func_lut[std::to_underlying(m_compare_op)];
glSamplerParameteri(m_gl_named_sampler, GL_TEXTURE_COMPARE_FUNC, gl_compare_func);
}
}
void sampler::set_min_lod(float lod)
{
if (m_min_lod != lod)
{
m_min_lod = lod;
glSamplerParameterf(m_gl_named_sampler, GL_TEXTURE_MIN_LOD, m_min_lod);
}
}
void sampler::set_max_lod(float lod)
{
if (m_max_lod != lod)
{
m_max_lod = lod;
glSamplerParameterf(m_gl_named_sampler, GL_TEXTURE_MAX_LOD, m_max_lod);
}
}
void sampler::set_border_color(const std::array<float, 4>& color)
{
if (m_border_color != color)
{
m_border_color = color;
glSamplerParameterfv(m_gl_named_sampler, GL_TEXTURE_BORDER_COLOR, m_border_color.data());
}
}
} // namespace gl

+ 274
- 0
src/engine/gl/sampler.hpp View File

@ -0,0 +1,274 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_SAMPLER_HPP
#define ANTKEEPER_GL_SAMPLER_HPP
#include <engine/gl/sampler-filter.hpp>
#include <engine/gl/sampler-mipmap-mode.hpp>
#include <engine/gl/sampler-address-mode.hpp>
#include <engine/gl/compare-op.hpp>
#include <array>
namespace gl {
/**
* Sampler object.
*/
class sampler
{
public:
/**
* Constructs a sampler object.
*
* @param mag_filter Magnification filter to apply to lookups.
* @param min_filter Minification filter to apply to lookups.
* @param mipmap_mode Mipmap filter to apply to lookups.
* @param address_mode_u Addressing mode for U-coordinates outside `[0, 1)`.
* @param address_mode_v Addressing mode for V-coordinates outside `[0, 1)`.
* @param address_mode_w Addressing mode for W-coordinates outside `[0, 1)`.
* @param mip_lod_bias Bias to be added to mipmap LOD calculation.
* @param max_anisotropy Anisotropy clamp value.
* @param compare_enabled `true` to enable comparison against a reference value during lookups, `false` otherwise.
* @param compare_op Comparison operator to apply to fetched data, if compare is enabled.
* @param min_lod Minimum clamp value of the computed LOD.
* @param max_lod Maximum clamp value of the computed LOD.
* @param border_color Border color used for texture lookups.
*/
explicit sampler
(
sampler_filter mag_filter = sampler_filter::linear,
sampler_filter min_filter = sampler_filter::nearest,
sampler_mipmap_mode mipmap_mode = sampler_mipmap_mode::linear,
sampler_address_mode address_mode_u = sampler_address_mode::repeat,
sampler_address_mode address_mode_v = sampler_address_mode::repeat,
sampler_address_mode address_mode_w = sampler_address_mode::repeat,
float mip_lod_bias = 0.0f,
float max_anisotropy = 0.0f,
bool compare_enabled = false,
gl::compare_op compare_op = gl::compare_op::less,
float min_lod = -1000.0f,
float max_lod = 1000.0f,
const std::array<float, 4>& border_color = {0.0f, 0.0f, 0.0f, 0.0f}
);
/// Destroys a sampler object.
~sampler();
sampler(const sampler&) = delete;
sampler(sampler&&) = delete;
sampler& operator=(const sampler&) = delete;
sampler& operator=(sampler&&) = delete;
/**
* Sets the magnification filter to apply to lookups.
*
* @param filter Magnification filter to apply to lookups.
*/
void set_mag_filter(sampler_filter filter);
/**
* Sets the minification filter to apply to lookups.
*
* @param filter Minification filter to apply to lookups.
*/
void set_min_filter(sampler_filter filter);
/**
* Sets the mipmap filter to apply to lookups.
*
* @param mode Mipmap filter to apply to lookups.
*/
void set_mipmap_mode(sampler_mipmap_mode mode);
/**
* Sets the addressing mode for U-coordinates outside `[0, 1)`.
*
* @param Addressing mode for U-coordinates outside `[0, 1)`.
*/
void set_address_mode_u(sampler_address_mode mode);
/**
* Sets the addressing mode for V-coordinates outside `[0, 1)`.
*
* @param Addressing mode for V-coordinates outside `[0, 1)`.
*/
void set_address_mode_v(sampler_address_mode mode);
/**
* Sets the addressing mode for W-coordinates outside `[0, 1)`.
*
* @param Addressing mode for W-coordinates outside `[0, 1)`.
*/
void set_address_mode_w(sampler_address_mode mode);
/**
* Sets the bias to be added to mipmap LOD calculation.
*
* @param bias Bias to be added to mipmap LOD calculation.
*/
void set_mip_lod_bias(float bias);
/**
* Sets the anisotropy clamp value.
*
* @param anisotropy Anisotropy clamp value.
*/
void set_max_anisotropy(float anisotropy);
/**
* Enables or disables a comparison against a reference value during lookups.
*
* @param enabled `true` to enable comparison against a reference value during lookups, `false` otherwise.
*/
void set_compare_enabled(bool enabled);
/**
* Sets the comparison operator to apply to fetched data, if compare is enabled.
*
* @param op Comparison operator to apply to fetched data, if compare is enabled.
*/
void set_compare_op(gl::compare_op op);
/**
* Sets the minimum clamp value of the computed LOD.
*
* @param lod Minimum clamp value of the computed LOD.
*/
void set_min_lod(float lod);
/**
* Sets the maximum clamp value of the computed LOD.
*
* @param lod Maximum clamp value of the computed LOD.
*/
void set_max_lod(float lod);
/**
* Sets the border color used for texture lookups.
*
* @param color Border color used for texture lookups.
*/
void set_border_color(const std::array<float, 4>& color);
/// Returns the magnification filter to apply to lookups.
[[nodiscard]] inline constexpr sampler_filter get_mag_filter() const noexcept
{
return m_mag_filter;
}
/// Returns the minification filter to apply to lookups.
[[nodiscard]] inline constexpr sampler_filter get_min_filter() const noexcept
{
return m_min_filter;
}
/// Returns the mipmap filter to apply to lookups.
[[nodiscard]] inline constexpr sampler_mipmap_mode get_mipmap_mode() const noexcept
{
return m_mipmap_mode;
}
/// Returns the addressing mode for U-coordinates outside `[0, 1)`.
[[nodiscard]] inline constexpr sampler_address_mode get_address_mode_u() const noexcept
{
return m_address_mode_u;
}
/// Returns the addressing mode for V-coordinates outside `[0, 1)`.
[[nodiscard]] inline constexpr sampler_address_mode get_address_mode_v() const noexcept
{
return m_address_mode_v;
}
/// Returns the addressing mode for W-coordinates outside `[0, 1)`.
[[nodiscard]] inline constexpr sampler_address_mode get_address_mode_w() const noexcept
{
return m_address_mode_w;
}
/// Returns the bias to be added to mipmap LOD calculation.
[[nodiscard]] inline constexpr float get_mip_lod_bias() const noexcept
{
return m_mip_lod_bias;
}
/// Returns the anisotropy clamp value.
[[nodiscard]] inline constexpr float get_max_anisotropy() const noexcept
{
return m_max_anisotropy;
}
/// Returns `true` if comparison against a reference value during lookups is enabled, `false` otherwise.
[[nodiscard]] inline constexpr float get_compare_enabled() const noexcept
{
return m_compare_enabled;
}
/// Returns the comparison operator to apply to fetched data, if compare is enabled.
[[nodiscard]] inline constexpr compare_op get_compare_op() const noexcept
{
return m_compare_op;
}
/// Returns the minimum clamp value of the computed LOD.
[[nodiscard]] inline constexpr float get_min_lod() const noexcept
{
return m_min_lod;
}
/// Returns the maximum clamp value of the computed LOD.
[[nodiscard]] inline constexpr float get_max_lod() const noexcept
{
return m_max_lod;
}
/// Returns the border color used for texture lookups.
[[nodiscard]] inline constexpr const std::array<float, 4>& get_border_color() const noexcept
{
return m_border_color;
}
private:
friend class pipeline;
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 m_gl_named_sampler{0};
sampler_filter m_mag_filter{sampler_filter::linear};
sampler_filter m_min_filter{sampler_filter::nearest};
sampler_mipmap_mode m_mipmap_mode{sampler_mipmap_mode::linear};
sampler_address_mode m_address_mode_u{sampler_address_mode::repeat};
sampler_address_mode m_address_mode_v{sampler_address_mode::repeat};
sampler_address_mode m_address_mode_w{sampler_address_mode::repeat};
float m_mip_lod_bias{0.0f};
float m_max_anisotropy{0.0f};
bool m_compare_enabled{false};
gl::compare_op m_compare_op{gl::compare_op::less};
float m_min_lod{-1000.0f};
float m_max_lod{1000.0f};
std::array<float, 4> m_border_color{0.0f, 0.0f, 0.0f, 0.0f};
};
} // namespace gl
#endif // ANTKEEPER_GL_SAMPLER_HPP

src/engine/gl/texture-filter.hpp → src/engine/gl/scissor-region.hpp View File

@ -17,35 +17,31 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_FILTER_HPP
#define ANTKEEPER_GL_TEXTURE_FILTER_HPP
#ifndef ANTKEEPER_GL_SCISSOR_REGION_HPP
#define ANTKEEPER_GL_SCISSOR_REGION_HPP
#include <cstdint>
namespace gl {
/**
* Texture minification filter modes.
* Scissor region offset and extents.
*/
enum class texture_min_filter: std::uint8_t
struct scissor_region
{
nearest,
linear,
nearest_mipmap_nearest,
linear_mipmap_nearest,
nearest_mipmap_linear,
linear_mipmap_linear
};
/**
* Texture magnification filter modes.
*/
enum class texture_mag_filter: std::uint8_t
{
nearest,
linear
/// X-coordinate offset of the scissor region.
std::int32_t x{};
/// Y-coordinate offset of the scissor region.
std::int32_t y{};
/// Width of the scissor region.
std::uint32_t width;
/// Height of the scissor region.
std::uint32_t height;
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_FILTER_HPP
#endif // ANTKEEPER_GL_SCISSOR_REGION_HPP

+ 1
- 25
src/engine/gl/shader-object.cpp View File

@ -18,7 +18,7 @@
*/
#include <engine/gl/shader-object.hpp>
#include <glad/glad.h>
#include <glad/gl.h>
#include <stdexcept>
namespace gl {
@ -55,18 +55,6 @@ void shader_object::source(std::string_view source_code)
const GLint gl_length = static_cast<GLint>(source_code.length());
const GLchar* gl_string = source_code.data();
glShaderSource(gl_shader_id, 1, &gl_string, &gl_length);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_VALUE:
throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL.");
break;
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader object handle is not a shader object.");
break;
}
}
bool shader_object::compile()
@ -77,18 +65,6 @@ bool shader_object::compile()
// Compile OpenGL shader object
glCompileShader(gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_VALUE:
throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL.");
break;
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader object handle is not a shader object.");
break;
}
// Get OpenGL shader object compilation status
GLint gl_compile_status;
glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &gl_compile_status);

+ 1
- 25
src/engine/gl/shader-program.cpp View File

@ -20,7 +20,7 @@
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-object.hpp>
#include <engine/gl/opengl/gl-shader-variables.hpp>
#include <glad/glad.h>
#include <glad/gl.h>
#include <stdexcept>
#include <string_view>
@ -68,14 +68,6 @@ void shader_program::attach(const shader_object& object)
// Attach the OpenGL shader object to the OpenGL shader program
glAttachShader(gl_program_id, object.gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader object is already attached to the shader program.");
break;
}
// Add shader object to set of attached objects
attached_objects.insert(&object);
}
@ -102,14 +94,6 @@ void shader_program::detach(const shader_object& object)
// Detach the OpenGL shader object from the OpenGL shader program
glDetachShader(gl_program_id, object.gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader object is not attached to the shader program.");
break;
}
// Remove shader object from set of attached objects
attached_objects.erase(&object);
}
@ -138,14 +122,6 @@ bool shader_program::link()
// Link OpenGL shader program
glLinkProgram(gl_program_id);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader program is the currently active program object and transform feedback mode is active.");
break;
}
// Get OpenGL shader program linking status
GLint gl_link_status;
glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status);

+ 1
- 2
src/engine/gl/shader-program.hpp View File

@ -30,7 +30,6 @@
namespace gl {
class shader_object;
class rasterizer;
class shader_variable;
/**
@ -149,7 +148,7 @@ public:
}
private:
friend class rasterizer;
friend class pipeline;
void load_variables();

+ 3
- 3
src/engine/gl/shader-template.cpp View File

@ -24,7 +24,7 @@
#include <engine/resources/resource-loader.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/utility/text-file.hpp>
#include <engine/utility/hash/combine.hpp>
#include <engine/utility/hash/hash-combine.hpp>
#include <sstream>
#include <unordered_set>
@ -126,7 +126,7 @@ std::unique_ptr shader_template::build(const dictionary_type
if (has_geometry_directive())
{
// Compile fragment shader object and attach to shader program
// Compile geometry shader object and attach to shader program
geometry_object = compile(gl::shader_stage::geometry, definitions);
program->attach(*geometry_object);
}
@ -189,7 +189,7 @@ void shader_template::rehash()
m_hash = 0;
for (const auto& line: m_template_source.lines)
{
m_hash = hash::combine(m_hash, std::hash<std::string>{}(line));
m_hash = hash_combine(m_hash, std::hash<std::string>{}(line));
}
}

+ 42
- 0
src/engine/gl/stencil-face-bits.hpp View File

@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_STENCIL_FACE_BITS_HPP
#define ANTKEEPER_GL_STENCIL_FACE_BITS_HPP
#include <cstdint>
namespace gl {
/// Stencil face mask bits specifying which stencil state(s) to update.
enum: std::uint8_t
{
/// Only the front set of stencil state is updated.
stencil_face_front_bit = 0b01,
/// Only the back set of stencil state is updated.
stencil_face_back_bit = 0b10,
/// Both sets of stencil state are updated.
stencil_face_front_and_back = 0b11,
};
} // namespace gl
#endif // ANTKEEPER_GL_STENCIL_FACE_BITS_HPP

+ 56
- 0
src/engine/gl/stencil-op-state.hpp View File

@ -0,0 +1,56 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_STENCIL_OP_STATE_HPP
#define ANTKEEPER_GL_STENCIL_OP_STATE_HPP
#include <engine/gl/compare-op.hpp>
#include <engine/gl/stencil-op.hpp>
#include <cstdint>
namespace gl {
/// Stencil operation state.
struct stencil_op_state
{
/// Action performed on samples that fail the stencil test.
stencil_op fail_op{stencil_op::keep};
/// Action performed on samples that pass both the depth and stencil tests.
stencil_op pass_op{stencil_op::keep};
/// Action performed on samples that pass the stencil test and fail the depth test.
stencil_op depth_fail_op{stencil_op::keep};
/// Comparison operator used in the stencil test.
gl::compare_op compare_op{gl::compare_op::always};
/// Bits of the unsigned integer stencil values participating in the stencil test.
std::uint32_t compare_mask{0xffffffff};
/// Bits of the unsigned integer stencil values updated by the stencil test in the stencil framebuffer attachment.
std::uint32_t write_mask{0xffffffff};
/// Stencil reference value that is used in the unsigned stencil comparison.
std::uint32_t reference{0};
};
} // namespace gl
#endif // ANTKEEPER_GL_STENCIL_OP_STATE_HPP

+ 57
- 0
src/engine/gl/stencil-op.hpp View File

@ -0,0 +1,57 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_STENCIL_OP_HPP
#define ANTKEEPER_GL_STENCIL_OP_HPP
#include <cstdint>
namespace gl {
/// Stencil comparison functions.
enum class stencil_op: std::uint8_t
{
/// Keeps the current value.
keep,
/// Sets the value to `0`.
zero,
/// Sets the value to `reference`.
replace,
/// Increments the current value and clamps to the maximum representable unsigned value.
increment_and_clamp,
/// Decrements the current value and clamps to `0`.
decrement_and_clamp,
/// Bitwise-inverts the current value.
invert,
/// Increments the current value and wraps to `0` when the maximum value would have been exceeded.
increment_and_wrap,
/// Decrements the current value and wraps to the maximum possible value when the value would go below `0`.
decrement_and_wrap
};
} // namespace gl
#endif // ANTKEEPER_GL_STENCIL_OP_HPP

+ 0
- 38
src/engine/gl/texture-1d.cpp View File

@ -1,38 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/texture-1d.hpp>
namespace gl {
texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data):
texture(width, false, type, format, transfer_function, data)
{}
void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data)
{
texture::resize(width, type, format, transfer_function, data);
}
void texture_1d::set_wrapping(gl::texture_wrapping wrap_s)
{
texture::set_wrapping(wrap_s);
}
} // namespace gl

+ 0
- 49
src/engine/gl/texture-1d.hpp View File

@ -1,49 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_1D_HPP
#define ANTKEEPER_GL_TEXTURE_1D_HPP
#include <engine/gl/texture.hpp>
namespace gl {
/**
* A 1D texture which can be uploaded to shaders via shader inputs.
*/
class texture_1d: public texture
{
public:
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::transfer_function transfer_function = gl::transfer_function::linear, const std::byte* data = nullptr);
[[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::transfer_function, const std::byte*)
virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data);
/// @copydoc texture::set_wrapping(gl::texture_wrapping)
virtual void set_wrapping(gl::texture_wrapping wrap_s);
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_1D_HPP

+ 0
- 43
src/engine/gl/texture-2d.cpp View File

@ -1,43 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/texture-2d.hpp>
namespace gl {
texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data):
texture(width, height, false, type, format, transfer_function, data)
{}
void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data)
{
texture::resize(width, height, type, format, transfer_function, data);
}
void texture_2d::resize(std::uint16_t width, std::uint16_t height, const std::byte* data)
{
texture::resize(width, height, get_pixel_type(), get_pixel_format(), get_transfer_function(), data);
}
void texture_2d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t)
{
texture::set_wrapping(wrap_s, wrap_t);
}
} // namespace gl

+ 0
- 58
src/engine/gl/texture-2d.hpp View File

@ -1,58 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_2D_HPP
#define ANTKEEPER_GL_TEXTURE_2D_HPP
#include <engine/gl/texture.hpp>
namespace gl {
/**
* A 2D texture which can be uploaded to shaders via shader inputs.
*/
class texture_2d: public texture
{
public:
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::transfer_function transfer_function = gl::transfer_function::linear, const std::byte* data = nullptr);
[[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::transfer_function, const std::byte*)
void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, 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)
void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t) override;
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_2D_HPP

+ 0
- 38
src/engine/gl/texture-3d.cpp View File

@ -1,38 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/texture-3d.hpp>
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::transfer_function transfer_function, const std::byte* data):
texture(width, height, depth, false, type, format, transfer_function, 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::transfer_function transfer_function, const std::byte* data)
{
texture::resize(width, height, depth, type, format, transfer_function, data);
}
void texture_3d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r)
{
texture::set_wrapping(wrap_s, wrap_t, wrap_r);
}
} // namespace gl

+ 0
- 49
src/engine/gl/texture-3d.hpp View File

@ -1,49 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_3D_HPP
#define ANTKEEPER_GL_TEXTURE_3D_HPP
#include <engine/gl/texture.hpp>
namespace gl {
/**
* A 3D texture which can be uploaded to shaders via shader inputs.
*/
class texture_3d: public texture
{
public:
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::transfer_function transfer_function = gl::transfer_function::linear, const std::byte* data = nullptr);
[[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::transfer_function, 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::transfer_function transfer_function, 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);
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_3D_HPP

+ 0
- 110
src/engine/gl/texture-cube.cpp View File

@ -1,110 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <engine/gl/texture-cube.hpp>
#include <cmath>
namespace gl {
cube_map_layout texture_cube::infer_cube_map_layout(std::uint16_t w, std::uint16_t h) noexcept
{
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 {};
}
std::uint16_t texture_cube::infer_cube_map_face_size(cube_map_layout layout, std::uint16_t w, std::uint16_t h) noexcept
{
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;
}
}
texture_cube::texture_cube(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data):
texture(width, height, true, type, format, transfer_function, data)
{
resized();
}
void texture_cube::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data)
{
texture::resize(width, height, type, format, transfer_function, 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_transfer_function(), 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);
m_mip_count = 1 + static_cast<std::uint16_t>(std::log2(m_face_size));
}
} // namespace gl

+ 0
- 92
src/engine/gl/texture-cube.hpp View File

@ -1,92 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_TEXTURE_CUBE_HPP
#define ANTKEEPER_GL_TEXTURE_CUBE_HPP
#include <engine/gl/texture.hpp>
#include <engine/gl/cube-map-layout.hpp>
namespace gl {
/**
* A cube texture which can be uploaded to shaders via shader inputs.
*/
class texture_cube: public texture
{
public:
/**
* 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.
*/
[[nodiscard]] static cube_map_layout infer_cube_map_layout(std::uint16_t w, std::uint16_t h) noexcept;
/**
* 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.
*/
[[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::transfer_function transfer_function = gl::transfer_function::linear, const std::byte* data = nullptr);
[[nodiscard]] inline constexpr texture_type get_texture_type() const noexcept override
{
return texture_type::cube;
}
/// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::transfer_function, const std::byte*)
void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, 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{};
};
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_CUBE_HPP

+ 181
- 811
src/engine/gl/texture.cpp
File diff suppressed because it is too large
View File


+ 216
- 182
src/engine/gl/texture.hpp View File

@ -20,254 +20,288 @@
#ifndef ANTKEEPER_GL_TEXTURE_HPP
#define ANTKEEPER_GL_TEXTURE_HPP
#include <engine/gl/transfer-function.hpp>
#include <engine/gl/pixel-format.hpp>
#include <engine/gl/pixel-type.hpp>
#include <engine/gl/texture-filter.hpp>
#include <engine/gl/texture-type.hpp>
#include <engine/gl/texture-wrapping.hpp>
#include <array>
#include <cstdint>
#include <cstddef>
#include <span>
#include <tuple>
#include <engine/gl/image-view.hpp>
#include <engine/gl/sampler.hpp>
#include <memory>
namespace gl {
class framebuffer;
class gl_shader_texture_1d;
class gl_shader_texture_2d;
class gl_shader_texture_3d;
class gl_shader_texture_cube;
/**
* Abstract base class for 1D, 2D, 3D, and cube textures which can be uploaded to shaders via shader inputs.
* Image view and sampler object pair.
*/
class texture
{
public:
/**
* Destructs a texture.
*/
virtual ~texture();
/**
* Reads texture pixel data from the GPU.
*
* @param[out] data Pixel data buffer.
* @param[in] type Returned pixel component data type.
* @param[in] format Returned pixel format.
* @param[in] level Mip level to read.
*/
void read(std::span<std::byte> data, gl::pixel_type type, gl::pixel_format format, std::uint8_t level = 0) const;
/**
* Sets the texture filter modes.
* Constructs a texture.
*
* @param min_filter Texture minification filter mode.
* @param mag_filter Texture magnification filter mode.
* @param image_view Texture image view.
* @param sampler Texture sampler.
*/
void set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter);
/// @{
inline texture(std::shared_ptr<gl::sampler> sampler) noexcept:
m_sampler(sampler)
{}
/**
* Sets the texture minification filter mode.
*
* @param filter Texture minification filter mode.
*/
void set_min_filter(texture_min_filter filter);
constexpr texture() noexcept = default;
/// @}
/**
* Sets the texture magnification filter mode.
* Sets the sampler object.
*
* @param filter Texture magnification filter mode.
* @param sampler Sampler object.
*/
void set_mag_filter(texture_mag_filter filter);
inline void set_sampler(std::shared_ptr<gl::sampler> sampler)
{
m_sampler = sampler;
}
/**
* Sets the index of lowest accessible mip level.
*
* @param level Index of the lowest accessible mip level.
*/
void set_base_level(std::uint8_t level);
/// Returns the sampler object.
[[nodiscard]] inline constexpr const std::shared_ptr<sampler>& get_sampler() const noexcept
{
return m_sampler;
}
/**
* Sets the index of highest accessible mip level.
*
* @param level Index of the highest accessible mip level.
*/
void set_max_level(std::uint8_t level);
private:
std::shared_ptr<sampler> m_sampler;
};
/**
* 1D texture.
*/
class texture_1d: public texture
{
public:
/// @copydoc texture::texture
/// @{
inline texture_1d(std::shared_ptr<image_view_1d> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
/**
* Sets the range of accessible mip levels.
*
* @param base Index of the lowest accessible mip level.
* @param max Index of the highest accessible mip level.
*/
void set_mip_range(std::uint8_t base_level, std::uint8_t max_level);
constexpr texture_1d() noexcept = default;
/// @}
/**
* Sets the maximum anisotropy.
*
* @param level Max anisotropy on `[0.0, 1.0]`, with `0.0` indicating normal filtering, and `1.0` indicating maximum anisotropic filtering.
* Sets the image view.
*/
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.
[[nodiscard]] inline const std::array<std::uint16_t, 3>& get_dimensions() const noexcept
inline void set_image_view(std::shared_ptr<gl::image_view_1d> image_view)
{
return m_dimensions;
m_image_view = image_view;
}
/// Returns the width of the texture, in pixels.
[[nodiscard]] inline std::uint16_t get_width() const noexcept
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_1d>& get_image_view() const noexcept
{
return m_dimensions[0];
return m_image_view;
}
/// Returns the height of the texture, in pixels.
[[nodiscard]] inline std::uint16_t get_height() const noexcept
{
return m_dimensions[1];
}
private:
std::shared_ptr<image_view_1d> m_image_view;
};
/**
* 1D texture array.
*/
class texture_1d_array: public texture
{
public:
/// @copydoc texture::texture
/// @{
inline texture_1d_array(std::shared_ptr<image_view_1d_array> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
/// Returns the depth of the texture, in pixels.
[[nodiscard]] inline std::uint16_t get_depth() const noexcept
{
return m_dimensions[2];
}
constexpr texture_1d_array() noexcept = default;
/// @}
/// Returns the pixel type enumeration.
[[nodiscard]] inline pixel_type get_pixel_type() const noexcept
/**
* Sets the image view.
*/
inline void set_image_view(std::shared_ptr<gl::image_view_1d_array> image_view)
{
return m_pixel_type;
m_image_view = image_view;
}
/// Returns the pixel format enumeration.
[[nodiscard]] inline pixel_format get_pixel_format() const noexcept
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_1d_array>& get_image_view() const noexcept
{
return m_pixel_format;
return m_image_view;
}
/// Returns the transfer function.
[[nodiscard]] inline transfer_function get_transfer_function() const noexcept
{
return m_transfer_function;
}
private:
std::shared_ptr<image_view_1d_array> m_image_view;
};
/**
* 2D texture.
*/
class texture_2d: public texture
{
public:
/// @copydoc texture::texture
/// @{
inline texture_2d(std::shared_ptr<image_view_2d> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
constexpr texture_2d() noexcept = default;
/// @}
/// Returns the wrapping modes of the texture.
[[nodiscard]] inline const std::array<texture_wrapping, 3>& get_wrapping() const noexcept
/**
* Sets the image view.
*/
inline void set_image_view(std::shared_ptr<gl::image_view_2d> image_view)
{
return m_wrapping;
m_image_view = image_view;
}
/// Returns the filtering modes of the texture.
[[nodiscard]] inline const std::tuple<texture_min_filter, texture_mag_filter>& get_filters() const noexcept
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_2d>& get_image_view() const noexcept
{
return m_filters;
return m_image_view;
}
/// Returns the number of available mip levels.
[[nodiscard]] inline std::uint16_t get_mip_count() const noexcept
private:
std::shared_ptr<image_view_2d> m_image_view;
};
/**
* 2D texture array.
*/
class texture_2d_array: public texture
{
public:
/// @copydoc texture::texture
/// @{
inline texture_2d_array(std::shared_ptr<image_view_2d_array> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
constexpr texture_2d_array() noexcept = default;
/// @}
/**
* Sets the image view.
*/
inline void set_image_view(std::shared_ptr<gl::image_view_2d_array> image_view)
{
return m_mip_count;
m_image_view = image_view;
}
/// Returns the index of the lowest accessible mip level.
[[nodiscard]] inline std::uint8_t get_base_level() const noexcept
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_2d_array>& get_image_view() const noexcept
{
return m_base_level;
return m_image_view;
}
/// Returns the index of the highest accessible mip level.
[[nodiscard]] inline std::uint8_t get_max_level() const noexcept
private:
std::shared_ptr<image_view_2d_array> m_image_view;
};
/**
* 3D texture.
*/
class texture_3d: public texture
{
public:
/// @copydoc texture::texture
/// @{
inline texture_3d(std::shared_ptr<image_view_3d> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
constexpr texture_3d() noexcept = default;
/// @}
/**
* Sets the image view.
*/
inline void set_image_view(std::shared_ptr<gl::image_view_3d> image_view)
{
return m_max_level;
m_image_view = image_view;
}
/// Returns the maximum anisotropy.
[[nodiscard]] inline float get_max_anisotropy() const noexcept
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_3d>& get_image_view() const noexcept
{
return m_max_anisotropy;
return m_image_view;
}
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 transfer_function Transfer function of the texture 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.
*/
private:
std::shared_ptr<image_view_3d> m_image_view;
};
/**
* Cube texture.
*/
class texture_cube: public texture
{
public:
/// @copydoc texture::texture
/// @{
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::transfer_function transfer_function = gl::transfer_function::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::transfer_function transfer_function = gl::transfer_function::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::transfer_function transfer_function = gl::transfer_function::linear, const std::byte* data = nullptr);
inline texture_cube(std::shared_ptr<image_view_cube> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
constexpr texture_cube() noexcept = default;
/// @}
/**
* Sets the texture wrapping modes.
*
* @param wrap_s Wrapping mode for s-coordinates.
* @param wrap_t Wrapping mode for t-coordinates.
* @param wrap_r Wrapping mode for r-coordinates.
* Sets the image view.
*/
inline void set_image_view(std::shared_ptr<gl::image_view_cube> image_view)
{
m_image_view = image_view;
}
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_cube>& get_image_view() const noexcept
{
return m_image_view;
}
private:
std::shared_ptr<image_view_cube> m_image_view;
};
/**
* Cube texture array.
*/
class texture_cube_array: public texture
{
public:
/// @copydoc texture::texture
/// @{
virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r);
virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t);
virtual void set_wrapping(gl::texture_wrapping wrap_s);
inline texture_cube_array(std::shared_ptr<image_view_cube_array> image_view, std::shared_ptr<gl::sampler> sampler) noexcept:
texture(sampler),
m_image_view(image_view)
{}
constexpr texture_cube_array() noexcept = default;
/// @}
/**
* Resizes the 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 transfer_function Transfer_function 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.
* Sets the image view.
*/
/// @{
virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data);
virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data);
virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::transfer_function transfer_function, const std::byte* data);
/// @}
inline void set_image_view(std::shared_ptr<gl::image_view_cube_array> image_view)
{
m_image_view = image_view;
}
/// Returns the image view.
[[nodiscard]] inline constexpr const std::shared_ptr<image_view_cube_array>& get_image_view() const noexcept
{
return m_image_view;
}
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 m_gl_texture_target{};
unsigned int m_gl_texture_id{};
std::array<std::uint16_t, 3> m_dimensions{};
gl::pixel_type m_pixel_type{};
gl::pixel_format m_pixel_format{};
gl::transfer_function m_transfer_function{gl::transfer_function::linear};
std::array<texture_wrapping, 3> m_wrapping{texture_wrapping::repeat, texture_wrapping::repeat, texture_wrapping::repeat};
std::tuple<texture_min_filter, texture_mag_filter> m_filters{texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear};
std::uint8_t m_base_level{};
std::uint8_t m_max_level{255};
float m_max_anisotropy{};
protected:
std::uint16_t m_mip_count{};
std::shared_ptr<image_view_cube_array> m_image_view;
};
} // namespace gl

+ 295
- 73
src/engine/gl/vertex-array.cpp View File

@ -18,93 +18,315 @@
*/
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <glad/glad.h>
#include <engine/gl/opengl/gl-format-lut.hpp>
#include <glad/gl.h>
#include <stdexcept>
#include <string>
namespace gl {
static constexpr GLenum vertex_attribute_type_lut[] =
{
GL_BYTE,
GL_UNSIGNED_BYTE,
GL_SHORT,
GL_UNSIGNED_SHORT,
GL_INT,
GL_UNSIGNED_INT,
GL_HALF_FLOAT,
GL_FLOAT,
GL_DOUBLE
};
vertex_array::vertex_array()
{
glGenVertexArrays(1, &gl_array_id);
namespace {
// 0 = unscaled, 1 = normalized, 2 = scaled
static constexpr std::uint8_t format_scale_lut[] =
{
0, // undefined
1, // r4g4_unorm_pack8
1, // r4g4b4a4_unorm_pack16
1, // b4g4r4a4_unorm_pack16
1, // r5g6b5_unorm_pack16
1, // b5g6r5_unorm_pack16
1, // r5g5b5a1_unorm_pack16
1, // b5g5r5a1_unorm_pack16
1, // a1r5g5b5_unorm_pack16
1, // r8_unorm
1, // r8_snorm
2, // r8_uscaled
2, // r8_sscaled
0, // r8_uint
0, // r8_sint
0, // r8_srgb
1, // r8g8_unorm
1, // r8g8_snorm
2, // r8g8_uscaled
2, // r8g8_sscaled
0, // r8g8_uint
0, // r8g8_sint
0, // r8g8_srgb
1, // r8g8b8_unorm
1, // r8g8b8_snorm
2, // r8g8b8_uscaled
2, // r8g8b8_sscaled
0, // r8g8b8_uint
0, // r8g8b8_sint
0, // r8g8b8_srgb
1, // b8g8r8_unorm
1, // b8g8r8_snorm
2, // b8g8r8_uscaled
2, // b8g8r8_sscaled
0, // b8g8r8_uint
0, // b8g8r8_sint
0, // b8g8r8_srgb
1, // r8g8b8a8_unorm
1, // r8g8b8a8_snorm
2, // r8g8b8a8_uscaled
2, // r8g8b8a8_sscaled
0, // r8g8b8a8_uint
0, // r8g8b8a8_sint
0, // r8g8b8a8_srgb
1, // b8g8r8a8_unorm
1, // b8g8r8a8_snorm
2, // b8g8r8a8_uscaled
2, // b8g8r8a8_sscaled
0, // b8g8r8a8_uint
0, // b8g8r8a8_sint
0, // b8g8r8a8_srgb
1, // a8b8g8r8_unorm_pack32
1, // a8b8g8r8_snorm_pack32
2, // a8b8g8r8_uscaled_pack32
2, // a8b8g8r8_sscaled_pack32
0, // a8b8g8r8_uint_pack32
0, // a8b8g8r8_sint_pack32
0, // a8b8g8r8_srgb_pack32
1, // a2r10g10b10_unorm_pack32
1, // a2r10g10b10_snorm_pack32
2, // a2r10g10b10_uscaled_pack32
2, // a2r10g10b10_sscaled_pack32
0, // a2r10g10b10_uint_pack32
0, // a2r10g10b10_sint_pack32
1, // a2b10g10r10_unorm_pack32
1, // a2b10g10r10_snorm_pack32
2, // a2b10g10r10_uscaled_pack32
2, // a2b10g10r10_sscaled_pack32
0, // a2b10g10r10_uint_pack32
0, // a2b10g10r10_sint_pack32
1, // r16_unorm
1, // r16_snorm
2, // r16_uscaled
2, // r16_sscaled
0, // r16_uint
0, // r16_sint
0, // r16_sfloat
1, // r16g16_unorm
1, // r16g16_snorm
2, // r16g16_uscaled
2, // r16g16_sscaled
0, // r16g16_uint
0, // r16g16_sint
0, // r16g16_sfloat
1, // r16g16b16_unorm
1, // r16g16b16_snorm
2, // r16g16b16_uscaled
2, // r16g16b16_sscaled
0, // r16g16b16_uint
0, // r16g16b16_sint
0, // r16g16b16_sfloat
1, // r16g16b16a16_unorm
1, // r16g16b16a16_snorm
2, // r16g16b16a16_uscaled
2, // r16g16b16a16_sscaled
0, // r16g16b16a16_uint
0, // r16g16b16a16_sint
0, // r16g16b16a16_sfloat
0, // r32_uint
0, // r32_sint
0, // r32_sfloat
0, // r32g32_uint
0, // r32g32_sint
0, // r32g32_sfloat
0, // r32g32b32_uint
0, // r32g32b32_sint
0, // r32g32b32_sfloat
0, // r32g32b32a32_uint
0, // r32g32b32a32_sint
0, // r32g32b32a32_sfloat
0, // r64_uint
0, // r64_sint
0, // r64_sfloat
0, // r64g64_uint
0, // r64g64_sint
0, // r64g64_sfloat
0, // r64g64b64_uint
0, // r64g64b64_sint
0, // r64g64b64_sfloat
0, // r64g64b64a64_uint
0, // r64g64b64a64_sint
0, // r64g64b64a64_sfloat
0, // b10g11r11_ufloat_pack32
0, // e5b9g9r9_ufloat_pack32
1, // d16_unorm
1, // x8_d24_unorm_pack32
0, // d32_sfloat
0, // s8_uint
1, // d16_unorm_s8_uint
1, // d24_unorm_s8_uint
0, // d32_sfloat_s8_uint
1, // bc1_rgb_unorm_block,
0, // bc1_rgb_srgb_block,
1, // bc1_rgba_unorm_block,
0, // bc1_rgba_srgb_block,
1, // bc2_unorm_block,
0, // bc2_srgb_block,
1, // bc3_unorm_block,
0, // bc3_srgb_block,
1, // bc4_unorm_block,
1, // bc4_snorm_block,
1, // bc5_unorm_block,
1, // bc5_snorm_block,
2, // bc6h_ufloat_block,
2, // bc6h_sfloat_block,
1, // bc7_unorm_block,
0, // bc7_srgb_block,
1, // etc2_r8g8b8_unorm_block,
0, // etc2_r8g8b8_srgb_block,
1, // etc2_r8g8b8a1_unorm_block,
0, // etc2_r8g8b8a1_srgb_block,
1, // etc2_r8g8b8a8_unorm_block,
0, // etc2_r8g8b8a8_srgb_block,
1, // eac_r11_unorm_block,
1, // eac_r11_snorm_block,
1, // eac_r11g11_unorm_block,
1, // eac_r11g11_snorm_block,
1, // astc_4x4_unorm_block,
0, // astc_4x4_srgb_block,
1, // astc_5x4_unorm_block,
0, // astc_5x4_srgb_block,
1, // astc_5x5_unorm_block,
0, // astc_5x5_srgb_block,
1, // astc_6x5_unorm_block,
0, // astc_6x5_srgb_block,
1, // astc_6x6_unorm_block,
0, // astc_6x6_srgb_block,
1, // astc_8x5_unorm_block,
0, // astc_8x5_srgb_block,
1, // astc_8x6_unorm_block,
0, // astc_8x6_srgb_block,
1, // astc_8x8_unorm_block,
0, // astc_8x8_srgb_block,
1, // astc_10x5_unorm_block,
0, // astc_10x5_srgb_block,
1, // astc_10x6_unorm_block,
0, // astc_10x6_srgb_block,
1, // astc_10x8_unorm_block,
0, // astc_10x8_srgb_block,
1, // astc_10x10_unorm_block,
0, // astc_10x10_srgb_block,
1, // astc_12x10_unorm_block,
0, // astc_12x10_srgb_block,
1, // astc_12x12_unorm_block,
0, // astc_12x12_srgb_block
};
}
vertex_array::~vertex_array()
{
glDeleteVertexArrays(1, &gl_array_id);
}
namespace gl {
void vertex_array::bind(attribute_location_type location, const vertex_attribute& attribute)
vertex_array::vertex_array(std::span<const vertex_input_attribute> attributes)
{
if (attribute.buffer == nullptr)
{
throw std::invalid_argument("Cannot bind vertex attribute that has a null vertex buffer.");
}
if (attribute.components < 1 || attribute.components > 4)
{
throw std::invalid_argument("Cannot bind vertex attribute that has an unsupported number of components (" + std::to_string(attribute.components) + ")");
}
m_attributes[location] = attribute;
m_attributes.assign(attributes.begin(), attributes.end());
GLenum gl_type = vertex_attribute_type_lut[static_cast<std::size_t>(attribute.type)];
glBindVertexArray(gl_array_id);
glBindBuffer(GL_ARRAY_BUFFER, attribute.buffer->gl_buffer_id);
glCreateVertexArrays(1, &m_gl_named_array);
if (attribute.normalized || gl_type == GL_FLOAT || gl_type == GL_HALF_FLOAT || gl_type == GL_DOUBLE)
for (const auto& attribute: m_attributes)
{
glVertexAttribPointer
(
static_cast<GLuint>(location),
static_cast<GLint>(attribute.components),
gl_type,
attribute.normalized ? GL_TRUE : GL_FALSE,
static_cast<GLsizei>(attribute.stride),
reinterpret_cast<const GLvoid*>(attribute.offset)
);
}
else
{
glVertexAttribIPointer
// Enable attribute
glEnableVertexArrayAttrib(m_gl_named_array, static_cast<GLuint>(attribute.location));
// Set attribute vertex binding index
glVertexArrayAttribBinding
(
static_cast<GLuint>(location),
static_cast<GLint>(attribute.components),
gl_type,
static_cast<GLsizei>(attribute.stride),
reinterpret_cast<const GLvoid*>(attribute.offset)
);
m_gl_named_array,
static_cast<GLuint>(attribute.location),
static_cast<GLuint>(attribute.binding)
);
const auto format_index = std::to_underlying(attribute.format);
const auto gl_base_format = gl_format_lut[format_index][1];
const auto gl_type = gl_format_lut[format_index][2];
const auto format_scale = format_scale_lut[format_index];
// Determine number of values per vertex
GLint gl_size;
switch (gl_base_format)
{
case GL_RED:
case GL_RED_INTEGER:
case GL_DEPTH_COMPONENT:
case GL_STENCIL_INDEX:
gl_size = 1;
break;
case GL_RG:
case GL_RG_INTEGER:
case GL_DEPTH_STENCIL:
gl_size = 2;
break;
case GL_BGR:
case GL_BGR_INTEGER:
case GL_RGB:
case GL_RGB_INTEGER:
gl_size = 3;
break;
case GL_BGRA:
case GL_BGRA_INTEGER:
case GL_RGBA:
case GL_RGBA_INTEGER:
gl_size = 4;
break;
default:
gl_size = 0;
break;
}
if (gl_size == 0 || gl_type == 0)
{
throw std::invalid_argument("Vertex input attribute has unsupported format.");
}
if (format_scale > 0 || gl_type == GL_FLOAT || gl_type == GL_HALF_FLOAT)
{
glVertexArrayAttribFormat
(
m_gl_named_array,
static_cast<GLuint>(attribute.location),
gl_size,
gl_type,
(format_scale == 1),
static_cast<GLuint>(attribute.offset)
);
}
else if (gl_type == GL_DOUBLE)
{
glVertexArrayAttribLFormat
(
m_gl_named_array,
static_cast<GLuint>(attribute.location),
gl_size,
gl_type,
static_cast<GLuint>(attribute.offset)
);
}
else
{
glVertexArrayAttribIFormat
(
m_gl_named_array,
static_cast<GLuint>(attribute.location),
gl_size,
gl_type,
static_cast<GLuint>(attribute.offset)
);
}
}
glEnableVertexAttribArray(static_cast<GLuint>(location));
}
void vertex_array::unbind(attribute_location_type location)
vertex_array::vertex_array()
{
glCreateVertexArrays(1, &m_gl_named_array);
}
vertex_array::~vertex_array()
{
if (auto it = m_attributes.find(location); it != m_attributes.end())
{
glBindVertexArray(gl_array_id);
glDisableVertexAttribArray(static_cast<GLuint>(location));
m_attributes.erase(it);
}
else
{
throw std::invalid_argument("Non-existent vertex attribute cannot be unbound.");
}
glDeleteVertexArrays(1, &m_gl_named_array);
}
} // namespace gl

+ 23
- 46
src/engine/gl/vertex-array.hpp View File

@ -20,72 +20,49 @@
#ifndef ANTKEEPER_GL_VERTEX_ARRAY_HPP
#define ANTKEEPER_GL_VERTEX_ARRAY_HPP
#include <cstdint>
#include <unordered_map>
#include <engine/gl/vertex-attribute.hpp>
#include <engine/gl/vertex-input-attribute.hpp>
#include <span>
#include <vector>
namespace gl {
class rasterizer;
class vertex_buffer;
/**
* Vertex array object (VAO), which describes how vertex attributes are stored in vertex buffer objects (VBOs).
*
* @see gl::vertex_attribute
* @see gl::vertex_buffer
* Vertex arrays describes how vertex input attributes are stored in vertex buffers.
*/
class vertex_array
{
public:
/// Vertex attribute binding location type.
typedef unsigned int attribute_location_type;
/// Maps vertex attribute to binding locations.
typedef std::unordered_map<attribute_location_type, vertex_attribute> attribute_map_type;
/// Constructs a vertex array.
vertex_array();
/// Destructs a vertex array.
~vertex_array();
vertex_array(const vertex_array&) = delete;
vertex_array(vertex_array&&) = delete;
vertex_array& operator=(const vertex_array&) = delete;
vertex_array& operator=(vertex_array&&) = delete;
/**
* Binds a vertex attribute to the vertex array.
* Constructs a vertex array.
*
* @param location Location to which the vertex attribute should be bound.
* @param attribute Vertex attribute to bind.
* @param attributes Vertex input attributes.
*
* @except std::invalid_argument Cannot bind vertex attribute that has a null vertex buffer.
* @except std::invalid_argument Cannot bind vertex attribute that has an unsupported number of components.
* @except std::invalid_argument Vertex input attribute has unsupported format.
*/
void bind(attribute_location_type location, const vertex_attribute& attribute);
/// @{
explicit vertex_array(std::span<const vertex_input_attribute> attributes);
vertex_array();
/// @}
/**
* Unbinds a vertex attribute from the vertex array.
*
* @param location Location of the vertex attribute to unbind.
*
* @except std::invalid_argument Non-existent vertex attribute cannot be unbound.
*/
void unbind(attribute_location_type location);
/// Destructs a vertex array.
~vertex_array();
/// Returns a const reference to the map of vertex attributes bound to this vertex array.
[[nodiscard]] inline const attribute_map_type& attributes() const noexcept
/// Returns the vertex array's vertex input attributes.
[[nodiscard]] inline constexpr const std::vector<vertex_input_attribute>& attributes() const noexcept
{
return m_attributes;
}
vertex_array(const vertex_array&) = delete;
vertex_array(vertex_array&&) = delete;
vertex_array& operator=(const vertex_array&) = delete;
vertex_array& operator=(vertex_array&&) = delete;
private:
friend class rasterizer;
friend class pipeline;
unsigned int gl_array_id{0};
attribute_map_type m_attributes;
std::vector<vertex_input_attribute> m_attributes;
unsigned int m_gl_named_array{0};
};
} // namespace gl

+ 0
- 72
src/engine/gl/vertex-attribute.hpp View File

@ -1,72 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_VERTEX_ATTRIBUTE_HPP
#define ANTKEEPER_GL_VERTEX_ATTRIBUTE_HPP
#include <cstddef>
#include <cstdint>
namespace gl {
class vertex_buffer;
enum class vertex_attribute_type: std::uint8_t
{
int_8,
uint_8,
int_16,
uint_16,
int_32,
uint_32,
float_16,
float_32,
float_64
};
/**
* Describes a vertex attribute within a vertex buffer.
*
* @see gl::vertex_buffer
* @see gl::vertex_array
*/
struct vertex_attribute
{
/// Pointer to the vertex buffer containing vertex attribute data.
const vertex_buffer* buffer{nullptr};
/// Offset to the first component of the first instance of this attribute in the vertex buffer, in bytes.
std::size_t offset{0};
/// Number of bytes between consecutive instances of this attribute in the vertex buffer. A value of `0` indicates attribute instances are tightly packed.
std::size_t stride{0};
/// Data type of each component in the attribute.
vertex_attribute_type type{0};
/// Number of components per attribute instance. Supported values are `1`, `2`, `3`, and `4`.
std::uint8_t components{0};
/// `true` if fixed point data should be normalized, `false` otherwise.
bool normalized{};
};
} // namespace gl
#endif // ANTKEEPER_GL_VERTEX_ATTRIBUTE_HPP

+ 64
- 43
src/engine/gl/vertex-buffer.cpp View File

@ -19,22 +19,26 @@
#include <engine/gl/vertex-buffer.hpp>
#include <stdexcept>
#include <glad/glad.h>
#include <glad/gl.h>
#include <utility>
namespace gl {
static constexpr GLenum buffer_usage_lut[] =
namespace
{
GL_STREAM_DRAW,
GL_STREAM_READ,
GL_STREAM_COPY,
GL_STATIC_DRAW,
GL_STATIC_READ,
GL_STATIC_COPY,
GL_DYNAMIC_DRAW,
GL_DYNAMIC_READ,
GL_DYNAMIC_COPY
};
static constexpr GLenum buffer_usage_lut[] =
{
GL_STREAM_DRAW,
GL_STREAM_READ,
GL_STREAM_COPY,
GL_STATIC_DRAW,
GL_STATIC_READ,
GL_STATIC_COPY,
GL_DYNAMIC_DRAW,
GL_DYNAMIC_READ,
GL_DYNAMIC_COPY
};
}
vertex_buffer::vertex_buffer(buffer_usage usage, std::size_t size, std::span<const std::byte> data):
m_usage{usage},
@ -45,23 +49,55 @@ vertex_buffer::vertex_buffer(buffer_usage usage, std::size_t size, std::span
// Bounds check
if (data.size() < size)
{
throw std::out_of_range("Vertex buffer creation operation exceeded data bounds.");
throw std::out_of_range("Vertex buffer construct operation exceeded data bounds.");
}
}
const GLenum gl_usage = buffer_usage_lut[static_cast<std::size_t>(m_usage)];
glGenBuffers(1, &gl_buffer_id);
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id);
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(m_size), data.empty() ? nullptr : data.data(), gl_usage);
const GLenum gl_usage = buffer_usage_lut[std::to_underlying(m_usage)];
glCreateBuffers(1, &m_gl_named_buffer);
glNamedBufferData(m_gl_named_buffer, static_cast<GLsizeiptr>(m_size), data.empty() ? nullptr : data.data(), gl_usage);
}
vertex_buffer::vertex_buffer(const vertex_buffer& buffer):
vertex_buffer(buffer.m_usage, buffer.m_size)
{
copy(buffer, m_size);
}
vertex_buffer::vertex_buffer():
vertex_buffer(buffer_usage::static_draw, 0)
vertex_buffer::vertex_buffer(vertex_buffer&& buffer):
m_gl_named_buffer{std::exchange(buffer.m_gl_named_buffer, 0)},
m_usage{std::move(buffer.m_usage)},
m_size{std::exchange(buffer.m_size, 0)}
{}
vertex_buffer::~vertex_buffer()
{
glDeleteBuffers(1, &gl_buffer_id);
if (m_gl_named_buffer)
{
glDeleteBuffers(1, &m_gl_named_buffer);
}
}
vertex_buffer& vertex_buffer::operator=(const vertex_buffer& buffer)
{
repurpose(buffer.m_usage, buffer.m_size);
copy(buffer, m_size);
return *this;
}
vertex_buffer& vertex_buffer::operator=(vertex_buffer&& buffer)
{
if (m_gl_named_buffer)
{
glDeleteBuffers(1, &m_gl_named_buffer);
}
m_gl_named_buffer = std::exchange(buffer.m_gl_named_buffer, 0);
m_usage = std::move(buffer.m_usage);
m_size = std::exchange(buffer.m_size, 0);
return *this;
}
void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, std::span<const std::byte> data)
@ -78,22 +114,11 @@ void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, std::span
m_usage = usage;
m_size = size;
const GLenum gl_usage = buffer_usage_lut[static_cast<std::size_t>(m_usage)];
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id);
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(m_size), data.empty() ? nullptr : data.data(), gl_usage);
}
void vertex_buffer::repurpose(buffer_usage usage, std::span<const std::byte> data)
{
repurpose(usage, m_size, data);
}
void vertex_buffer::resize(std::size_t size, std::span<const std::byte> data)
{
repurpose(m_usage, size, data);
const auto gl_usage = buffer_usage_lut[std::to_underlying(m_usage)];
glNamedBufferData(m_gl_named_buffer, static_cast<GLsizeiptr>(m_size), data.empty() ? nullptr : data.data(), gl_usage);
}
void vertex_buffer::write(std::span<const std::byte> data, std::size_t offset)
void vertex_buffer::write(std::size_t offset, std::span<const std::byte> data)
{
// Ignore empty write operations
if (data.empty())
@ -107,13 +132,12 @@ void vertex_buffer::write(std::span data, std::size_t offset)
throw std::out_of_range("Vertex buffer write operation exceeded buffer bounds.");
}
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id);
glBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(data.size()), data.data());
glNamedBufferSubData(m_gl_named_buffer, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(data.size()), data.data());
}
void vertex_buffer::read(std::span<std::byte> data, std::size_t offset) const
void vertex_buffer::read(std::size_t offset, std::span<std::byte> data) const
{
// Abort empty read operations
// Ignore empty read operations
if (data.empty())
{
return;
@ -125,8 +149,7 @@ void vertex_buffer::read(std::span data, std::size_t offset) const
throw std::out_of_range("Vertex buffer read operation exceeded buffer bounds.");
}
glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id);
glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(data.size()), data.data());
glGetNamedBufferSubData(m_gl_named_buffer, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(data.size()), data.data());
}
void vertex_buffer::copy(const vertex_buffer& read_buffer, std::size_t copy_size, std::size_t read_offset, std::size_t write_offset)
@ -141,9 +164,7 @@ void vertex_buffer::copy(const vertex_buffer& read_buffer, std::size_t copy_size
throw std::out_of_range("Vertex buffer copy operation exceeded write buffer bounds.");
}
glBindBuffer(GL_COPY_READ_BUFFER, read_buffer.gl_buffer_id);
glBindBuffer(GL_COPY_WRITE_BUFFER, gl_buffer_id);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, static_cast<GLintptr>(read_offset), static_cast<GLintptr>(write_offset), static_cast<GLsizeiptr>(copy_size));
glCopyNamedBufferSubData(read_buffer.m_gl_named_buffer, m_gl_named_buffer, static_cast<GLintptr>(read_offset), static_cast<GLintptr>(write_offset), static_cast<GLsizeiptr>(copy_size));
}
} // namespace gl

+ 81
- 18
src/engine/gl/vertex-buffer.hpp View File

@ -20,14 +20,12 @@
#ifndef ANTKEEPER_GL_VERTEX_BUFFER_HPP
#define ANTKEEPER_GL_VERTEX_BUFFER_HPP
#include <cstddef>
#include <engine/gl/buffer-usage.hpp>
#include <cstddef>
#include <span>
namespace gl {
class vertex_array;
/**
* Vertex buffer object (VBO).
*/
@ -41,22 +39,54 @@ public:
* @param size Buffer size, in bytes.
* @param data Buffer data. If empty, buffer data will not be set.
*
* @except std::out_of_range Vertex buffer creation operation exceeded data bounds.
* @except std::out_of_range Vertex buffer construct operation exceeded data bounds.
*/
/// @{
vertex_buffer(buffer_usage usage, std::size_t size, std::span<const std::byte> data = {});
inline explicit vertex_buffer(buffer_usage usage, std::span<const std::byte> data = {}):
vertex_buffer(usage, data.size(), data)
{}
inline vertex_buffer():
vertex_buffer(buffer_usage::static_draw, 0)
{}
/// @}
/**
* Constructs a copy of a vertex buffer.
*
* @param buffer Buffer to copy.
*/
vertex_buffer(const vertex_buffer& buffer);
/**
* Constructs an empty vertex buffer.
* Move-constructs a vertex buffer.
*
* @param buffer Buffer to move.
*/
vertex_buffer();
vertex_buffer(vertex_buffer&& buffer);
/// Destroys a vertex buffer.
~vertex_buffer();
vertex_buffer(const vertex_buffer&) = delete;
vertex_buffer(vertex_buffer&&) = delete;
vertex_buffer& operator=(const vertex_buffer&) = delete;
vertex_buffer& operator=(vertex_buffer&&) = delete;
/**
* Copies another vertex buffer.
*
* @param buffer Buffer to copy.
*
* @return Reference to this vertex buffer.
*/
vertex_buffer& operator=(const vertex_buffer& buffer);
/**
* Moves another vertex buffer into this buffer.
*
* @param buffer Buffer to move.
*
* @return Reference to this vertex buffer.
*/
vertex_buffer& operator=(vertex_buffer&& buffer);
/**
* Repurposes the vertex buffer, changing its usage hint, size, and updating its data.
@ -69,7 +99,16 @@ public:
*/
/// @{
void repurpose(buffer_usage usage, std::size_t size, std::span<const std::byte> data = {});
void repurpose(buffer_usage usage, std::span<const std::byte> data = {});
inline void repurpose(buffer_usage usage, std::span<const std::byte> data)
{
repurpose(usage, data.size(), data);
}
inline void repurpose(buffer_usage usage)
{
repurpose(usage, m_size, {});
}
/// @}
/**
@ -80,7 +119,17 @@ public:
*
* @except std::out_of_range Vertex buffer resize operation exceeded data bounds.
*/
void resize(std::size_t size, std::span<const std::byte> data = {});
/// @{
inline void resize(std::size_t size, std::span<const std::byte> data = {})
{
repurpose(m_usage, size, data);
}
inline void resize(std::span<const std::byte> data)
{
repurpose(m_usage, data.size(), data);
}
/// @}
/**
* Writes data into the vertex buffer.
@ -90,7 +139,14 @@ public:
*
* @except std::out_of_range Vertex buffer write operation exceeded buffer bounds.
*/
void write(std::span<const std::byte> data, std::size_t offset = 0);
/// @{
void write(std::size_t offset, std::span<const std::byte> data);
inline void write(std::span<const std::byte> data)
{
write(0, data);
}
/// @}
/**
* Reads a subset of the buffer's data from the GL and returns it to the application.
@ -100,7 +156,14 @@ public:
*
* @except std::out_of_range Vertex buffer read operation exceeded buffer bounds.
*/
void read(std::span<std::byte> data, std::size_t offset = 0) const;
/// @{
void read(std::size_t offset, std::span<std::byte> data) const;
inline void read(std::span<std::byte> data) const
{
read(0, data);
}
/// @}
/**
* Copies a subset of another vertex buffer's data into this vertex buffer.
@ -116,21 +179,21 @@ public:
void copy(const vertex_buffer& read_buffer, std::size_t copy_size, std::size_t read_offset = 0, std::size_t write_offset = 0);
/// Returns the size of the buffer's data, in bytes.
[[nodiscard]] inline std::size_t size() const noexcept
[[nodiscard]] inline constexpr std::size_t size() const noexcept
{
return m_size;
}
/// Return's the buffer's usage hint.
[[nodiscard]] inline buffer_usage usage() const noexcept
[[nodiscard]] inline constexpr buffer_usage usage() const noexcept
{
return m_usage;
}
private:
friend class vertex_array;
friend class pipeline;
unsigned int gl_buffer_id{0};
unsigned int m_gl_named_buffer{0};
buffer_usage m_usage{buffer_usage::static_draw};
std::size_t m_size{0};
};

src/engine/gl/cube-map-layout.hpp → src/engine/gl/vertex-input-attribute.hpp View File

@ -17,35 +17,30 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_CUBE_MAP_LAYOUT_HPP
#define ANTKEEPER_GL_CUBE_MAP_LAYOUT_HPP
#ifndef ANTKEEPER_GL_VERTEX_INPUT_ATTRIBUTE_HPP
#define ANTKEEPER_GL_VERTEX_INPUT_ATTRIBUTE_HPP
#include <engine/gl/format.hpp>
#include <cstdint>
namespace gl {
/// Cube map layout types.
enum class cube_map_layout: std::uint8_t
/// Vertex input attribute.
struct vertex_input_attribute
{
/// Faces are stored consecutively in a vertical column.
column = 1,
/// Shader input location number for this attribute.
std::uint32_t location{0};
/// Faces are stored consecutively in a horizontal row.
row,
/// Binding number which this attribute takes its data from.
std::uint32_t binding{0};
/// Faces are stored in a vertical cross.
vertical_cross,
/// Size and type of the vertex attribute data.
gl::format format{gl::format::undefined};
/// 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
/// Byte offset of this attribute relative to the start of an element in the vertex input binding.
std::uint32_t offset{0};
};
} // namespace gl
#endif // ANTKEEPER_GL_CUBE_MAP_LAYOUT_HPP
#endif // ANTKEEPER_GL_VERTEX_INPUT_ATTRIBUTE_HPP

+ 43
- 0
src/engine/gl/vertex-input-binding.hpp View File

@ -0,0 +1,43 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_VERTEX_INPUT_BINDING_HPP
#define ANTKEEPER_GL_VERTEX_INPUT_BINDING_HPP
#include <engine/gl/vertex-input-rate.hpp>
#include <cstdint>
namespace gl {
/// Vertex input binding.
struct vertex_input_binding
{
/// Binding number that this structure describes.
std::uint32_t binding{0};
/// Byte stride between consecutive elements within the buffer.
std::uint32_t stride{0};
/// Specifies whether vertex attribute addressing is a function of the vertex index or of the instance index.
vertex_input_rate input_rate{vertex_input_rate::vertex};
};
} // namespace gl
#endif // ANTKEEPER_GL_VERTEX_INPUT_BINDING_HPP

+ 39
- 0
src/engine/gl/vertex-input-rate.hpp View File

@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_VERTEX_INPUT_RATE_HPP
#define ANTKEEPER_GL_VERTEX_INPUT_RATE_HPP
#include <cstdint>
namespace gl {
/// Rate at which vertex attributes are pulled from buffers.
enum class vertex_input_rate: std::uint8_t
{
/// Vertex attribute addressing is a function of the vertex index.
vertex,
/// Vertex attribute addressing is a function of the instance index.
instance
};
} // namespace gl
#endif // ANTKEEPER_GL_VERTEX_INPUT_RATE_HPP

+ 53
- 0
src/engine/gl/viewport.hpp View File

@ -0,0 +1,53 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GL_VIEWPORT_HPP
#define ANTKEEPER_GL_VIEWPORT_HPP
#include <cstdint>
namespace gl {
/**
* Viewport position, dimensions, and depth range.
*/
struct viewport
{
/// X-coordinate of the viewport's lower left corner.
float x{0.0f};
/// Y-coordinate of the viewport's lower left corner.
float y{0.0f};
/// Width of the viewport.
float width{0.0f};
/// Height of the viewport.
float height{0.0f};
/// Minimum depth range of the viewport.
float min_depth{0.0f};
/// Maximum depth range of the viewport.
float max_depth{1.0f};
};
} // namespace gl
#endif // ANTKEEPER_GL_VIEWPORT_HPP

+ 44
- 0
src/engine/math/vector.hpp View File

@ -22,6 +22,7 @@
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <cmath>
#include <concepts>
#include <format>
@ -1788,6 +1789,49 @@ inline constexpr vector& operator/=(vector& x, T y) noexcept
}
/// @}
/**
* Tests two vector for equality
*
* @param x First value.
* @param y Second value.
*
* @return `true` if the vectors are identical, `false` otherwise.
*/
template <class T, std::size_t N>
inline constexpr bool operator==(const vector<T, N>& x, const vector<T, N>& y) noexcept
{
// if consteval
// {
for (std::size_t i = 0; i < N; ++i)
{
if (x[i] != y[i])
{
return false;
}
}
return true;
// }
// else
// {
// !std::memcmp(x.data(), y.data(), N * sizeof(T));
// }
}
/**
* Tests two vector for inequality
*
* @param x First value.
* @param y Second value.
*
* @return `false` if the vectors are identical, `true` otherwise.
*/
template <class T, std::size_t N>
inline constexpr bool operator!=(const vector<T, N>& x, const vector<T, N>& y) noexcept
{
return !(x == y);
}
} // namespace operators
} // namespace math

+ 1
- 4
src/engine/render/material-variable.hpp View File

@ -22,10 +22,7 @@
#include <engine/math/vector.hpp>
#include <engine/math/matrix.hpp>
#include <engine/gl/texture-1d.hpp>
#include <engine/gl/texture-2d.hpp>
#include <engine/gl/texture-3d.hpp>
#include <engine/gl/texture-cube.hpp>
#include <engine/gl/texture.hpp>
#include <engine/render/material-variable-type.hpp>
#include <memory>
#include <vector>

+ 5
- 5
src/engine/render/material.cpp View File

@ -23,7 +23,7 @@
#include <engine/resources/resource-manager.hpp>
#include <engine/render/material-flags.hpp>
#include <engine/utility/json.hpp>
#include <engine/utility/hash/combine.hpp>
#include <engine/utility/hash/hash-combine.hpp>
#include <utility>
#include <type_traits>
#include <string>
@ -115,10 +115,10 @@ void material::rehash() noexcept
m_hash = shader_template->hash();
}
m_hash = hash::combine(m_hash, std::hash<bool>{}(two_sided));
m_hash = hash::combine(m_hash, std::hash<material_blend_mode>{}(blend_mode));
m_hash = hash::combine(m_hash, std::hash<material_shadow_mode>{}(shadow_mode));
m_hash = hash::combine(m_hash, std::hash<std::uint32_t>{}(flags));
m_hash = hash_combine(m_hash, std::hash<bool>{}(two_sided));
m_hash = hash_combine(m_hash, std::hash<material_blend_mode>{}(blend_mode));
m_hash = hash_combine(m_hash, std::hash<material_shadow_mode>{}(shadow_mode));
m_hash = hash_combine(m_hash, std::hash<std::uint32_t>{}(flags));
}
} // namespace render

+ 143
- 94
src/engine/render/model.cpp View File

@ -20,32 +20,20 @@
#include <engine/render/model.hpp>
#include <engine/resources/resource-loader.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/render/vertex-attribute.hpp>
#include <engine/gl/vertex-attribute.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/render/vertex-attribute-location.hpp>
#include <engine/math/numbers.hpp>
#include <engine/utility/hash/fnv1a.hpp>
#include <bit>
#include <cstdint>
namespace render {
model::model()
{
vertex_array = std::make_shared<gl::vertex_array>();
vertex_buffer = std::make_shared<gl::vertex_buffer>();
}
} // namespace render
inline constexpr std::uint16_t vertex_attribute_position = 0b0000000000000001;
inline constexpr std::uint16_t vertex_attribute_uv = 0b0000000000000010;
inline constexpr std::uint16_t vertex_attribute_normal = 0b0000000000000100;
inline constexpr std::uint16_t vertex_attribute_tangent = 0b0000000000001000;
inline constexpr std::uint16_t vertex_attribute_color = 0b0000000000010000;
inline constexpr std::uint16_t vertex_attribute_bone = 0b0000000000100000;
inline constexpr std::uint16_t vertex_attribute_barycentric = 0b0000000001000000;
inline constexpr std::uint16_t vertex_attribute_bone_index = 0b0000000000100000;
inline constexpr std::uint16_t vertex_attribute_bone_weight = 0b0000000001000000;
inline constexpr std::uint16_t vertex_attribute_morph_target = 0b0000000010000000;
inline constexpr std::uint16_t vertex_attribute_index = 0b0000000100000000;
template <>
std::unique_ptr<render::model> resource_loader<render::model>::load(::resource_manager& resource_manager, deserialize_context& ctx)
@ -54,9 +42,9 @@ std::unique_ptr resource_loader::load(::resource_m
std::uint16_t vertex_format_flags = 0;
ctx.read16<std::endian::little>(reinterpret_cast<std::byte*>(&vertex_format_flags), 1);
// Read bone per vertex (if any)
// Read bones per vertex (if any)
std::uint8_t bones_per_vertex = 0;
if (vertex_format_flags & vertex_attribute_bone)
if ((vertex_format_flags & vertex_attribute_bone_index) || (vertex_format_flags & vertex_attribute_bone_weight))
{
ctx.read8(reinterpret_cast<std::byte*>(&bones_per_vertex), 1);
}
@ -65,49 +53,48 @@ std::unique_ptr resource_loader::load(::resource_m
std::uint32_t vertex_count = 0;
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&vertex_count), 1);
// Determine vertex size
std::size_t vertex_size = 0;
// Determine vertex stride
std::size_t vertex_stride = 0;
if (vertex_format_flags & vertex_attribute_position)
{
vertex_size += sizeof(float) * 3;
vertex_stride += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_uv)
{
vertex_size += sizeof(float) * 2;
vertex_stride += sizeof(float) * 2;
}
if (vertex_format_flags & vertex_attribute_normal)
{
vertex_size += sizeof(float) * 3;
vertex_stride += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_tangent)
{
vertex_size += sizeof(float) * 4;
vertex_stride += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_color)
{
vertex_size += sizeof(float) * 4;
vertex_stride += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_bone)
if (vertex_format_flags & vertex_attribute_bone_index)
{
vertex_size += sizeof(std::uint16_t) * bones_per_vertex;
vertex_size += sizeof(float) * bones_per_vertex;
vertex_stride += sizeof(std::uint16_t) * bones_per_vertex;
}
if (vertex_format_flags & vertex_attribute_barycentric)
if (vertex_format_flags & vertex_attribute_bone_weight)
{
vertex_size += sizeof(float) * 3;
vertex_stride += sizeof(float) * bones_per_vertex;
}
if (vertex_format_flags & vertex_attribute_morph_target)
{
vertex_size += sizeof(float) * 3;
vertex_stride += sizeof(float) * 3;
}
// Allocate vertex data
std::vector<std::byte> vertex_data(vertex_count * vertex_size);
std::vector<std::byte> vertex_data(vertex_count * vertex_stride);
// Read vertices
if constexpr (std::endian::native == std::endian::little)
{
ctx.read8(vertex_data.data(), vertex_count * vertex_size);
ctx.read8(vertex_data.data(), vertex_count * vertex_stride);
}
else
{
@ -139,18 +126,15 @@ std::unique_ptr resource_loader::load(::resource_m
ctx.read32<std::endian::little>(vertex_data_offset, 4);
vertex_data_offset += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_bone)
if (vertex_format_flags & vertex_attribute_bone_index)
{
ctx.read32<std::endian::little>(vertex_data_offset, bones_per_vertex);
ctx.read32<std::endian::little>(vertex_data_offset, bones_per_vertex);
ctx.read16<std::endian::little>(vertex_data_offset, bones_per_vertex);
vertex_data_offset += sizeof(std::uint16_t) * bones_per_vertex;
vertex_data_offset += sizeof(float) * bones_per_vertex;
}
if (vertex_format_flags & vertex_attribute_barycentric)
if (vertex_format_flags & vertex_attribute_bone_weight)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
ctx.read32<std::endian::little>(vertex_data_offset, bones_per_vertex);
vertex_data_offset += sizeof(float) * bones_per_vertex;
}
if (vertex_format_flags & vertex_attribute_morph_target)
{
@ -163,81 +147,146 @@ std::unique_ptr resource_loader::load(::resource_m
// Allocate model
std::unique_ptr<render::model> model = std::make_unique<render::model>();
// Resize model VBO and upload vertex data
gl::vertex_buffer& vbo = *model->get_vertex_buffer();
vbo.resize(vertex_data.size(), vertex_data);
// Build model vertex buffer
model->get_vertex_buffer() = std::make_shared<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data);
model->set_vertex_offset(0);
model->set_vertex_stride(vertex_stride);
// Free vertex data
vertex_data.clear();
// Bind vertex attributes to VAO
gl::vertex_array& vao = *model->get_vertex_array();
gl::vertex_attribute attribute;
attribute.buffer = &vbo;
attribute.offset = 0;
attribute.stride = vertex_size;
// Build vertex input attributes
std::vector<gl::vertex_input_attribute> attributes(std::popcount(vertex_format_flags));
std::uint32_t vertex_offset = 0;
std::uint32_t attribute_index = 0;
if (vertex_format_flags & vertex_attribute_position)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::position, attribute);
attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::position;
attribute.binding = 0;
attribute.format = gl::format::r32g32b32_sfloat;
attribute.offset = vertex_offset;
vertex_offset += 3 * sizeof(float);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_uv)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 2;
vao.bind(render::vertex_attribute::uv, attribute);
attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::uv;
attribute.binding = 0;
attribute.format = gl::format::r32g32_sfloat;
attribute.offset = vertex_offset;
vertex_offset += 2 * sizeof(float);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_normal)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::normal, attribute);
attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::normal;
attribute.binding = 0;
attribute.format = gl::format::r32g32b32_sfloat;
attribute.offset = vertex_offset;
vertex_offset += 3 * sizeof(float);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_tangent)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 4;
vao.bind(render::vertex_attribute::tangent, attribute);
attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::tangent;
attribute.binding = 0;
attribute.format = gl::format::r32g32b32a32_sfloat;
attribute.offset = vertex_offset;
vertex_offset += 4 * sizeof(float);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_color)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 4;
vao.bind(render::vertex_attribute::color, attribute);
attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::color;
attribute.binding = 0;
attribute.format = gl::format::r32g32b32a32_sfloat;
attribute.offset = vertex_offset;
vertex_offset += 4 * sizeof(float);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_bone)
if (vertex_format_flags & vertex_attribute_bone_index)
{
attribute.type = gl::vertex_attribute_type::uint_16;
attribute.components = bones_per_vertex;
vao.bind(render::vertex_attribute::bone_index, attribute);
attribute.offset += sizeof(std::uint16_t) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::bone_index;
attribute.binding = 0;
switch (bones_per_vertex)
{
case 1:
attribute.format = gl::format::r16_uint;
break;
case 2:
attribute.format = gl::format::r16g16_uint;
break;
case 3:
attribute.format = gl::format::r16g16b16_uint;
break;
case 4:
attribute.format = gl::format::r16g16b16a16_uint;
break;
default:
attribute.format = gl::format::undefined;
break;
}
attribute.offset = vertex_offset;
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = bones_per_vertex;
vao.bind(render::vertex_attribute::bone_weight, attribute);
attribute.offset += sizeof(float) * attribute.components;
vertex_offset += bones_per_vertex * sizeof(std::uint16_t);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_barycentric)
if (vertex_format_flags & vertex_attribute_bone_weight)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::barycentric, attribute);
attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::bone_weight;
attribute.binding = 0;
switch (bones_per_vertex)
{
case 1:
attribute.format = gl::format::r32_sfloat;
break;
case 2:
attribute.format = gl::format::r32g32_sfloat;
break;
case 3:
attribute.format = gl::format::r32g32b32_sfloat;
break;
case 4:
attribute.format = gl::format::r32g32b32a32_sfloat;
break;
default:
attribute.format = gl::format::undefined;
break;
}
attribute.offset = vertex_offset;
vertex_offset += bones_per_vertex * sizeof(float);
++attribute_index;
}
if (vertex_format_flags & vertex_attribute_morph_target)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::target, attribute);
//attribute.offset += sizeof(float) * attribute.components;
auto& attribute = attributes[attribute_index];
attribute.location = render::vertex_attribute_location::target;
attribute.binding = 0;
attribute.format = gl::format::r32g32b32_sfloat;
attribute.offset = vertex_offset;
// vertex_offset += 3 * sizeof(float);
// ++attribute_index;
}
// Build model vertex array
model->get_vertex_array() = std::make_shared<gl::vertex_array>(attributes);
// Read model bounds
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&model->get_bounds()), 6);
@ -262,14 +311,14 @@ std::unique_ptr resource_loader::load(::resource_m
// Generate group ID by hashing material name
group.id = hash::fnv1a32<char>(material_name);
// Set group drawing mode
group.drawing_mode = gl::drawing_mode::triangles;
// Set group primitive topology
group.primitive_topology = gl::primitive_topology::triangle_list;
// Read offset to index of first vertex
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&group.start_index), 1);
// Read index of first vertex
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&group.first_vertex), 1);
// Read vertex count
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&group.index_count), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&group.vertex_count), 1);
// Slugify material filename
std::string material_filename = material_name + ".mtl";
@ -280,7 +329,7 @@ std::unique_ptr resource_loader::load(::resource_m
}
// Read skeleton
if (vertex_format_flags & vertex_attribute_bone)
if ((vertex_format_flags & vertex_attribute_bone_index) || (vertex_format_flags & vertex_attribute_bone_weight))
{
::skeleton& skeleton = model->get_skeleton();

+ 52
- 23
src/engine/render/model.hpp View File

@ -22,7 +22,7 @@
#include <engine/animation/skeleton.hpp>
#include <engine/geom/primitives/box.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/primitive-topology.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/render/material.hpp>
@ -39,11 +39,11 @@ namespace render {
*/
struct model_group
{
hash::fnv1a32_t id;
gl::drawing_mode drawing_mode;
std::uint32_t start_index;
std::uint32_t index_count;
std::shared_ptr<material> material;
hash::fnv1a32_t id{};
gl::primitive_topology primitive_topology{gl::primitive_topology::triangle_list};
std::uint32_t first_vertex{};
std::uint32_t vertex_count{};
std::shared_ptr<render::material> material;
};
/**
@ -56,9 +56,24 @@ public:
using aabb_type = geom::box<float>;
/**
* Constructs a model.
* Sets the byte offset to the first vertex in the vertex buffer.
*
* @param offset Byte offset into the vertex buffer.
*/
model();
inline void set_vertex_offset(std::size_t offset) noexcept
{
m_vertex_offset = offset;
}
/**
* Sets the byte stride between consecutive elements within the vertex buffer.
*
* @param stride Byte stride between consecutive elements within the vertex buffer.
*/
inline void set_vertex_stride(std::size_t stride) noexcept
{
m_vertex_stride = stride;
}
/**
* Returns the vertex array associated with this model.
@ -66,11 +81,11 @@ public:
/// @{
[[nodiscard]] inline const std::shared_ptr<gl::vertex_array>& get_vertex_array() const noexcept
{
return vertex_array;
return m_vertex_array;
}
[[nodiscard]] inline std::shared_ptr<gl::vertex_array>& get_vertex_array() noexcept
{
return vertex_array;
return m_vertex_array;
}
/// @}
@ -80,25 +95,37 @@ public:
/// @{
[[nodiscard]] inline const std::shared_ptr<gl::vertex_buffer>& get_vertex_buffer() const noexcept
{
return vertex_buffer;
return m_vertex_buffer;
}
[[nodiscard]] inline std::shared_ptr<gl::vertex_buffer>& get_vertex_buffer() noexcept
{
return vertex_buffer;
return m_vertex_buffer;
}
/// @}
/// Returns the byte offset to the first vertex in the vertex buffer.
[[nodiscard]] inline constexpr std::size_t get_vertex_offset() const noexcept
{
return m_vertex_offset;
}
/// Returns the byte stride between consecutive elements within the vertex buffer.
[[nodiscard]] inline constexpr std::size_t get_vertex_stride() const noexcept
{
return m_vertex_stride;
}
/**
* Returns the bounds of the model.
*/
/// @{
[[nodiscard]] inline const aabb_type& get_bounds() const noexcept
{
return bounds;
return m_bounds;
}
[[nodiscard]] inline aabb_type& get_bounds() noexcept
{
return bounds;
return m_bounds;
}
/// @}
@ -108,11 +135,11 @@ public:
/// @{
[[nodiscard]] inline const std::vector<model_group>& get_groups() const noexcept
{
return groups;
return m_groups;
}
[[nodiscard]] inline std::vector<model_group>& get_groups() noexcept
{
return groups;
return m_groups;
}
/// @}
@ -122,20 +149,22 @@ public:
/// @{
[[nodiscard]] inline const ::skeleton& get_skeleton() const noexcept
{
return skeleton;
return m_skeleton;
}
[[nodiscard]] inline ::skeleton& get_skeleton() noexcept
{
return skeleton;
return m_skeleton;
}
/// @}
private:
std::shared_ptr<gl::vertex_array> vertex_array;
std::shared_ptr<gl::vertex_buffer> vertex_buffer;
aabb_type bounds{{0, 0, 0}, {0, 0, 0}};
std::vector<model_group> groups;
::skeleton skeleton;
std::shared_ptr<gl::vertex_array> m_vertex_array;
std::shared_ptr<gl::vertex_buffer> m_vertex_buffer;
std::size_t m_vertex_offset{};
std::size_t m_vertex_stride{};
aabb_type m_bounds{{0, 0, 0}, {0, 0, 0}};
std::vector<model_group> m_groups;
::skeleton m_skeleton;
};
} // namespace render

+ 11
- 5
src/engine/render/operation.hpp View File

@ -22,7 +22,8 @@
#include <engine/math/vector.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/gl/primitive-topology.hpp>
#include <engine/render/material.hpp>
#include <cstdint>
#include <memory>
@ -35,16 +36,21 @@ namespace render {
*/
struct operation
{
gl::primitive_topology primitive_topology{gl::primitive_topology::triangle_list};
const gl::vertex_array* vertex_array{nullptr};
gl::drawing_mode drawing_mode{gl::drawing_mode::triangles};
std::size_t start_index{};
std::size_t index_count{};
const gl::vertex_buffer* vertex_buffer{nullptr};
std::size_t vertex_offset{0};
std::size_t vertex_stride{0};
std::uint32_t first_vertex{0};
std::uint32_t vertex_count{0};
std::uint32_t first_instance{0};
std::uint32_t instance_count{1};
std::shared_ptr<render::material> material;
math::fmat4 transform{math::fmat4::identity()};
float depth{};
std::size_t instance_count{};
std::span<const math::fmat4> matrix_palette{};
std::uint32_t layer_mask{};

+ 11
- 6
src/engine/render/pass.cpp View File

@ -21,10 +21,10 @@
namespace render {
pass::pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer):
rasterizer(rasterizer),
framebuffer(framebuffer),
enabled(true)
pass::pass(gl::pipeline* pipeline, const gl::framebuffer* framebuffer):
m_pipeline(pipeline),
m_framebuffer(framebuffer),
m_enabled(true)
{}
pass::~pass()
@ -32,12 +32,17 @@ pass::~pass()
void pass::set_enabled(bool enabled)
{
this->enabled = enabled;
m_enabled = enabled;
}
void pass::set_framebuffer(const gl::framebuffer* framebuffer)
{
this->framebuffer = framebuffer;
m_framebuffer = framebuffer;
}
void pass::clear()
{
m_pipeline->clear_attachments(m_clear_mask, m_clear_value);
}
} // namespace render

+ 26
- 13
src/engine/render/pass.hpp View File

@ -20,8 +20,9 @@
#ifndef ANTKEEPER_RENDER_PASS_HPP
#define ANTKEEPER_RENDER_PASS_HPP
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/pipeline.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/clear-value.hpp>
#include <engine/render/context.hpp>
namespace render {
@ -32,30 +33,42 @@ namespace render {
class pass
{
public:
pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer);
pass(gl::pipeline* pipeline, const gl::framebuffer* framebuffer);
virtual ~pass();
virtual void render(render::context& ctx) = 0;
void set_enabled(bool enabled);
bool is_enabled() const;
[[nodiscard]] inline constexpr bool is_enabled() const noexcept
{
return m_enabled;
}
void set_framebuffer(const gl::framebuffer* framebuffer);
inline void set_clear_mask(std::uint8_t mask) noexcept
{
m_clear_mask = mask;
}
inline void set_clear_value(const gl::clear_value& value) noexcept
{
m_clear_value = value;
}
void clear();
protected:
gl::rasterizer* rasterizer;
const gl::framebuffer* framebuffer;
gl::pipeline* m_pipeline;
const gl::framebuffer* m_framebuffer;
std::uint8_t m_clear_mask{};
gl::clear_value m_clear_value;
private:
bool enabled;
bool m_enabled;
};
inline bool pass::is_enabled() const
{
return enabled;
}
} // namespace render
#endif // ANTKEEPER_RENDER_PASS_HPP

+ 150
- 160
src/engine/render/passes/bloom-pass.cpp View File

@ -19,37 +19,28 @@
#include <engine/render/passes/bloom-pass.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/pipeline.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-variable.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-attribute.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/texture-2d.hpp>
#include <engine/gl/texture-wrapping.hpp>
#include <engine/gl/texture-filter.hpp>
#include <engine/render/vertex-attribute.hpp>
#include <engine/gl/texture.hpp>
#include <engine/render/vertex-attribute-location.hpp>
#include <engine/render/context.hpp>
#include <algorithm>
#include <cmath>
#include <glad/glad.h>
namespace render {
bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager):
pass(rasterizer, nullptr),
source_texture(nullptr),
mip_chain_length(0),
filter_radius(0.005f),
corrected_filter_radius{filter_radius, filter_radius}
bloom_pass::bloom_pass(gl::pipeline* pipeline, resource_manager* resource_manager):
pass(pipeline, nullptr)
{
// Load downsample shader template
auto downsample_shader_template = resource_manager->load<gl::shader_template>("bloom-downsample.glsl");
// Build downsample shader program with Karis averaging
downsample_karis_shader = downsample_shader_template->build
m_downsample_karis_shader = downsample_shader_template->build
(
{
{"KARIS_AVERAGE", std::string()}
@ -57,213 +48,201 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma
);
// Build downsample shader program without Karis averaging
downsample_shader = downsample_shader_template->build();
m_downsample_shader = downsample_shader_template->build();
// Load upsample shader template
auto upsample_shader_template = resource_manager->load<gl::shader_template>("bloom-upsample.glsl");
// Build upsample shader program
upsample_shader = upsample_shader_template->build();
m_upsample_shader = upsample_shader_template->build();
// Construct framebuffer texture sampler
m_sampler = std::make_shared<gl::sampler>
(
gl::sampler_filter::linear,
gl::sampler_filter::linear,
gl::sampler_mipmap_mode::linear,
gl::sampler_address_mode::clamp_to_edge,
gl::sampler_address_mode::clamp_to_edge
);
// Allocate empty vertex array
m_vertex_array = std::make_unique<gl::vertex_array>();
}
void bloom_pass::render(render::context& ctx)
{
// Execute command buffer
for (const auto& command: command_buffer)
for (const auto& command: m_command_buffer)
{
command();
}
}
void bloom_pass::resize()
void bloom_pass::set_source_texture(std::shared_ptr<gl::texture_2d> texture)
{
unsigned int source_width = 1;
unsigned int source_height = 1;
if (source_texture)
if (m_source_texture != texture)
{
// Get source texture dimensions
source_width = source_texture->get_width();
source_height = source_texture->get_height();
m_source_texture = texture;
// Correct filter radius according to source texture aspect ratio
const float aspect_ratio = static_cast<float>(source_height) / static_cast<float>(source_width);
corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius};
}
// Resize mip chain
for (unsigned int i = 0; i < mip_chain_length; ++i)
{
// Calculate mip dimensions
unsigned int mip_width = std::max<unsigned int>(1, source_width >> (i + 1));
unsigned int mip_height = std::max<unsigned int>(1, source_height >> (i + 1));
// Resize mip texture
textures[i]->resize(mip_width, mip_height, nullptr);
// Resize mip framebuffer
framebuffers[i]->resize({(int)mip_width, (int)mip_height});
rebuild_mip_chain();
correct_filter_radius();
rebuild_command_buffer();
}
}
void bloom_pass::set_source_texture(const gl::texture_2d* texture)
void bloom_pass::set_mip_chain_length(unsigned int length)
{
m_mip_chain_length = length;
rebuild_mip_chain();
rebuild_command_buffer();
}
void bloom_pass::set_filter_radius(float radius)
{
if (texture != source_texture)
{
if (texture)
{
if (source_texture)
{
if (texture->get_width() != source_texture->get_width() || texture->get_height() != source_texture->get_height())
{
source_texture = texture;
resize();
}
else
{
source_texture = texture;
}
}
else
{
source_texture = texture;
resize();
rebuild_command_buffer();
}
}
else
{
source_texture = nullptr;
rebuild_command_buffer();
}
}
m_filter_radius = radius;
correct_filter_radius();
}
void bloom_pass::set_mip_chain_length(unsigned int length)
void bloom_pass::rebuild_mip_chain()
{
unsigned int source_width = 1;
unsigned int source_height = 1;
if (source_texture)
if (m_source_texture && m_mip_chain_length)
{
// Get source texture dimensions
source_width = source_texture->get_width();
source_height = source_texture->get_height();
}
if (length > mip_chain_length)
{
// Generate additional framebuffers
for (unsigned int i = mip_chain_length; i < length; ++i)
// Rebuild target image
m_target_image = std::make_shared<gl::image_2d>
(
gl::format::r16g16b16_sfloat,
m_source_texture->get_image_view()->get_image()->get_dimensions()[0],
m_source_texture->get_image_view()->get_image()->get_dimensions()[1],
m_mip_chain_length
);
m_target_textures.resize(m_mip_chain_length);
m_target_framebuffers.resize(m_mip_chain_length);
for (unsigned int i = 0; i < m_mip_chain_length; ++i)
{
// Calculate mip resolution
unsigned int mip_width = std::max<unsigned int>(1, source_width >> (i + 1));
unsigned int mip_height = std::max<unsigned int>(1, source_height >> (i + 1));
// Generate mip texture
auto texture = std::make_unique<gl::texture_2d>(mip_width, mip_height, gl::pixel_type::float_16, gl::pixel_format::rgb);
texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend);
texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear);
texture->set_max_anisotropy(0.0f);
// Generate mip framebuffer
auto framebuffer = std::make_unique<gl::framebuffer>(mip_width, mip_height);
framebuffer->attach(gl::framebuffer_attachment_type::color, texture.get());
// Rebuild mip texture
m_target_textures[i] = std::make_shared<gl::texture_2d>
(
std::make_shared<gl::image_view_2d>
(
m_target_image,
m_target_image->get_format(),
i,
1
),
m_sampler
);
textures.push_back(std::move(texture));
framebuffers.emplace_back(std::move(framebuffer));
// Rebuild mip framebuffer
const gl::framebuffer_attachment attachments[1] =
{{
gl::color_attachment_bit,
m_target_textures[i]->get_image_view(),
0
}};
m_target_framebuffers[i] = std::make_shared<gl::framebuffer>
(
attachments,
m_target_image->get_dimensions()[0] >> i,
m_target_image->get_dimensions()[1] >> i
);
}
}
else if (length < mip_chain_length)
else
{
framebuffers.resize(length);
textures.resize(length);
m_target_image = nullptr;
m_target_textures.clear();
m_target_framebuffers.clear();
}
// Update mip chain length
mip_chain_length = length;
// Rebuild command buffer
rebuild_command_buffer();
}
void bloom_pass::set_filter_radius(float radius)
void bloom_pass::correct_filter_radius()
{
filter_radius = radius;
// Get aspect ratio of source texture
// Get aspect ratio of target image
float aspect_ratio = 1.0f;
if (source_texture)
if (m_target_image)
{
aspect_ratio = static_cast<float>(source_texture->get_height()) / static_cast<float>(source_texture->get_width());
aspect_ratio = static_cast<float>(m_target_image->get_dimensions()[1]) /
static_cast<float>(m_target_image->get_dimensions()[0]);
}
// Correct filter radius according to source texture aspect ratio
corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius};
// Correct filter radius according to target image aspect ratio
m_corrected_filter_radius = {m_filter_radius * aspect_ratio, m_filter_radius};
}
void bloom_pass::rebuild_command_buffer()
{
command_buffer.clear();
m_command_buffer.clear();
if (!source_texture ||
!mip_chain_length ||
!downsample_karis_shader ||
!downsample_shader ||
!upsample_shader)
if (!m_source_texture ||
!m_mip_chain_length ||
!m_downsample_karis_shader ||
!m_downsample_shader ||
!m_upsample_shader)
{
return;
}
// Setup downsample state
command_buffer.emplace_back
m_command_buffer.emplace_back
(
[]()
[&]()
{
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
m_pipeline->set_primitive_topology(gl::primitive_topology::triangle_list);
m_pipeline->bind_vertex_array(m_vertex_array.get());
m_pipeline->set_depth_test_enabled(false);
m_pipeline->set_cull_mode(gl::cull_mode::back);
m_pipeline->set_color_blend_enabled(false);
}
);
// Downsample first mip with Karis average
if (auto source_texture_var = downsample_karis_shader->variable("source_texture"))
if (auto source_texture_var = m_downsample_karis_shader->variable("source_texture"))
{
command_buffer.emplace_back
m_command_buffer.emplace_back
(
[&, source_texture_var]()
{
rasterizer->use_program(*downsample_karis_shader);
rasterizer->use_framebuffer(*framebuffers[0]);
rasterizer->set_viewport(0, 0, textures[0]->get_width(), textures[0]->get_height());
m_pipeline->bind_shader_program(m_downsample_karis_shader.get());
m_pipeline->bind_framebuffer(m_target_framebuffers[0].get());
source_texture_var->update(*source_texture);
const auto& target_dimensions = m_target_image->get_dimensions();
const gl::viewport viewport[1] = {{0.0f, 0.0f, static_cast<float>(target_dimensions[0]), static_cast<float>(target_dimensions[1])}};
m_pipeline->set_viewport(0, viewport);
rasterizer->draw_arrays(gl::drawing_mode::triangles, 0, 3);
source_texture_var->update(*m_source_texture);
// Draw fullscreen triangle
m_pipeline->draw(3, 1, 0, 0);
}
);
}
// Downsample remaining mips
if (mip_chain_length > 1)
if (m_mip_chain_length > 1)
{
if (auto source_texture_var = downsample_shader->variable("source_texture"))
if (auto source_texture_var = m_downsample_shader->variable("source_texture"))
{
command_buffer.emplace_back([&](){rasterizer->use_program(*downsample_shader);});
m_command_buffer.emplace_back([&](){m_pipeline->bind_shader_program(m_downsample_shader.get());});
for (int i = 1; i < static_cast<int>(mip_chain_length); ++i)
for (int i = 1; i < static_cast<int>(m_mip_chain_length); ++i)
{
command_buffer.emplace_back
m_command_buffer.emplace_back
(
[&, source_texture_var, i]()
{
rasterizer->use_framebuffer(*framebuffers[i]);
rasterizer->set_viewport(0, 0, textures[i]->get_width(), textures[i]->get_height());
m_pipeline->bind_framebuffer(m_target_framebuffers[i].get());
const auto& target_dimensions = m_target_image->get_dimensions();
const gl::viewport viewport[1] = {{0.0f, 0.0f, static_cast<float>(target_dimensions[0] >> i), static_cast<float>(target_dimensions[1] >> i)}};
m_pipeline->set_viewport(0, viewport);
// Use previous downsample texture as downsample source
source_texture_var->update(*textures[i - 1]);
source_texture_var->update(*m_target_textures[i - 1]);
rasterizer->draw_arrays(gl::drawing_mode::triangles, 0, 3);
// Draw fullscreen triangle
m_pipeline->draw(3, 1, 0, 0);
}
);
}
@ -271,43 +250,54 @@ void bloom_pass::rebuild_command_buffer()
}
// Setup upsample state
command_buffer.emplace_back
m_command_buffer.emplace_back
(
[&]()
{
// Enable additive blending
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
m_pipeline->set_color_blend_enabled(true);
m_pipeline->set_color_blend_equation
({
gl::blend_factor::one,
gl::blend_factor::one,
gl::blend_op::add,
gl::blend_factor::one,
gl::blend_factor::one,
gl::blend_op::add
});
// Bind upsample shader
rasterizer->use_program(*upsample_shader);
m_pipeline->bind_shader_program(m_upsample_shader.get());
}
);
// Update upsample filter radius
if (auto filter_radius_var = upsample_shader->variable("filter_radius"))
if (auto filter_radius_var = m_upsample_shader->variable("filter_radius"))
{
command_buffer.emplace_back([&, filter_radius_var](){filter_radius_var->update(corrected_filter_radius);});
m_command_buffer.emplace_back([&, filter_radius_var](){filter_radius_var->update(m_corrected_filter_radius);});
}
// Upsample
if (auto source_texture_var = upsample_shader->variable("source_texture"))
if (auto source_texture_var = m_upsample_shader->variable("source_texture"))
{
for (int i = static_cast<int>(mip_chain_length) - 1; i > 0; --i)
for (int i = static_cast<int>(m_mip_chain_length) - 1; i > 0; --i)
{
const int j = i - 1;
command_buffer.emplace_back
m_command_buffer.emplace_back
(
[&, source_texture_var, i, j]()
{
rasterizer->use_framebuffer(*framebuffers[j]);
rasterizer->set_viewport(0, 0, textures[j]->get_width(), textures[j]->get_height());
m_pipeline->bind_framebuffer(m_target_framebuffers[j].get());
const auto& target_dimensions = m_target_image->get_dimensions();
const gl::viewport viewport[1] = {{0.0f, 0.0f, static_cast<float>(target_dimensions[0] >> j), static_cast<float>(target_dimensions[1] >> j)}};
m_pipeline->set_viewport(0, viewport);
source_texture_var->update(*textures[i]);
source_texture_var->update(*m_target_textures[i]);
rasterizer->draw_arrays(gl::drawing_mode::triangles, 0, 3);
// Draw fullscreen triangle
m_pipeline->draw(3, 1, 0, 0);
}
);
}

+ 24
- 20
src/engine/render/passes/bloom-pass.hpp View File

@ -26,7 +26,8 @@
#include <engine/gl/shader-variable.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/texture-2d.hpp>
#include <engine/gl/texture.hpp>
#include <engine/gl/sampler.hpp>
#include <memory>
#include <functional>
@ -46,10 +47,10 @@ public:
/**
* Constructs a bloom pass.
*
* @param rasterizer Rasterizer.
* @param pipeline Graphics pipeline.
* @param resource_manager Resource manager.
*/
bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager);
bloom_pass(gl::pipeline* pipeline, resource_manager* resource_manager);
/**
* Renders a bloom texture.
@ -69,7 +70,7 @@ public:
*
* @param texture Bloom source texture.
*/
void set_source_texture(const gl::texture_2d* texture);
void set_source_texture(std::shared_ptr<gl::texture_2d> texture);
/**
* Sets the mip chain length. A length of `1` indicates a single stage bloom.
@ -88,31 +89,34 @@ public:
/**
* Returns the texture containing the bloom result.
*/
const gl::texture_2d* get_bloom_texture() const;
[[nodiscard]] inline std::shared_ptr<gl::texture_2d> get_bloom_texture() const
{
return m_target_textures.empty() ? nullptr : m_target_textures.front();
}
private:
void rebuild_mip_chain();
void correct_filter_radius();
void rebuild_command_buffer();
const gl::texture_2d* source_texture;
std::shared_ptr<gl::texture_2d> m_source_texture;
std::shared_ptr<gl::image_2d> m_target_image;
std::vector<std::shared_ptr<gl::texture_2d>> m_target_textures;
std::vector<std::shared_ptr<gl::framebuffer>> m_target_framebuffers;
std::unique_ptr<gl::shader_program> downsample_karis_shader;
std::unique_ptr<gl::shader_program> downsample_shader;
std::unique_ptr<gl::shader_program> upsample_shader;
std::unique_ptr<gl::shader_program> m_downsample_karis_shader;
std::unique_ptr<gl::shader_program> m_downsample_shader;
std::unique_ptr<gl::shader_program> m_upsample_shader;
unsigned int mip_chain_length;
std::vector<std::unique_ptr<gl::framebuffer>> framebuffers;
std::vector<std::unique_ptr<gl::texture_2d>> textures;
float filter_radius;
math::fvec2 corrected_filter_radius;
std::shared_ptr<gl::sampler> m_sampler;
std::unique_ptr<gl::vertex_array> m_vertex_array;
unsigned int m_mip_chain_length{0};
float m_filter_radius{0.005f};
math::fvec2 m_corrected_filter_radius{0.005f, 0.005f};
std::vector<std::function<void()>> command_buffer;
std::vector<std::function<void()>> m_command_buffer;
};
inline const gl::texture_2d* bloom_pass::get_bloom_texture() const
{
return textures.empty() ? nullptr : textures.front().get();
}
} // namespace render
#endif // ANTKEEPER_RENDER_BLOOM_PASS_HPP

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save