Browse Source

Revise resource management and resource loading. RAII-ify entire codebase. Improve materials and shaders. Optimize and improve all render passes. Make material pass use shader templates to support arbitrary numbers of lights. Add fnv1a data types

master
C. J. Howard 1 year ago
parent
commit
f1d46e10da
308 changed files with 11227 additions and 11457 deletions
  1. +1
    -0
      CMakeLists.txt
  2. +1
    -0
      src/engine/animation/animation-channel.hpp
  3. +29
    -37
      src/engine/animation/bone.hpp
  4. +10
    -8
      src/engine/animation/screen-transition.cpp
  5. +6
    -6
      src/engine/animation/screen-transition.hpp
  6. +2
    -1
      src/engine/animation/skeleton.hpp
  7. +2
    -2
      src/engine/app/input-manager.cpp
  8. +1
    -1
      src/engine/app/input-manager.hpp
  9. +3
    -5
      src/engine/app/sdl/sdl-input-manager.cpp
  10. +2
    -1
      src/engine/app/sdl/sdl-input-manager.hpp
  11. +3
    -3
      src/engine/app/sdl/sdl-window.cpp
  12. +3
    -2
      src/engine/app/sdl/sdl-window.hpp
  13. +2
    -2
      src/engine/app/window-manager.cpp
  14. +1
    -1
      src/engine/app/window-manager.hpp
  15. +0
    -9
      src/engine/config.hpp.in
  16. +0
    -116
      src/engine/geom/csg.cpp
  17. +0
    -88
      src/engine/geom/csg.hpp
  18. +16
    -16
      src/engine/geom/marching-cubes.cpp
  19. +1
    -1
      src/engine/geom/marching-cubes.hpp
  20. +3
    -11
      src/engine/geom/mesh-functions.cpp
  21. +3
    -3
      src/engine/geom/meshes/grid.cpp
  22. +2
    -1
      src/engine/geom/meshes/grid.hpp
  23. +9
    -32
      src/engine/geom/rect-pack.hpp
  24. +3
    -1
      src/engine/gl/buffer-usage.hpp
  25. +8
    -4
      src/engine/gl/color-space.hpp
  26. +3
    -2
      src/engine/gl/drawing-mode.hpp
  27. +3
    -2
      src/engine/gl/element-array-type.hpp
  28. +3
    -4
      src/engine/gl/framebuffer.hpp
  29. +0
    -21
      src/engine/gl/gl.hpp
  30. +666
    -0
      src/engine/gl/opengl/gl-shader-variables.cpp
  31. +526
    -0
      src/engine/gl/opengl/gl-shader-variables.hpp
  32. +26
    -10
      src/engine/gl/pixel-format.hpp
  33. +3
    -2
      src/engine/gl/pixel-type.hpp
  34. +3
    -5
      src/engine/gl/rasterizer.cpp
  35. +8
    -10
      src/engine/gl/rasterizer.hpp
  36. +0
    -773
      src/engine/gl/shader-input.cpp
  37. +0
    -202
      src/engine/gl/shader-input.hpp
  38. +11
    -18
      src/engine/gl/shader-object.cpp
  39. +18
    -23
      src/engine/gl/shader-object.hpp
  40. +121
    -133
      src/engine/gl/shader-program.cpp
  41. +58
    -52
      src/engine/gl/shader-program.hpp
  42. +3
    -2
      src/engine/gl/shader-stage.hpp
  43. +364
    -0
      src/engine/gl/shader-template.cpp
  44. +39
    -29
      src/engine/gl/shader-template.hpp
  45. +6
    -2
      src/engine/gl/shader-variable-type.hpp
  46. +396
    -0
      src/engine/gl/shader-variable.cpp
  47. +195
    -0
      src/engine/gl/shader-variable.hpp
  48. +2
    -2
      src/engine/gl/texture-1d.cpp
  49. +4
    -4
      src/engine/gl/texture-1d.hpp
  50. +3
    -3
      src/engine/gl/texture-2d.cpp
  51. +5
    -5
      src/engine/gl/texture-2d.hpp
  52. +2
    -2
      src/engine/gl/texture-3d.cpp
  53. +4
    -4
      src/engine/gl/texture-3d.hpp
  54. +0
    -5
      src/engine/gl/texture-cube.hpp
  55. +4
    -3
      src/engine/gl/texture-filter.hpp
  56. +3
    -2
      src/engine/gl/texture-wrapping.hpp
  57. +216
    -9
      src/engine/gl/texture.cpp
  58. +15
    -8
      src/engine/gl/texture.hpp
  59. +8
    -12
      src/engine/gl/vertex-array.cpp
  60. +9
    -4
      src/engine/gl/vertex-array.hpp
  61. +1
    -1
      src/engine/gl/vertex-attribute.hpp
  62. +70
    -23
      src/engine/gl/vertex-buffer.cpp
  63. +56
    -36
      src/engine/gl/vertex-buffer.hpp
  64. +13
    -2
      src/engine/i18n/string-map.cpp
  65. +2
    -1
      src/engine/i18n/string-map.hpp
  66. +80
    -0
      src/engine/i18n/string-table.cpp
  67. +6
    -7
      src/engine/i18n/string-table.hpp
  68. +58
    -47
      src/engine/physics/orbit/ephemeris.cpp
  69. +6
    -1
      src/engine/physics/orbit/ephemeris.hpp
  70. +1
    -1
      src/engine/physics/orbit/trajectory.hpp
  71. +2
    -2
      src/engine/render/compositor.cpp
  72. +1
    -1
      src/engine/render/compositor.hpp
  73. +9
    -7
      src/engine/render/material-blend-mode.hpp
  74. +0
    -1
      src/engine/render/material-flags.hpp
  75. +0
    -460
      src/engine/render/material-property.hpp
  76. +10
    -8
      src/engine/render/material-shadow-mode.hpp
  77. +34
    -21
      src/engine/render/material-variable-type.hpp
  78. +373
    -0
      src/engine/render/material-variable.hpp
  79. +456
    -79
      src/engine/render/material.cpp
  80. +106
    -159
      src/engine/render/material.hpp
  81. +295
    -76
      src/engine/render/model.cpp
  82. +87
    -154
      src/engine/render/model.hpp
  83. +1
    -1
      src/engine/render/pass.hpp
  84. +146
    -103
      src/engine/render/passes/bloom-pass.cpp
  85. +17
    -25
      src/engine/render/passes/bloom-pass.hpp
  86. +78
    -20
      src/engine/render/passes/clear-pass.cpp
  87. +7
    -3
      src/engine/render/passes/clear-pass.hpp
  88. +104
    -68
      src/engine/render/passes/final-pass.cpp
  89. +15
    -17
      src/engine/render/passes/final-pass.hpp
  90. +71
    -48
      src/engine/render/passes/fxaa-pass.cpp
  91. +11
    -15
      src/engine/render/passes/fxaa-pass.hpp
  92. +33
    -34
      src/engine/render/passes/ground-pass.cpp
  93. +11
    -11
      src/engine/render/passes/ground-pass.hpp
  94. +826
    -534
      src/engine/render/passes/material-pass.cpp
  95. +86
    -75
      src/engine/render/passes/material-pass.hpp
  96. +23
    -17
      src/engine/render/passes/outline-pass.cpp
  97. +10
    -9
      src/engine/render/passes/outline-pass.hpp
  98. +63
    -42
      src/engine/render/passes/resample-pass.cpp
  99. +12
    -14
      src/engine/render/passes/resample-pass.hpp
  100. +41
    -28
      src/engine/render/passes/shadow-map-pass.cpp

+ 1
- 0
CMakeLists.txt View File

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.25)
option(APPLICATION_NAME "Application name" "Antkeeper")
option(APPLICATION_VERSION "Application version string" "0.0.0")
option(APPLICATION_AUTHOR "Application author" "C. J. Howard")

+ 1
- 0
src/engine/animation/animation-channel.hpp View File

@ -128,6 +128,7 @@ animation_channel& animation_channel::operator=(const animation_channel& o
{
id = other.id;
keyframes = other.keyframes;
return *this;
}
template <typename T>

+ 29
- 37
src/engine/animation/bone.hpp View File

@ -22,15 +22,13 @@
#include <cstdint>
/**
* Skeletal animation bone identifier, consisting of a bone index in the lower half, and a parent bone index in the upper half.
*/
typedef std::uint32_t bone;
using bone = std::uint32_t;
/// Mask to extract the index of a bone.
constexpr bone bone_index_mask = 0xffff;
inline constexpr bone bone_index_mask = 0xffff;
/**
* Bone index comparison function object.
@ -42,9 +40,13 @@ struct bone_index_compare
*
* @param lhs First bone.
* @param rhs Second bone.
*
* @return Comparison result.
*/
bool operator()(const bone& lhs, const bone& rhs) const;
[[nodiscard]] inline bool operator()(const bone& lhs, const bone& rhs) const noexcept
{
return (lhs & bone_index_mask) < (rhs & bone_index_mask);
}
};
/**
@ -52,68 +54,58 @@ struct bone_index_compare
*
* @param index Index of the bone.
* @param parent_index Index of the parent bone.
*
* @return Bone identifier.
*/
bone make_bone(std::uint16_t index, std::uint16_t parent_index);
[[nodiscard]] inline bone make_bone(std::uint16_t index, std::uint16_t parent_index) noexcept
{
return (static_cast<std::uint32_t>(parent_index) << 16) | index;
}
/**
* Constructs an orphan bone identifier.
*
* @param index Index of the orphan bone.
*
* @return Orphan bone identifier.
*/
bone make_bone(std::uint16_t index);
[[nodiscard]] inline bone make_bone(std::uint16_t index) noexcept
{
return make_bone(index, index);
}
/**
* Returns the index of a bone.
*
* @param x Bone identifier.
*
* @return Index of the bone.
*/
std::uint16_t bone_index(bone x);
[[nodiscard]] inline std::uint16_t bone_index(bone x) noexcept
{
return static_cast<std::uint16_t>(x & bone_index_mask);
}
/**
* Returns the parent index of a bone.
*
* @param x Bone identifier.
*
* @return Index of the parent bone.
*/
std::uint16_t bone_parent_index(bone x);
[[nodiscard]] inline std::uint16_t bone_parent_index(bone x) noexcept
{
return static_cast<std::uint16_t>(x >> 16);
}
/**
* Returns `true` if a bone has a parent, `false` otherwise.
*
* @param x Bone identifier.
*
* @return Bone parent status.
*/
bool bone_has_parent(bone x);
inline bool bone_index_compare::operator()(const bone& lhs, const bone& rhs) const
{
return (lhs & bone_index_mask) < (rhs & bone_index_mask);
}
inline bone make_bone(std::uint16_t index, std::uint16_t parent_index)
{
return (static_cast<std::uint32_t>(parent_index) << 16) | index;
}
inline bone make_bone(std::uint16_t index)
{
return make_bone(index, index);
}
inline std::uint16_t bone_index(bone x)
{
return static_cast<std::uint16_t>(x & bone_index_mask);
}
inline std::uint16_t bone_parent_index(bone x)
{
return static_cast<std::uint16_t>(x >> 16);
}
inline bool bone_has_parent(bone x)
[[nodiscard]] inline bool bone_has_parent(bone x) noexcept
{
return (x & bone_index_mask) != (x >> 16);
}

+ 10
- 8
src/engine/animation/screen-transition.cpp View File

@ -23,13 +23,15 @@
screen_transition::screen_transition()
{
progress = std::make_shared<render::material_float>(1, 0.0f);
// Setup material
//material.set_flags(MATERIAL_FLAG_X_RAY);
material.set_blend_mode(render::blend_mode::translucent);
progress = material.add_property<float>("progress");
material = std::make_shared<render::material>();
material->set_blend_mode(render::material_blend_mode::translucent);
material->set_variable("progress", progress);
// Setup billboard
billboard.set_material(&material);
billboard.set_material(material);
billboard.set_active(false);
// Add single channel to transition animation
@ -52,7 +54,7 @@ screen_transition::screen_transition()
(
[this](int channel, float progress)
{
this->progress->set_value(progress);
this->progress->set(progress);
}
);
@ -61,7 +63,7 @@ screen_transition::screen_transition()
(
[this](int channel, float progress)
{
this->progress->set_value(progress);
this->progress->set(progress);
}
);
}
@ -104,8 +106,8 @@ void screen_transition::transition(float duration, bool reverse, ::animation
}
// Update tweens
progress->set_value(initial_state);
material.update_tweens();
progress->set(initial_state);
//material.update_tweens();
// Reset and play transition animation
animation.stop();

+ 6
- 6
src/engine/animation/screen-transition.hpp View File

@ -22,7 +22,7 @@
#include <engine/animation/animation.hpp>
#include <engine/render/material.hpp>
#include <engine/render/material-property.hpp>
#include <engine/render/material-variable.hpp>
#include <engine/scene/billboard.hpp>
/**
@ -38,13 +38,13 @@ public:
void transition(float duration, bool reverse, animation<float>::interpolator_type interpolator, bool hide = true, const std::function<void()>& callback = nullptr);
scene::billboard* get_billboard();
render::material* get_material();
std::shared_ptr<render::material> get_material();
::animation<float>* get_animation();
private:
scene::billboard billboard;
render::material material;
render::material_property<float>* progress;
std::shared_ptr<render::material> material;
std::shared_ptr<render::material_float> progress;
::animation<float> animation;
::animation<float>::channel* channel;
std::function<void()> callback;
@ -55,9 +55,9 @@ inline scene::billboard* screen_transition::get_billboard()
return &billboard;
}
inline render::material* screen_transition::get_material()
inline std::shared_ptr<render::material> screen_transition::get_material()
{
return &material;
return material;
}
inline animation<float>* screen_transition::get_animation()

+ 2
- 1
src/engine/animation/skeleton.hpp View File

@ -22,6 +22,7 @@
#include <engine/animation/bone.hpp>
#include <engine/animation/pose.hpp>
#include <engine/utility/hash/fnv1a.hpp>
#include <string>
#include <unordered_map>
@ -37,7 +38,7 @@ struct skeleton
pose inverse_bind_pose;
/// Maps bone names to bone identifiers.
std::unordered_map<std::string, bone> bone_map;
std::unordered_map<hash::fnv1a32_t, bone> bone_map;
};
#endif // ANTKEEPER_ANIMATION_SKELETON_HPP

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

@ -22,9 +22,9 @@
namespace app {
input_manager* input_manager::instance()
std::unique_ptr<input_manager> input_manager::instance()
{
return new sdl_input_manager();
return std::make_unique<sdl_input_manager>();
}
void input_manager::register_device(input::device& device)

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

@ -40,7 +40,7 @@ public:
/**
* Allocates and returns an input manager.
*/
static input_manager* instance();
static std::unique_ptr<input_manager> instance();
/// Destructs an input manager.
virtual ~input_manager() = default;

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

@ -268,13 +268,11 @@ void sdl_input_manager::update()
debug::log::info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string());
// Create new gamepad
input::gamepad* gamepad = new input::gamepad();
// Allocate gamepad
auto& gamepad = gamepad_map[event.cdevice.which];
gamepad = std::make_unique<input::gamepad>();
gamepad->set_uuid(gamepad_uuid);
// Add gamepad to gamepad map
gamepad_map[event.cdevice.which] = gamepad;
// Register gamepad
register_device(*gamepad);

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

@ -21,6 +21,7 @@
#define ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP
#include <engine/app/input-manager.hpp>
#include <memory>
namespace app {
@ -49,7 +50,7 @@ public:
private:
input::keyboard keyboard;
input::mouse mouse;
std::unordered_map<int, input::gamepad*> gamepad_map;
std::unordered_map<int, std::unique_ptr<input::gamepad>> gamepad_map;
};
} // namespace app

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

@ -179,13 +179,13 @@ sdl_window::sdl_window
SDL_GL_GetDrawableSize(internal_window, &this->viewport_size.x(), &this->viewport_size.y());
// Allocate rasterizer
this->rasterizer = new gl::rasterizer();
this->rasterizer = std::make_unique<gl::rasterizer>();
}
sdl_window::~sdl_window()
{
// Destruct rasterizer
delete rasterizer;
// Deallocate rasterizer
rasterizer.reset();
// Destruct the OpenGL context
SDL_GL_DeleteContext(internal_context);

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

@ -22,6 +22,7 @@
#include <engine/app/window.hpp>
#include <SDL2/SDL.h>
#include <memory>
namespace app {
@ -60,7 +61,7 @@ public:
[[nodiscard]] inline virtual gl::rasterizer* get_rasterizer() noexcept
{
return rasterizer;
return rasterizer.get();
}
private:
@ -68,7 +69,7 @@ private:
SDL_Window* internal_window;
SDL_GLContext internal_context;
gl::rasterizer* rasterizer;
std::unique_ptr<gl::rasterizer> rasterizer;
};
} // namespace app

+ 2
- 2
src/engine/app/window-manager.cpp View File

@ -22,9 +22,9 @@
namespace app {
window_manager* window_manager::instance()
std::unique_ptr<window_manager> window_manager::instance()
{
return new sdl_window_manager();
return std::make_unique<sdl_window_manager>();
}
} // namespace app

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

@ -37,7 +37,7 @@ public:
/**
* Allocates and returns a window manager.
*/
static window_manager* instance();
static std::unique_ptr<window_manager> instance();
/// Destructs a window manager.
virtual ~window_manager() = default;

+ 0
- 9
src/engine/config.hpp.in View File

@ -126,15 +126,6 @@ inline constexpr float new_colony_fade_out_duration = 1.0f;
/// Duration of the nuptial flight fade in, in seconds.
inline constexpr float nuptial_flight_fade_in_duration = 5.0f;
#define MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT 1
#define MATERIAL_PASS_MAX_POINT_LIGHT_COUNT 1
#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 3
#define MATERIAL_PASS_MAX_SPOTLIGHT_COUNT 1
#define MATERIAL_PASS_MAX_BONE_COUNT 64
#define TERRAIN_PATCH_SIZE 200.0f
#define TERRAIN_PATCH_RESOLUTION 4
#define VEGETATION_PATCH_RESOLUTION 1
} // namespace config
#endif // ANTKEEPER_CONFIG_HPP

+ 0
- 116
src/engine/geom/csg.cpp View File

@ -1,116 +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/geom/csg.hpp>
#include <tuple>
namespace geom {
namespace csg {
enum class polygon_classification
{
coplanar,
front,
back,
spanning
};
/**
* Classifies a polygon relative to a partitioning plane.
*
* @param partition Partitioning plane relative to which the polygon should be classified.
* @param poly Polygon to be classified.
* @return Classification of the polygon, relative to the plane.
*/
static polygon_classification classify_polygon(const plane& partition, const polygon& poly)
{
for (const float3& vertex: poly.vertices)
{
}
return polygon_classification::coplanar;
}
/**
* Splits a polygon along a partitioning plane.
*
* @param poly Polygon to split.
* @param partition Partitioning plane along which the polygon should be split.
* @return List of polygons which were formed by splitting the specified polygon along the partitioning plane, along with their respective classifications relative to the partition.
*/
std::list<std::tuple<polygon, polygon_classification>> split_polygon(const polygon& poly, const plane& partition)
{
return {};
}
bsp_tree::bsp_tree(const std::list<polygon>& polygons):
front(nullptr),
back(nullptr)
{
//partition = polygons.front();
std::list<polygon> front_polygons;
std::list<polygon> back_polygons;
// Classify all polygons relative to this node's partitioning plane
for (const polygon& p: polygons)
{
polygon_classification classification = classify_polygon(partition, p);
switch (classification)
{
case polygon_classification::coplanar:
coplanar_polygons.push_back(p);
break;
case polygon_classification::front:
front_polygons.push_back(p);
break;
case polygon_classification::back:
back_polygons.push_back(p);
break;
case polygon_classification::spanning:
break;
}
}
if (!front_polygons.empty())
{
// Make subtree containing all polygons in front of this node's plane
front = new bsp_tree(front_polygons);
}
if (!back_polygons.empty())
{
// Make subtree containing all polygons behind this node's plane
back = new bsp_tree(back_polygons);
}
}
bsp_tree::~bsp_tree()
{
delete front;
delete back;
}
} // namespace csg
} // namespace geom

+ 0
- 88
src/engine/geom/csg.hpp View File

@ -1,88 +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_GEOM_CSG_HPP
#define ANTKEEPER_GEOM_CSG_HPP
#include <engine/utility/fundamental-types.hpp>
#include <list>
namespace geom {
/// Constructive solid geometry (CSG)
namespace csg {
struct plane
{
float3 normal;
float distance;
};
struct polygon
{
std::list<float3> vertices;
void* shared;
};
/**
* 3D solid represented by a collection of polygons.
*/
typedef std::list<polygon> solid;
/**
* BSP tree node.
*/
class bsp_tree
{
public:
/**
* Recursively constructs a BSP tree from a collection of polygons.
*
* @param polygons Collection of polygons from which to create the BSP tree.
*/
explicit bsp_tree(const std::list<polygon>& polygons);
/**
* Destroys a BSP tree.
*/
~bsp_tree();
private:
/// Partition which separates the front and back polygons.
plane partition;
/// Set of polygons which are coplanar with the partition.
std::list<polygon> coplanar_polygons;
/// Subtree containing all polygons in front of the partition.
bsp_tree* front;
/// Subtree containing all polygons behind the partition.
bsp_tree* back;
};
solid op_union(const solid& a, const solid& b);
solid op_difference(const solid& a, const solid& b);
solid op_intersect(const solid& a, const solid& b);
} // namespace csg
} // namespace geom
#endif // ANTKEEPER_GEOM_CSG_HPP

+ 16
- 16
src/engine/geom/marching-cubes.cpp View File

@ -23,7 +23,7 @@
namespace geom {
namespace mc {
static constexpr std::uint_fast8_t vertex_table[12][2] =
static constexpr std::uint8_t vertex_table[12][2] =
{
{0, 1},
{1, 2},
@ -39,7 +39,7 @@ static constexpr std::uint_fast8_t vertex_table[12][2] =
{3, 7}
};
static constexpr std::uint_fast16_t edge_table[256] =
static constexpr std::uint16_t edge_table[256] =
{
0x000, 0x109, 0x203, 0x30A, 0x406, 0x50F, 0x605, 0x70C,
0x80C, 0x905, 0xA0F, 0xB06, 0xC0A, 0xD03, 0xE09, 0xF00,
@ -75,7 +75,7 @@ static constexpr std::uint_fast16_t edge_table[256] =
0x70C, 0x605, 0x50F, 0x406, 0x30A, 0x203, 0x109, 0x000
};
static constexpr std::int_fast8_t triangle_table[256][16] =
static constexpr std::int8_t triangle_table[256][16] =
{
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
@ -335,34 +335,34 @@ static constexpr std::int_fast8_t triangle_table[256][16] =
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
};
void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8_t* triangles, std::uint_fast8_t* triangle_count, const float* corners, const float* distances)
void polygonize(float* vertices, std::uint8_t* vertex_count, std::int8_t* triangles, std::uint8_t* triangle_count, const float* corners, const float* distances)
{
*vertex_count = 0;
*triangle_count = 0;
// Calculate signed distances for each cube corner and form an edge table index.
std::uint_fast8_t edge_index = 0;
for (std::uint_fast8_t i = 0; i < 8; ++i)
std::uint8_t edge_index = 0;
for (std::uint8_t i = 0; i < 8; ++i)
edge_index |= (distances[i] < 0.0f) ? (1 << i) : 0;
// Get edge flags from edge table
const std::uint_fast16_t edge_flags = edge_table[edge_index];
const std::uint16_t edge_flags = edge_table[edge_index];
if (!edge_flags)
return;
// Get vertex indices of the case
const std::int_fast8_t* indices = triangle_table[edge_index];
const std::int8_t* indices = triangle_table[edge_index];
// Calculate vertices and store in vertex buffer
float vertex_buffer[12 * 3];
for (std::uint_fast16_t i = 0; i < 12; ++i)
for (std::uint16_t i = 0; i < 12; ++i)
{
// If this edge is intersected
if (edge_flags & (1 << i))
{
// Find the two vertices which make up edge ab
std::uint_fast8_t a = vertex_table[i][0];
std::uint_fast8_t b = vertex_table[i][1];
std::uint8_t a = vertex_table[i][0];
std::uint8_t b = vertex_table[i][1];
const float* v_a = corners + a * 3;
const float* v_b = corners + b * 3;
float f_a = distances[a];
@ -389,14 +389,14 @@ void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8
}
// Remap vertex buffer to be stored contiguously
std::int_fast8_t vertex_remap[12];
for (std::uint_fast8_t i = 0; i < 12; ++i)
std::int8_t vertex_remap[12];
for (std::uint8_t i = 0; i < 12; ++i)
vertex_remap[i] = -1;
for (std::uint_fast8_t i = 0; indices[i] != -1; ++i)
for (std::uint8_t i = 0; indices[i] != -1; ++i)
{
if (vertex_remap[indices[i]] == -1)
{
std::int_fast8_t index = indices[i] * 3;
std::int8_t index = indices[i] * 3;
*(vertices++) = vertex_buffer[ index];
*(vertices++) = vertex_buffer[++index];
*(vertices++) = vertex_buffer[++index];
@ -405,7 +405,7 @@ void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8
}
// Form triangles
for (std::uint_fast8_t i = 0; indices[i] != -1;)
for (std::uint8_t i = 0; indices[i] != -1;)
{
*(triangles++) = vertex_remap[indices[i++]];
*(triangles++) = vertex_remap[indices[i++]];

+ 1
- 1
src/engine/geom/marching-cubes.hpp View File

@ -35,7 +35,7 @@ namespace mc {
* @param[out] triangles Array which can hold 5 at least triangles (15 ints).
* @param[out] triangle_count Number of generated triangles. The maximum number triangles generated for a single cell is 5.
*/
void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8_t* triangles, std::uint_fast8_t* triangle_count, const float* corners, const float* distances);
void polygonize(float* vertices, std::uint8_t* vertex_count, std::int8_t* triangles, std::uint8_t* triangle_count, const float* corners, const float* distances);
/**
* Vertices of a unit cube.

+ 3
- 11
src/engine/geom/mesh-functions.cpp View File

@ -20,6 +20,7 @@
#include <engine/geom/mesh-functions.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <unordered_map>
#include <vector>
namespace geom {
@ -101,13 +102,8 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const
const std::vector<mesh::vertex*>& vertices = mesh.get_vertices();
// Allocate tangent and bitangent buffers
float3* tangent_buffer = new float3[vertices.size()];
float3* bitangent_buffer = new float3[vertices.size()];
for (std::size_t i = 0; i < vertices.size(); ++i)
{
tangent_buffer[i] = {0.0f, 0.0f, 0.0f};
bitangent_buffer[i] = {0.0f, 0.0f, 0.0f};
}
std::vector<float3> tangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f});
std::vector<float3> bitangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f});
// Accumulate tangents and bitangents
for (std::size_t i = 0; i < faces.size(); ++i)
@ -155,10 +151,6 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const
tangents[i] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign};
}
// Free faceted tangents and bitangents
delete[] tangent_buffer;
delete[] bitangent_buffer;
}
aabb<float> calculate_bounds(const mesh& mesh)

+ 3
- 3
src/engine/geom/meshes/grid.cpp View File

@ -25,10 +25,10 @@
namespace geom {
namespace meshes {
geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y)
std::unique_ptr<geom::mesh> grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y)
{
// Allocate new mesh
geom::mesh* mesh = new geom::mesh();
std::unique_ptr<geom::mesh> mesh = std::make_unique<geom::mesh>();
// Determine vertex count and placement
std::size_t columns = subdivisions_x + 1;
@ -94,7 +94,7 @@ geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdiv
mesh->add_face({ab, bd, dc, ca});
}
}
return mesh;
}

+ 2
- 1
src/engine/geom/meshes/grid.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_GEOM_MESHES_GRID_HPP
#include <engine/geom/mesh.hpp>
#include <memory>
namespace geom {
namespace meshes {
@ -33,7 +34,7 @@ namespace meshes {
* @param subdivisions_y Number of subdivisions on the y-axis.
* @return Grid mesh on the XY plane.
*/
geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y);
[[nodiscard]] std::unique_ptr<geom::mesh> grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y);
} // namespace meshes
} // namespace geom

+ 9
- 32
src/engine/geom/rect-pack.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_GEOM_RECT_PACK_HPP
#include <engine/geom/rect.hpp>
#include <memory>
namespace geom {
@ -38,38 +39,16 @@ struct rect_pack_node
/// Rect type.
typedef rect<T> rect_type;
/// Creates a rect pack node.
rect_pack_node();
/// Destroys a rect pack node and its children.
~rect_pack_node();
/// Pointers to the two children of the node, if any.
rect_pack_node* children[2];
std::unique_ptr<rect_pack_node> children[2];
/// Bounds of the node.
rect_type bounds;
rect_type bounds{T{0}, T{0}, T{0}, T{0}};
/// `true` if the node is occupied, `false` otherwise.
bool occupied;
bool occupied{false};
};
template <class T>
rect_pack_node<T>::rect_pack_node():
bounds{T(0), T(0), T(0), T(0)},
occupied(false)
{
children[0] = nullptr;
children[1] = nullptr;
}
template <class T>
rect_pack_node<T>::~rect_pack_node()
{
delete children[0];
delete children[1];
}
/**
* Packs 2D rectangles.
*
@ -136,7 +115,7 @@ private:
template <class T>
rect_pack<T>::rect_pack(scalar_type w, scalar_type h)
{
root.bounds = {T(0), T(0), w, h};
root.bounds = {T{0}, T{0}, w, h};
}
template <class T>
@ -154,10 +133,8 @@ void rect_pack::resize(scalar_type w, scalar_type h)
template <class T>
void rect_pack<T>::clear()
{
delete root.children[0];
delete root.children[1];
root.children[0] = nullptr;
root.children[1] = nullptr;
root.children[0].reset();
root.children[1].reset();
root.occupied = false;
}
@ -209,8 +186,8 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t
}
// Split the node
node.children[0] = new node_type();
node.children[1] = new node_type();
node.children[0] = std::make_unique<node_type>();
node.children[1] = std::make_unique<node_type>();
// Determine split direction
scalar_type dw = node_w - w;

+ 3
- 1
src/engine/gl/buffer-usage.hpp View File

@ -20,6 +20,8 @@
#ifndef ANTKEEPER_GL_BUFFER_USAGE_HPP
#define ANTKEEPER_GL_BUFFER_USAGE_HPP
#include <cstdint>
namespace gl {
/**
@ -27,7 +29,7 @@ namespace gl {
*
* @see gl::vertex_buffer
*/
enum class buffer_usage
enum class buffer_usage: std::uint8_t
{
/// Data will be modified once, by the application, and used at most a few times, for drawing commands.
stream_draw,

+ 8
- 4
src/engine/gl/color-space.hpp View File

@ -20,15 +20,19 @@
#ifndef ANTKEEPER_GL_COLOR_SPACE_HPP
#define ANTKEEPER_GL_COLOR_SPACE_HPP
#include <cstdint>
namespace gl {
enum class color_space
enum class color_space: std::uint8_t
{
linear, ///< Linear color space
srgb ///< sRGB color space
/// Linear color space.
linear,
/// sRGB color space.
srgb
};
} // namespace gl
#endif // ANTKEEPER_GL_COLOR_SPACE_HPP

+ 3
- 2
src/engine/gl/drawing-mode.hpp View File

@ -20,9 +20,11 @@
#ifndef ANTKEEPER_GL_DRAWING_MODE_HPP
#define ANTKEEPER_GL_DRAWING_MODE_HPP
#include <cstdint>
namespace gl {
enum class drawing_mode
enum class drawing_mode: std::uint8_t
{
points,
line_strip,
@ -40,4 +42,3 @@ enum class drawing_mode
} // namespace gl
#endif // ANTKEEPER_GL_DRAWING_MODE_HPP

+ 3
- 2
src/engine/gl/element-array-type.hpp View File

@ -20,9 +20,11 @@
#ifndef ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP
#define ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP
#include <cstdint>
namespace gl {
enum class element_array_type
enum class element_array_type: std::uint8_t
{
uint_8,
uint_16,
@ -32,4 +34,3 @@ enum class element_array_type
} // namespace gl
#endif // ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP

+ 3
- 4
src/engine/gl/framebuffer.hpp View File

@ -21,13 +21,14 @@
#define ANTKEEPER_GL_FRAMEBUFFER_HPP
#include <array>
#include <cstdint>
namespace gl {
class rasterizer;
class texture_2d;
enum class framebuffer_attachment_type
enum class framebuffer_attachment_type: std::uint8_t
{
color,
depth,
@ -41,6 +42,7 @@ public:
* Creates a framebuffer.
*/
framebuffer(int width, int height);
framebuffer();
/// Destroys a framebuffer.
~framebuffer();
@ -73,8 +75,6 @@ public:
private:
friend class rasterizer;
framebuffer();
unsigned int gl_framebuffer_id;
std::array<int, 2> dimensions;
texture_2d* color_attachment;
@ -120,4 +120,3 @@ inline texture_2d* framebuffer::get_stencil_attachment()
} // namespace gl
#endif // ANTKEEPER_GL_FRAMEBUFFER_HPP

+ 0
- 21
src/engine/gl/gl.hpp View File

@ -23,25 +23,4 @@
/// Graphics library interface.
namespace gl {}
#include <engine/gl/buffer-usage.hpp>
#include <engine/gl/color-space.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/element-array-type.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/pixel-format.hpp>
#include <engine/gl/pixel-type.hpp>
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/gl/shader-object.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-stage.hpp>
#include <engine/gl/shader-variable-type.hpp>
#include <engine/gl/texture-2d.hpp>
#include <engine/gl/texture-cube.hpp>
#include <engine/gl/texture-filter.hpp>
#include <engine/gl/texture-wrapping.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-attribute.hpp>
#include <engine/gl/vertex-buffer.hpp>
#endif // ANTKEEPER_GL_HPP

+ 666
- 0
src/engine/gl/opengl/gl-shader-variables.cpp View File

@ -0,0 +1,666 @@
/*
* 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/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 <numeric>
namespace gl {
gl_shader_bool::gl_shader_bool(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
ivalues(size)
{}
void gl_shader_bool::update(bool value) const noexcept
{
glUniform1i(gl_uniform_location, static_cast<GLint>(value));
}
void gl_shader_bool::update(bool value, std::size_t index) const
{
glUniform1i(gl_uniform_location + static_cast<GLint>(index), static_cast<GLint>(value));
}
void gl_shader_bool::update(std::span<const bool> values, std::size_t index) const
{
for (std::size_t i = 0; i < values.size(); ++i)
{
ivalues[i] = static_cast<GLint>(values[i]);
}
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), ivalues.data());
}
gl_shader_bool2::gl_shader_bool2(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
ivalues(size * 2)
{}
void gl_shader_bool2::update(const bool2& value) const noexcept
{
glUniform2i(gl_uniform_location, static_cast<GLint>(value[0]), static_cast<GLint>(value[1]));
}
void gl_shader_bool2::update(const bool2& value, std::size_t index) const
{
glUniform2i(gl_uniform_location + static_cast<GLint>(index), static_cast<GLint>(value[0]), static_cast<GLint>(value[1]));
}
void gl_shader_bool2::update(std::span<const bool2> values, std::size_t index) const
{
GLint* ivalue = ivalues.data();
for (const auto& value: values)
{
*(++ivalue) = static_cast<GLint>(value[0]);
*(++ivalue) = static_cast<GLint>(value[1]);
}
glUniform2iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), ivalues.data());
}
gl_shader_bool3::gl_shader_bool3(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
ivalues(size * 3)
{}
void gl_shader_bool3::update(const bool3& value) const noexcept
{
glUniform3i(gl_uniform_location, static_cast<GLint>(value[0]), static_cast<GLint>(value[1]), static_cast<GLint>(value[2]));
}
void gl_shader_bool3::update(const bool3& value, std::size_t index) const
{
glUniform3i(gl_uniform_location + static_cast<GLint>(index), static_cast<GLint>(value[0]), static_cast<GLint>(value[1]), static_cast<GLint>(value[2]));
}
void gl_shader_bool3::update(std::span<const bool3> values, std::size_t index) const
{
GLint* ivalue = ivalues.data();
for (const auto& value: values)
{
*(++ivalue) = static_cast<GLint>(value[0]);
*(++ivalue) = static_cast<GLint>(value[1]);
*(++ivalue) = static_cast<GLint>(value[2]);
}
glUniform3iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), ivalues.data());
}
gl_shader_bool4::gl_shader_bool4(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
ivalues(size * 4)
{}
void gl_shader_bool4::update(const bool4& value) const noexcept
{
glUniform4i(gl_uniform_location, static_cast<GLint>(value[0]), static_cast<GLint>(value[1]), static_cast<GLint>(value[2]), static_cast<GLint>(value[3]));
}
void gl_shader_bool4::update(const bool4& value, std::size_t index) const
{
glUniform4i(gl_uniform_location + static_cast<GLint>(index), static_cast<GLint>(value[0]), static_cast<GLint>(value[1]), static_cast<GLint>(value[2]), static_cast<GLint>(value[3]));
}
void gl_shader_bool4::update(std::span<const bool4> values, std::size_t index) const
{
GLint* ivalue = ivalues.data();
for (const auto& value: values)
{
*(++ivalue) = static_cast<GLint>(value[0]);
*(++ivalue) = static_cast<GLint>(value[1]);
*(++ivalue) = static_cast<GLint>(value[2]);
*(++ivalue) = static_cast<GLint>(value[3]);
}
glUniform4iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), ivalues.data());
}
gl_shader_int::gl_shader_int(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_int::update(int value) const noexcept
{
glUniform1i(gl_uniform_location, value);
}
void gl_shader_int::update(int value, std::size_t index) const
{
glUniform1i(gl_uniform_location + static_cast<GLint>(index), value);
}
void gl_shader_int::update(std::span<const int> values, std::size_t index) const
{
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data());
}
gl_shader_int2::gl_shader_int2(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_int2::update(const int2& value) const noexcept
{
glUniform2iv(gl_uniform_location, 1, value.data());
}
void gl_shader_int2::update(const int2& value, std::size_t index) const
{
glUniform2iv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_int2::update(std::span<const int2> values, std::size_t index) const
{
glUniform2iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_int3::gl_shader_int3(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_int3::update(const int3& value) const noexcept
{
glUniform3iv(gl_uniform_location, 1, value.data());
}
void gl_shader_int3::update(const int3& value, std::size_t index) const
{
glUniform3iv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_int3::update(std::span<const int3> values, std::size_t index) const
{
glUniform3iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_int4::gl_shader_int4(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_int4::update(const int4& value) const noexcept
{
glUniform4iv(gl_uniform_location, 1, value.data());
}
void gl_shader_int4::update(const int4& value, std::size_t index) const
{
glUniform4iv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_int4::update(std::span<const int4> values, std::size_t index) const
{
glUniform4iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_uint::gl_shader_uint(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_uint::update(unsigned int value) const noexcept
{
glUniform1ui(gl_uniform_location, value);
}
void gl_shader_uint::update(unsigned int value, std::size_t index) const
{
glUniform1ui(gl_uniform_location + static_cast<GLint>(index), value);
}
void gl_shader_uint::update(std::span<const unsigned int> values, std::size_t index) const
{
glUniform1uiv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data());
}
gl_shader_uint2::gl_shader_uint2(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_uint2::update(const uint2& value) const noexcept
{
glUniform2uiv(gl_uniform_location, 1, value.data());
}
void gl_shader_uint2::update(const uint2& value, std::size_t index) const
{
glUniform2uiv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_uint2::update(std::span<const uint2> values, std::size_t index) const
{
glUniform2uiv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_uint3::gl_shader_uint3(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_uint3::update(const uint3& value) const noexcept
{
glUniform3uiv(gl_uniform_location, 1, value.data());
}
void gl_shader_uint3::update(const uint3& value, std::size_t index) const
{
glUniform3uiv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_uint3::update(std::span<const uint3> values, std::size_t index) const
{
glUniform3uiv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_uint4::gl_shader_uint4(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_uint4::update(const uint4& value) const noexcept
{
glUniform4uiv(gl_uniform_location, 1, value.data());
}
void gl_shader_uint4::update(const uint4& value, std::size_t index) const
{
glUniform4uiv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_uint4::update(std::span<const uint4> values, std::size_t index) const
{
glUniform4uiv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_float::gl_shader_float(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float::update(float value) const noexcept
{
glUniform1f(gl_uniform_location, value);
}
void gl_shader_float::update(float value, std::size_t index) const
{
glUniform1f(gl_uniform_location + static_cast<GLint>(index), value);
}
void gl_shader_float::update(std::span<const float> values, std::size_t index) const
{
glUniform1fv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data());
}
gl_shader_float2::gl_shader_float2(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float2::update(const float2& value) const noexcept
{
glUniform2fv(gl_uniform_location, 1, value.data());
}
void gl_shader_float2::update(const float2& value, std::size_t index) const
{
glUniform2fv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_float2::update(std::span<const float2> values, std::size_t index) const
{
glUniform2fv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_float3::gl_shader_float3(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float3::update(const float3& value) const noexcept
{
glUniform3fv(gl_uniform_location, 1, value.data());
}
void gl_shader_float3::update(const float3& value, std::size_t index) const
{
glUniform3fv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_float3::update(std::span<const float3> values, std::size_t index) const
{
glUniform3fv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_float4::gl_shader_float4(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float4::update(const float4& value) const noexcept
{
glUniform4fv(gl_uniform_location, 1, value.data());
}
void gl_shader_float4::update(const float4& value, std::size_t index) const
{
glUniform4fv(gl_uniform_location + static_cast<GLint>(index), 1, value.data());
}
void gl_shader_float4::update(std::span<const float4> values, std::size_t index) const
{
glUniform4fv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), values.data()->data());
}
gl_shader_float2x2::gl_shader_float2x2(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float2x2::update(const float2x2& value) const noexcept
{
glUniformMatrix2fv(gl_uniform_location, 1, GL_FALSE, value.data());
}
void gl_shader_float2x2::update(const float2x2& value, std::size_t index) const
{
glUniformMatrix2fv(gl_uniform_location + static_cast<GLint>(index) * 2, 1, GL_FALSE, value.data());
}
void gl_shader_float2x2::update(std::span<const float2x2> values, std::size_t index) const
{
glUniformMatrix2fv(gl_uniform_location + static_cast<GLint>(index) * 2, static_cast<GLsizei>(values.size()), GL_FALSE, values.data()->data());
}
gl_shader_float3x3::gl_shader_float3x3(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float3x3::update(const float3x3& value) const noexcept
{
glUniformMatrix3fv(gl_uniform_location, 1, GL_FALSE, value.data());
}
void gl_shader_float3x3::update(const float3x3& value, std::size_t index) const
{
glUniformMatrix3fv(gl_uniform_location + static_cast<GLint>(index) * 3, 1, GL_FALSE, value.data());
}
void gl_shader_float3x3::update(std::span<const float3x3> values, std::size_t index) const
{
glUniformMatrix3fv(gl_uniform_location + static_cast<GLint>(index) * 3, static_cast<GLsizei>(values.size()), GL_FALSE, values.data()->data());
}
gl_shader_float4x4::gl_shader_float4x4(std::size_t size, GLint gl_uniform_location):
shader_variable(size),
gl_uniform_location{gl_uniform_location}
{}
void gl_shader_float4x4::update(const float4x4& value) const noexcept
{
glUniformMatrix4fv(gl_uniform_location, 1, GL_FALSE, value.data());
}
void gl_shader_float4x4::update(const float4x4& value, std::size_t index) const
{
glUniformMatrix4fv(gl_uniform_location + static_cast<GLint>(index) * 4, 1, GL_FALSE, value.data());
}
void gl_shader_float4x4::update(std::span<const float4x4> values, std::size_t index) const
{
glUniformMatrix4fv(gl_uniform_location + static_cast<GLint>(index) * 4, static_cast<GLsizei>(values.size()), GL_FALSE, values.data()->data());
}
gl_shader_texture_1d::gl_shader_texture_1d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
gl_texture_unit_indices(size)
{
std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index);
}
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
}
void gl_shader_texture_1d::update(const texture_1d& value, std::size_t index) const
{
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
}
void gl_shader_texture_1d::update(std::span<const texture_1d* const> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
void gl_shader_texture_1d::update(std::span<const std::shared_ptr<texture_1d>> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
gl_shader_texture_2d::gl_shader_texture_2d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
gl_texture_unit_indices(size)
{
std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index);
}
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
}
void gl_shader_texture_2d::update(const texture_2d& value, std::size_t index) const
{
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
}
void gl_shader_texture_2d::update(std::span<const texture_2d* const> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
void gl_shader_texture_2d::update(std::span<const std::shared_ptr<texture_2d>> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
gl_shader_texture_3d::gl_shader_texture_3d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
gl_texture_unit_indices(size)
{
std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index);
}
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
}
void gl_shader_texture_3d::update(const texture_3d& value, std::size_t index) const
{
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
}
void gl_shader_texture_3d::update(std::span<const texture_3d* const> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
void gl_shader_texture_3d::update(std::span<const std::shared_ptr<texture_3d>> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
gl_shader_texture_cube::gl_shader_texture_cube(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index):
shader_variable(size),
gl_uniform_location{gl_uniform_location},
gl_texture_unit_indices(size)
{
std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index);
}
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location, gl_texture_unit_indices.front());
}
void gl_shader_texture_cube::update(const texture_cube& value, std::size_t index) const
{
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.gl_texture_id);
// Pass texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<GLint>(index), gl_texture_index);
}
void gl_shader_texture_cube::update(std::span<const texture_cube* const> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
void gl_shader_texture_cube::update(std::span<const std::shared_ptr<texture_cube>> values, std::size_t index) const
{
// 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]->gl_texture_id);
}
// Pass texture unit indices to shader
glUniform1iv(gl_uniform_location + static_cast<GLint>(index), static_cast<GLsizei>(values.size()), &gl_texture_unit_indices[index]);
}
} // namespace gl

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

@ -0,0 +1,526 @@
/*
* 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_SHADER_VARIABLES_HPP
#define ANTKEEPER_GL_GL_SHADER_VARIABLES_HPP
#include <engine/gl/shader-variable.hpp>
#include <glad/glad.h>
#include <vector>
namespace gl {
/**
* Boolean shader variable implementation using OpenGL.
*/
class gl_shader_bool: public shader_variable
{
public:
gl_shader_bool(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::bool1;
}
void update(bool value) const noexcept override;
void update(bool value, std::size_t index) const override;
void update(std::span<const bool> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
mutable std::vector<GLint> ivalues;
};
/**
* 2-dimensional boolean vector shader variable implementation using OpenGL.
*/
class gl_shader_bool2: public shader_variable
{
public:
gl_shader_bool2(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::bool2;
}
void update(const bool2& value) const noexcept override;
void update(const bool2& value, std::size_t index) const override;
void update(std::span<const bool2> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
mutable std::vector<GLint> ivalues;
};
/**
* 3-dimensional boolean vector shader variable implementation using OpenGL.
*/
class gl_shader_bool3: public shader_variable
{
public:
gl_shader_bool3(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::bool3;
}
void update(const bool3& value) const noexcept override;
void update(const bool3& value, std::size_t index) const override;
void update(std::span<const bool3> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
mutable std::vector<GLint> ivalues;
};
/**
* 4-dimensional boolean vector shader variable implementation using OpenGL.
*/
class gl_shader_bool4: public shader_variable
{
public:
gl_shader_bool4(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::bool4;
}
void update(const bool4& value) const noexcept override;
void update(const bool4& value, std::size_t index) const override;
void update(std::span<const bool4> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
mutable std::vector<GLint> ivalues;
};
/**
* Signed integer shader variable implementation using OpenGL.
*/
class gl_shader_int: public shader_variable
{
public:
gl_shader_int(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::int1;
}
void update(int value) const noexcept override;
void update(int value, std::size_t index) const override;
void update(std::span<const int> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 2-dimensional signed integer vector shader variable implementation using OpenGL.
*/
class gl_shader_int2: public shader_variable
{
public:
gl_shader_int2(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::int2;
}
void update(const int2& value) const noexcept override;
void update(const int2& value, std::size_t index) const override;
void update(std::span<const int2> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 3-dimensional signed integer vector shader variable implementation using OpenGL.
*/
class gl_shader_int3: public shader_variable
{
public:
gl_shader_int3(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::int3;
}
void update(const int3& value) const noexcept override;
void update(const int3& value, std::size_t index) const override;
void update(std::span<const int3> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 4-dimensional signed integer vector shader variable implementation using OpenGL.
*/
class gl_shader_int4: public shader_variable
{
public:
gl_shader_int4(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::int4;
}
void update(const int4& value) const noexcept override;
void update(const int4& value, std::size_t index) const override;
void update(std::span<const int4> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* Unsigned integer shader variable implementation using OpenGL.
*/
class gl_shader_uint: public shader_variable
{
public:
gl_shader_uint(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::uint1;
}
void update(unsigned int value) const noexcept override;
void update(unsigned int value, std::size_t index) const override;
void update(std::span<const unsigned int> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 2-dimensional unsigned integer vector shader variable implementation using OpenGL.
*/
class gl_shader_uint2: public shader_variable
{
public:
gl_shader_uint2(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::uint2;
}
void update(const uint2& value) const noexcept override;
void update(const uint2& value, std::size_t index) const override;
void update(std::span<const uint2> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 3-dimensional unsigned integer vector shader variable implementation using OpenGL.
*/
class gl_shader_uint3: public shader_variable
{
public:
gl_shader_uint3(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::uint3;
}
void update(const uint3& value) const noexcept override;
void update(const uint3& value, std::size_t index) const override;
void update(std::span<const uint3> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 4-dimensional unsigned integer vector shader variable implementation using OpenGL.
*/
class gl_shader_uint4: public shader_variable
{
public:
gl_shader_uint4(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::uint4;
}
void update(const uint4& value) const noexcept override;
void update(const uint4& value, std::size_t index) const override;
void update(std::span<const uint4> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* Floating-point shader variable implementation using OpenGL.
*/
class gl_shader_float: public shader_variable
{
public:
gl_shader_float(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float1;
}
void update(float value) const noexcept override;
void update(float value, std::size_t index) const override;
void update(std::span<const float> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 2-dimensional floating-point vector shader variable implementation using OpenGL.
*/
class gl_shader_float2: public shader_variable
{
public:
gl_shader_float2(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float2;
}
void update(const float2& value) const noexcept override;
void update(const float2& value, std::size_t index) const override;
void update(std::span<const float2> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 3-dimensional floating-point vector shader variable implementation using OpenGL.
*/
class gl_shader_float3: public shader_variable
{
public:
gl_shader_float3(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float3;
}
void update(const float3& value) const noexcept override;
void update(const float3& value, std::size_t index) const override;
void update(std::span<const float3> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 4-dimensional floating-point vector shader variable implementation using OpenGL.
*/
class gl_shader_float4: public shader_variable
{
public:
gl_shader_float4(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float4;
}
void update(const float4& value) const noexcept override;
void update(const float4& value, std::size_t index) const override;
void update(std::span<const float4> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 2x2 floating-point matrix shader variable implementation using OpenGL.
*/
class gl_shader_float2x2: public shader_variable
{
public:
gl_shader_float2x2(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float2x2;
}
void update(const float2x2& value) const noexcept override;
void update(const float2x2& value, std::size_t index) const override;
void update(std::span<const float2x2> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 3x3 floating-point matrix shader variable implementation using OpenGL.
*/
class gl_shader_float3x3: public shader_variable
{
public:
gl_shader_float3x3(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float3x3;
}
void update(const float3x3& value) const noexcept override;
void update(const float3x3& value, std::size_t index) const override;
void update(std::span<const float3x3> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 4x4 floating-point matrix shader variable implementation using OpenGL.
*/
class gl_shader_float4x4: public shader_variable
{
public:
gl_shader_float4x4(std::size_t size, GLint gl_uniform_location);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::float4x4;
}
void update(const float4x4& value) const noexcept override;
void update(const float4x4& value, std::size_t index) const override;
void update(std::span<const float4x4> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
};
/**
* 1-dimensional texture shader variable implementation using OpenGL.
*/
class gl_shader_texture_1d: public shader_variable
{
public:
gl_shader_texture_1d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_index);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::texture_1d;
}
void update(const texture_1d& value) const noexcept override;
void update(const texture_1d& value, std::size_t index) const override;
void update(std::span<const texture_1d* const> values, std::size_t index = 0) const override;
void update(std::span<const std::shared_ptr<texture_1d>> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
std::vector<GLint> gl_texture_unit_indices;
};
/**
* 2-dimensional texture shader variable implementation using OpenGL.
*/
class gl_shader_texture_2d: public shader_variable
{
public:
gl_shader_texture_2d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::texture_2d;
}
void update(const texture_2d& value) const noexcept override;
void update(const texture_2d& value, std::size_t index) const override;
void update(std::span<const texture_2d* const> values, std::size_t index = 0) const override;
void update(std::span<const std::shared_ptr<texture_2d>> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
std::vector<GLint> gl_texture_unit_indices;
};
/**
* 3-dimensional texture shader variable implementation using OpenGL.
*/
class gl_shader_texture_3d: public shader_variable
{
public:
gl_shader_texture_3d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_index);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::texture_3d;
}
void update(const texture_3d& value) const noexcept override;
void update(const texture_3d& value, std::size_t index) const override;
void update(std::span<const texture_3d* const> values, std::size_t index = 0) const override;
void update(std::span<const std::shared_ptr<texture_3d>> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
std::vector<GLint> gl_texture_unit_indices;
};
/**
* Cube texture shader variable implementation using OpenGL.
*/
class gl_shader_texture_cube: public shader_variable
{
public:
gl_shader_texture_cube(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_index);
[[nodiscard]] inline constexpr shader_variable_type type() const noexcept override
{
return shader_variable_type::texture_cube;
}
void update(const texture_cube& value) const noexcept override;
void update(const texture_cube& value, std::size_t index) const override;
void update(std::span<const texture_cube* const> values, std::size_t index = 0) const override;
void update(std::span<const std::shared_ptr<texture_cube>> values, std::size_t index = 0) const override;
private:
GLint gl_uniform_location;
std::vector<GLint> gl_texture_unit_indices;
};
} // namespace gl
#endif // ANTKEEPER_GL_GL_SHADER_VARIABLES_HPP

+ 26
- 10
src/engine/gl/pixel-format.hpp View File

@ -20,21 +20,37 @@
#ifndef ANTKEEPER_GL_PIXEL_FORMAT_HPP
#define ANTKEEPER_GL_PIXEL_FORMAT_HPP
#include <cstdint>
namespace gl {
enum class pixel_format
enum class pixel_format: std::uint8_t
{
d, ///< Depth
ds, ///< Depth, stencil
r, ///< Red
rg, ///< Red, green
rgb, ///< Red, green, blue
bgr, ///< Blue, green, red
rgba, ///< Red, green, blue, alpha
bgra ///< Blue, green, red, alpha
/// Depth
d,
/// Depth, stencil
ds,
/// Red
r,
/// Red, green
rg,
/// Red, green, blue
rgb,
/// Blue, green, red
bgr,
/// Red, green, blue, alpha
rgba,
///< Blue, green, red, alpha
bgra
};
} // namespace gl
#endif // ANTKEEPER_GL_PIXEL_FORMAT_HPP

+ 3
- 2
src/engine/gl/pixel-type.hpp View File

@ -20,9 +20,11 @@
#ifndef ANTKEEPER_GL_PIXEL_TYPE_HPP
#define ANTKEEPER_GL_PIXEL_TYPE_HPP
#include <cstdint>
namespace gl {
enum class pixel_type
enum class pixel_type: std::uint8_t
{
int_8,
uint_8,
@ -37,4 +39,3 @@ enum class pixel_type
} // namespace gl
#endif // ANTKEEPER_GL_PIXEL_TYPE_HPP

+ 3
- 5
src/engine/gl/rasterizer.cpp View File

@ -56,18 +56,16 @@ rasterizer::rasterizer():
glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
// Setup default framebuffer
default_framebuffer = new framebuffer();
default_framebuffer = std::make_unique<framebuffer>();
default_framebuffer->gl_framebuffer_id = 0;
default_framebuffer->dimensions = {scissor_box[2], scissor_box[3]};
// Bind default framebuffer
bound_framebuffer = default_framebuffer;
bound_framebuffer = default_framebuffer.get();
}
rasterizer::~rasterizer()
{
delete default_framebuffer;
}
{}
void rasterizer::context_resized(int width, int height)
{

+ 8
- 10
src/engine/gl/rasterizer.hpp View File

@ -20,15 +20,16 @@
#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;
enum class drawing_mode;
enum class element_array_type;
/**
* Interface to the OpenGL state and drawing functions.
@ -121,21 +122,18 @@ public:
/**
* Returns the default framebuffer associated with the OpenGL context of a window.
*/
const framebuffer& get_default_framebuffer() const;
[[nodiscard]] inline const framebuffer& get_default_framebuffer() const noexcept
{
return *default_framebuffer;
}
private:
framebuffer* default_framebuffer;
std::unique_ptr<framebuffer> default_framebuffer;
const framebuffer* bound_framebuffer;
const vertex_array* bound_vao;
const shader_program* bound_shader_program;
};
inline const framebuffer& rasterizer::get_default_framebuffer() const
{
return *default_framebuffer;
}
} // namespace gl
#endif // ANTKEEPER_GL_RASTERIZER_HPP

+ 0
- 773
src/engine/gl/shader-input.cpp View File

@ -1,773 +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/shader-input.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 <glad/glad.h>
namespace gl {
shader_input::shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit):
program(program),
input_index(input_index),
gl_uniform_location(gl_uniform_location),
name(name),
data_type(data_type),
element_count(element_count),
texture_unit(texture_unit)
{}
shader_input::~shader_input()
{}
bool shader_input::upload(const bool& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1i(gl_uniform_location, static_cast<GLint>(value));
return true;
}
bool shader_input::upload(const bool2& value) const
{
if (gl_uniform_location == -1)
return false;
const GLint values[] = {value[0], value[1]};
glUniform2iv(gl_uniform_location, 1, values);
return true;
}
bool shader_input::upload(const bool3& value) const
{
if (gl_uniform_location == -1)
return false;
const GLint values[] = {value[0], value[1], value[2]};
glUniform3iv(gl_uniform_location, 1, values);
return true;
}
bool shader_input::upload(const bool4& value) const
{
if (gl_uniform_location == -1)
return false;
const GLint values[] = {value[0], value[1], value[2], value[3]};
glUniform4iv(gl_uniform_location, 1, values);
return true;
}
bool shader_input::upload(const int& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1i(gl_uniform_location, value);
return true;
}
bool shader_input::upload(const int2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform2iv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const int3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform3iv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const int4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform4iv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const unsigned int& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1ui(gl_uniform_location, value);
return true;
}
bool shader_input::upload(const uint2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform2uiv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const uint3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform3uiv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const uint4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform4uiv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const float& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1f(gl_uniform_location, value);
return true;
}
bool shader_input::upload(const float2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform2fv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const float3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform3fv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const float4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform4fv(gl_uniform_location, 1, value.data());
return true;
}
bool shader_input::upload(const float2x2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix2fv(gl_uniform_location, 1, GL_FALSE, value[0].data());
return true;
}
bool shader_input::upload(const float3x3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix3fv(gl_uniform_location, 1, GL_FALSE, value[0].data());
return true;
}
bool shader_input::upload(const float4x4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix4fv(gl_uniform_location, 1, GL_FALSE, value[0].data());
return true;
}
bool shader_input::upload(const texture_1d* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_1D, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location, texture_unit);
return true;
}
bool shader_input::upload(const texture_2d* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location, texture_unit);
return true;
}
bool shader_input::upload(const texture_3d* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_3D, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location, texture_unit);
return true;
}
bool shader_input::upload(const texture_cube* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location, texture_unit);
return true;
}
bool shader_input::upload(std::size_t index, const bool& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1i(gl_uniform_location + static_cast<int>(index), static_cast<GLint>(value));
return true;
}
bool shader_input::upload(std::size_t index, const bool2& value) const
{
if (gl_uniform_location == -1)
return false;
const GLint values[] = {value[0], value[1]};
glUniform2iv(gl_uniform_location + static_cast<int>(index), 1, values);
return true;
}
bool shader_input::upload(std::size_t index, const bool3& value) const
{
if (gl_uniform_location == -1)
return false;
const GLint values[] = {value[0], value[1], value[3]};
glUniform3iv(gl_uniform_location + static_cast<int>(index), 1, values);
return true;
}
bool shader_input::upload(std::size_t index, const bool4& value) const
{
if (gl_uniform_location == -1)
return false;
const GLint values[] = {value[0], value[1], value[3], value[4]};
glUniform4iv(gl_uniform_location + static_cast<int>(index), 1, values);
return true;
}
bool shader_input::upload(std::size_t index, const int& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1i(gl_uniform_location + static_cast<int>(index), value);
return true;
}
bool shader_input::upload(std::size_t index, const int2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform2iv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const int3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform3iv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const int4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform4iv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const unsigned int& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1ui(gl_uniform_location + static_cast<int>(index), value);
return true;
}
bool shader_input::upload(std::size_t index, const uint2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform2uiv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const uint3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform3uiv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const uint4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform4uiv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const float& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform1f(gl_uniform_location + static_cast<int>(index), value);
return true;
}
bool shader_input::upload(std::size_t index, const float2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform2fv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const float3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform3fv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const float4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniform4fv(gl_uniform_location + static_cast<int>(index), 1, value.data());
return true;
}
bool shader_input::upload(std::size_t index, const float2x2& value) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix2fv(gl_uniform_location + static_cast<int>(index) * 2, 1, GL_FALSE, value[0].data());
return true;
}
bool shader_input::upload(std::size_t index, const float3x3& value) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix3fv(gl_uniform_location + static_cast<int>(index) * 3, 1, GL_FALSE, value[0].data());
return true;
}
bool shader_input::upload(std::size_t index, const float4x4& value) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix4fv(gl_uniform_location + static_cast<int>(index) * 4, 1, GL_FALSE, value[0].data());
return true;
}
bool shader_input::upload(std::size_t index, const texture_1d* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index));
glBindTexture(GL_TEXTURE_1D, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index), texture_unit + static_cast<int>(index));
return true;
}
bool shader_input::upload(std::size_t index, const texture_2d* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index));
glBindTexture(GL_TEXTURE_2D, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index), texture_unit + static_cast<int>(index));
return true;
}
bool shader_input::upload(std::size_t index, const texture_3d* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index));
glBindTexture(GL_TEXTURE_3D, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index), texture_unit + static_cast<int>(index));
return true;
}
bool shader_input::upload(std::size_t index, const texture_cube* value) const
{
if (gl_uniform_location == -1)
return false;
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index));
glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index), texture_unit + static_cast<int>(index));
return true;
}
bool shader_input::upload(std::size_t index, const bool* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
int* int_values = new int[count];
for (std::size_t i = 0; i < count; ++i)
int_values[i] = values[i];
glUniform1iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &(*int_values));
delete[] int_values;
return true;
}
bool shader_input::upload(std::size_t index, const bool2* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
int2* int2_values = new int2[count];
for (std::size_t i = 0; i < count; ++i)
int2_values[i] = {values[i][0], values[i][1]};
glUniform2iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &((*int2_values)[0]));
delete[] int2_values;
return true;
}
bool shader_input::upload(std::size_t index, const bool3* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
int3* int3_values = new int3[count];
for (std::size_t i = 0; i < count; ++i)
int3_values[i] = {values[i][0], values[i][1], values[i][2]};
glUniform3iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &((*int3_values)[0]));
delete[] int3_values;
return true;
}
bool shader_input::upload(std::size_t index, const bool4* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
int4* int4_values = new int4[count];
for (std::size_t i = 0; i < count; ++i)
int4_values[i] = {values[i][0], values[i][1], values[i][2], values[i][3]};
glUniform4iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &((*int4_values)[0]));
delete[] int4_values;
return true;
}
bool shader_input::upload(std::size_t index, const int* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform1iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &(*values));
return true;
}
bool shader_input::upload(std::size_t index, const int2* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform2iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const int3* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform3iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const int4* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform4iv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const unsigned int* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform1uiv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &(*values));
return true;
}
bool shader_input::upload(std::size_t index, const uint2* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform2uiv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const uint3* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform3uiv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const uint4* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform4uiv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const float* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform1fv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), &(*values));
return true;
}
bool shader_input::upload(std::size_t index, const float2* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform2fv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const float3* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform3fv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const float4* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniform4fv(gl_uniform_location + static_cast<int>(index), static_cast<GLsizei>(count), (*values).data());
return true;
}
bool shader_input::upload(std::size_t index, const float2x2* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix2fv(gl_uniform_location + static_cast<int>(index) * 2, static_cast<GLsizei>(count), GL_FALSE, (*values)[0].data());
return true;
}
bool shader_input::upload(std::size_t index, const float3x3* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix3fv(gl_uniform_location + static_cast<int>(index) * 3, static_cast<GLsizei>(count), GL_FALSE, (*values)[0].data());
return true;
}
bool shader_input::upload(std::size_t index, const float4x4* values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
glUniformMatrix4fv(gl_uniform_location + static_cast<int>(index) * 4, static_cast<GLsizei>(count), GL_FALSE, (*values)[0].data());
return true;
}
bool shader_input::upload(std::size_t index, const texture_1d** values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
for (std::size_t i = 0; i < count; ++i)
{
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index + i));
glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index + i), texture_unit + static_cast<int>(index + i));
}
return true;
}
bool shader_input::upload(std::size_t index, const texture_2d** values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
for (std::size_t i = 0; i < count; ++i)
{
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index + i));
glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index + i), texture_unit + static_cast<int>(index + i));
}
return true;
}
bool shader_input::upload(std::size_t index, const texture_3d** values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
for (std::size_t i = 0; i < count; ++i)
{
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index + i));
glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index + i), texture_unit + static_cast<int>(index + i));
}
return true;
}
bool shader_input::upload(std::size_t index, const texture_cube** values, std::size_t count) const
{
if (gl_uniform_location == -1)
return false;
for (std::size_t i = 0; i < count; ++i)
{
// Bind texture to a texture unit reserved by this shader input
glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast<int>(index + i));
glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id);
// Upload texture unit index to shader
glUniform1i(gl_uniform_location + static_cast<int>(index + i), texture_unit + static_cast<int>(index + i));
}
return true;
}
} // namespace gl

+ 0
- 202
src/engine/gl/shader-input.hpp View File

@ -1,202 +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_SHADER_INPUT_HPP
#define ANTKEEPER_GL_SHADER_INPUT_HPP
#include <engine/utility/fundamental-types.hpp>
#include <string>
namespace gl {
class shader_program;
class texture_1d;
class texture_2d;
class texture_3d;
class texture_cube;
enum class shader_variable_type;
/**
* Port through which data can be uploaded to shader variables.
*/
class shader_input
{
public:
/**
* Returns the type of data which can be passed through this input.
*/
shader_variable_type get_data_type() const;
/**
* Returns `true` if the input data is stored in an array.
*/
bool is_array() const;
/**
* Returns the number of elements the array can contain, or `1` if the data is not stored in an array.
*/
std::size_t get_element_count() const;
/**
* Uploads a value to the shader.
*
* @param value Value to upload.
* @return `true` if the value was uploaded successfully, `false` otherwise.
*/
///@{
bool upload(const bool& value) const;
bool upload(const bool2& value) const;
bool upload(const bool3& value) const;
bool upload(const bool4& value) const;
bool upload(const int& value) const;
bool upload(const int2& value) const;
bool upload(const int3& value) const;
bool upload(const int4& value) const;
bool upload(const unsigned int& value) const;
bool upload(const uint2& value) const;
bool upload(const uint3& value) const;
bool upload(const uint4& value) const;
bool upload(const float& value) const;
bool upload(const float2& value) const;
bool upload(const float3& value) const;
bool upload(const float4& value) const;
bool upload(const float2x2& value) const;
bool upload(const float3x3& value) const;
bool upload(const float4x4& value) const;
bool upload(const texture_1d* value) const;
bool upload(const texture_2d* value) const;
bool upload(const texture_3d* value) const;
bool upload(const texture_cube* value) const;
///@}
/**
* Uploads a single array element to the shader.
*
* @param index Index of an array element.
* @param values Value to upload.
* @return `true` if the value was uploaded successfully, `false` otherwise.
*/
///@{
bool upload(std::size_t index, const bool& value) const;
bool upload(std::size_t index, const bool2& value) const;
bool upload(std::size_t index, const bool3& value) const;
bool upload(std::size_t index, const bool4& value) const;
bool upload(std::size_t index, const int& value) const;
bool upload(std::size_t index, const int2& value) const;
bool upload(std::size_t index, const int3& value) const;
bool upload(std::size_t index, const int4& value) const;
bool upload(std::size_t index, const unsigned int& value) const;
bool upload(std::size_t index, const uint2& value) const;
bool upload(std::size_t index, const uint3& value) const;
bool upload(std::size_t index, const uint4& value) const;
bool upload(std::size_t index, const float& value) const;
bool upload(std::size_t index, const float2& value) const;
bool upload(std::size_t index, const float3& value) const;
bool upload(std::size_t index, const float4& value) const;
bool upload(std::size_t index, const float2x2& value) const;
bool upload(std::size_t index, const float3x3& value) const;
bool upload(std::size_t index, const float4x4& value) const;
bool upload(std::size_t index, const texture_1d* value) const;
bool upload(std::size_t index, const texture_2d* value) const;
bool upload(std::size_t index, const texture_3d* value) const;
bool upload(std::size_t index, const texture_cube* value) const;
///@}
/**
* Uploads a range of array elements to the shader.
*
* @param index Index of the first array element.
* @param values Pointer to an array of values.
* @param count Number of elements to upload.
* @return `true` if the value was fed successfully, `false` otherwise.
*/
///@{
bool upload(std::size_t index, const bool* values, std::size_t count) const;
bool upload(std::size_t index, const bool2* values, std::size_t count) const;
bool upload(std::size_t index, const bool3* values, std::size_t count) const;
bool upload(std::size_t index, const bool4* values, std::size_t count) const;
bool upload(std::size_t index, const int* values, std::size_t count) const;
bool upload(std::size_t index, const int2* values, std::size_t count) const;
bool upload(std::size_t index, const int3* values, std::size_t count) const;
bool upload(std::size_t index, const int4* values, std::size_t count) const;
bool upload(std::size_t index, const unsigned int* values, std::size_t count) const;
bool upload(std::size_t index, const uint2* values, std::size_t count) const;
bool upload(std::size_t index, const uint3* values, std::size_t count) const;
bool upload(std::size_t index, const uint4* values, std::size_t count) const;
bool upload(std::size_t index, const float* values, std::size_t count) const;
bool upload(std::size_t index, const float2* values, std::size_t count) const;
bool upload(std::size_t index, const float3* values, std::size_t count) const;
bool upload(std::size_t index, const float4* values, std::size_t count) const;
bool upload(std::size_t index, const float2x2* values, std::size_t count) const;
bool upload(std::size_t index, const float3x3* values, std::size_t count) const;
bool upload(std::size_t index, const float4x4* values, std::size_t count) const;
bool upload(std::size_t index, const texture_1d** values, std::size_t count) const;
bool upload(std::size_t index, const texture_2d** values, std::size_t count) const;
bool upload(std::size_t index, const texture_3d** values, std::size_t count) const;
bool upload(std::size_t index, const texture_cube** values, std::size_t count) const;
///@}
private:
friend class shader_program;
/**
* Creates a shader input.
*
* @param program Shader program with which this input is associated.
* @param gl_uniform_location Location of the shader uniform with which this shader input is associated.
* @param name Name of the input.
* @param data_type Type of data which can be passed through this input.
* @param element_count Number of elements which the array can contain, or `0` if input data is not stored in an array.
* @param texture_unit Texture unit to which texture shader variables can be bound, or `-1` if the data type is not a texture type.
*/
shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit);
/**
* Destroys a shader input.
*/
~shader_input();
shader_program* program;
std::size_t input_index;
int gl_uniform_location;
std::string name;
shader_variable_type data_type;
std::size_t element_count;
int texture_unit;
};
inline shader_variable_type shader_input::get_data_type() const
{
return data_type;
}
inline bool shader_input::is_array() const
{
return (element_count > 1);
}
inline std::size_t shader_input::get_element_count() const
{
return element_count;
}
} // namespace gl
#endif // ANTKEEPER_GL_SHADER_INPUT_HPP

+ 11
- 18
src/engine/gl/shader-object.cpp View File

@ -31,34 +31,29 @@ static constexpr GLenum gl_shader_type_lut[] =
};
shader_object::shader_object(shader_stage stage):
gl_shader_id(0),
stage(stage),
compiled(false)
m_stage{stage}
{
// Look up OpenGL shader type enumeration that corresponds to the given stage
GLenum gl_shader_type = gl_shader_type_lut[static_cast<std::size_t>(stage)];
const GLenum gl_shader_type = gl_shader_type_lut[static_cast<std::size_t>(m_stage)];
// Create an OpenGL shader object
gl_shader_id = glCreateShader(gl_shader_type);
// Handle OpenGL errors
if (!gl_shader_id)
{
throw std::runtime_error("An error occurred while creating an OpenGL shader object.");
throw std::runtime_error("Unable to create OpenGL shader object");
}
}
shader_object::~shader_object()
{
// Flag the OpenGL shader object for deletion
glDeleteShader(gl_shader_id);
}
void shader_object::source(const std::string& source_code)
void shader_object::source(std::string_view source_code)
{
// Replace OpenGL shader object source code
GLint gl_length = static_cast<GLint>(source_code.length());
const GLchar* gl_string = source_code.c_str();
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
@ -76,6 +71,9 @@ void shader_object::source(const std::string& source_code)
bool shader_object::compile()
{
m_compiled = false;
info_log.clear();
// Compile OpenGL shader object
glCompileShader(gl_shader_id);
@ -94,7 +92,7 @@ bool shader_object::compile()
// Get OpenGL shader object compilation status
GLint gl_compile_status;
glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &gl_compile_status);
compiled = (gl_compile_status == GL_TRUE);
m_compiled = (gl_compile_status == GL_TRUE);
// Get OpenGL shader object info log length
GLint gl_info_log_length;
@ -111,14 +109,9 @@ bool shader_object::compile()
// Remove redundant null terminator from string
info_log.pop_back();
}
else
{
// Empty info log
info_log.clear();
}
// Return compilation status
return compiled;
return m_compiled;
}
} // namespace gl

+ 18
- 23
src/engine/gl/shader-object.hpp View File

@ -23,6 +23,7 @@
#include <engine/gl/shader-stage.hpp>
#include <cstddef>
#include <string>
#include <string_view>
namespace gl {
@ -44,7 +45,7 @@ public:
*
* @exception std::runtime_error An error occurred while creating an OpenGL shader object.
*/
shader_object(shader_stage stage);
explicit shader_object(shader_stage stage);
/**
* Destroys a shader object.
@ -59,7 +60,7 @@ public:
* @exception std::runtime_error Shader object handle is not a value generated by OpenGL.
* @exception std::runtime_error Shader object handle is not a shader object.
*/
void source(const std::string& source_code);
void source(std::string_view source_code);
/**
* Compiles the shader object.
@ -74,13 +75,22 @@ public:
bool compile();
/// Returns the shader stage of this shader object.
shader_stage get_stage() const;
[[nodiscard]] inline shader_stage stage() const noexcept
{
return m_stage;
}
/// Returns the shader object info log, which is updated when the shader object is compiled.
const std::string& get_info_log() const;
[[nodiscard]] inline const std::string& info() const noexcept
{
return info_log;
}
/// Returns `true` if the shader object has been successfully compiled, `false` otherwise.
bool was_compiled() const;
[[nodiscard]] inline bool compiled() const noexcept
{
return m_compiled;
}
shader_object(const shader_object&) = delete;
shader_object& operator=(const shader_object&) = delete;
@ -88,27 +98,12 @@ public:
private:
friend class shader_program;
unsigned int gl_shader_id;
shader_stage stage;
unsigned int gl_shader_id{0};
shader_stage m_stage;
bool m_compiled{false};
std::string info_log;
bool compiled;
};
inline shader_stage shader_object::get_stage() const
{
return stage;
}
inline const std::string& shader_object::get_info_log() const
{
return info_log;
}
inline bool shader_object::was_compiled() const
{
return compiled;
}
} // namespace gl
#endif // ANTKEEPER_GL_SHADER_OBJECT_HPP

+ 121
- 133
src/engine/gl/shader-program.cpp View File

@ -19,16 +19,14 @@
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-object.hpp>
#include <engine/gl/shader-variable-type.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/gl/opengl/gl-shader-variables.hpp>
#include <glad/glad.h>
#include <stdexcept>
#include <string_view>
namespace gl {
shader_program::shader_program():
gl_program_id(0),
linked(false)
shader_program::shader_program()
{
// Create an OpenGL shader program
gl_program_id = glCreateProgram();
@ -36,15 +34,12 @@ shader_program::shader_program():
// Handle OpenGL errors
if (!gl_program_id)
{
throw std::runtime_error("An error occurred while creating an OpenGL shader program.");
throw std::runtime_error("Unable to create OpenGL shader program");
}
}
shader_program::~shader_program()
{
// Delete shader inputs
free_inputs();
// Detach all shader objects
detach_all();
@ -52,22 +47,26 @@ shader_program::~shader_program()
glDeleteProgram(gl_program_id);
}
void shader_program::attach(const shader_object* object)
void shader_program::attach(const shader_object& object)
{
if (attached_objects.find(object) != attached_objects.end())
if (attached_objects.find(&object) != attached_objects.end())
{
throw std::runtime_error("Shader object is already attached to the shader program.");
throw std::runtime_error("OpenGL shader object already attached to the shader program");
}
else
{
// Check that both the OpenGL shader program and OpenGL shader object are valid
if (glIsProgram(gl_program_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader program is not a valid program object.");
if (glIsShader(object->gl_shader_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader object is not a valid shader object.");
{
throw std::runtime_error("Invalid OpenGL shader program");
}
if (glIsShader(object.gl_shader_id) != GL_TRUE)
{
throw std::runtime_error("Invalid OpenGL shader object");
}
// Attach the OpenGL shader object to the OpenGL shader program
glAttachShader(gl_program_id, object->gl_shader_id);
glAttachShader(gl_program_id, object.gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
@ -78,13 +77,13 @@ void shader_program::attach(const shader_object* object)
}
// Add shader object to set of attached objects
attached_objects.insert(object);
attached_objects.insert(&object);
}
}
void shader_program::detach(const shader_object* object)
void shader_program::detach(const shader_object& object)
{
if (attached_objects.find(object) == attached_objects.end())
if (attached_objects.find(&object) == attached_objects.end())
{
throw std::runtime_error("Shader object is not attached to the shader program.");
}
@ -92,12 +91,16 @@ void shader_program::detach(const shader_object* object)
{
// Check that both the OpenGL shader program and OpenGL shader object are valid
if (glIsProgram(gl_program_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader program is not a valid program object.");
if (glIsShader(object->gl_shader_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader object is not a valid shader object.");
{
throw std::runtime_error("Invalid OpenGL shader program");
}
if (glIsShader(object.gl_shader_id) != GL_TRUE)
{
throw std::runtime_error("Invalid OpenGL shader object");
}
// Detach the OpenGL shader object from the OpenGL shader program
glDetachShader(gl_program_id, object->gl_shader_id);
glDetachShader(gl_program_id, object.gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
@ -108,21 +111,29 @@ void shader_program::detach(const shader_object* object)
}
// Remove shader object from set of attached objects
attached_objects.erase(object);
attached_objects.erase(&object);
}
}
void shader_program::detach_all()
{
while (!attached_objects.empty())
detach(*attached_objects.begin());
{
detach(**attached_objects.begin());
}
}
bool shader_program::link()
{
m_linked = false;
info_log.clear();
variable_map.clear();
// Check that the OpenGL shader program is valid
if (glIsProgram(gl_program_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader program is not a valid program object.");
{
throw std::runtime_error("Invalid OpenGL shader program");
}
// Link OpenGL shader program
glLinkProgram(gl_program_id);
@ -138,202 +149,179 @@ bool shader_program::link()
// Get OpenGL shader program linking status
GLint gl_link_status;
glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status);
linked = (gl_link_status == GL_TRUE);
m_linked = (gl_link_status == GL_TRUE);
// Get OpenGL shader program info log length
GLint gl_info_log_length;
glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length);
if (gl_info_log_length > 0)
// Populate info log string
if (m_linked)
{
// Resize string to accommodate OpenGL shader program info log
info_log.resize(gl_info_log_length);
// Read OpenGL shader program info log into string
glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data());
// Remove redundant null terminator from string
info_log.pop_back();
// Load shader variables
load_variables();
}
else
{
// Empty info log
info_log.clear();
// Get OpenGL shader program info log length
GLint gl_info_log_length;
glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length);
if (gl_info_log_length > 0)
{
// Resize string to accommodate OpenGL shader program info log
info_log.resize(gl_info_log_length);
// Read OpenGL shader program info log into string
glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data());
// Remove redundant null terminator from string
info_log.pop_back();
}
}
// Find shader inputs
find_inputs();
return linked;
return m_linked;
}
void shader_program::find_inputs()
void shader_program::load_variables()
{
variable_map.clear();
// Get maximum uniform name length
GLint max_uniform_name_length = 0;
glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_length);
// Allocate uniform name buffer
GLchar* uniform_name = new GLchar[max_uniform_name_length];
std::string uniform_name(static_cast<std::size_t>(max_uniform_name_length), '\0');
// Get number of active uniforms in the shader
GLint active_uniform_count = 0;
glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORMS, &active_uniform_count);
// Set first available texture unit
int available_texture_unit = 0;
// Init texture unit index
GLint texture_index = 0;
// For each active uniform
for (GLuint uniform_index = 0; uniform_index < static_cast<GLuint>(active_uniform_count); ++uniform_index)
for (GLint uniform_index = 0; uniform_index < active_uniform_count; ++uniform_index)
{
// Get information about uniform
// Get uniform info
GLsizei uniform_name_length;
GLint uniform_size;
GLenum uniform_type;
glGetActiveUniform(gl_program_id, uniform_index, static_cast<GLsizei>(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, &uniform_name[0]);
glGetActiveUniform(gl_program_id, static_cast<GLuint>(uniform_index), static_cast<GLsizei>(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, uniform_name.data());
// Get name without array symbols
std::string input_name = uniform_name;
std::size_t bracketPos = input_name.find_first_of("[");
if (bracketPos != std::string::npos)
// Get uniform location
const GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name.c_str());
if (uniform_location == -1)
{
input_name = input_name.substr(0, bracketPos);
throw std::runtime_error("Unable to get shader uniform location");
}
// Determine corresponding shader variable data type
shader_variable_type variable_type;
int texture_unit = -1;
bool unsupported = false;
// Get length of variable name by stripping array notation from uniform name
std::size_t uniform_base_name_length = static_cast<std::size_t>(uniform_name_length);
if (std::size_t i = std::string_view(uniform_name.c_str(), uniform_name_length).find_first_of("["); i != std::string::npos)
{
uniform_base_name_length = i;
}
// Hash variable name to get variable key
const hash::fnv1a32_t variable_key = hash::fnv1a32<char>({uniform_name.c_str(), uniform_base_name_length});
// Get size of variable
const std::size_t variable_size = static_cast<std::size_t>(uniform_size);
// Construct shader variable
std::unique_ptr<const gl::shader_variable> variable;
switch (uniform_type)
{
case GL_BOOL:
variable_type = shader_variable_type::bool1;
variable = std::make_unique<const gl_shader_bool>(variable_size, uniform_location);
break;
case GL_BOOL_VEC2:
variable_type = shader_variable_type::bool2;
variable = std::make_unique<const gl_shader_bool2>(variable_size, uniform_location);
break;
case GL_BOOL_VEC3:
variable_type = shader_variable_type::bool3;
variable = std::make_unique<const gl_shader_bool3>(variable_size, uniform_location);
break;
case GL_BOOL_VEC4:
variable_type = shader_variable_type::bool4;
variable = std::make_unique<const gl_shader_bool4>(variable_size, uniform_location);
break;
case GL_INT:
variable_type = shader_variable_type::int1;
variable = std::make_unique<const gl_shader_int>(variable_size, uniform_location);
break;
case GL_INT_VEC2:
variable_type = shader_variable_type::int2;
variable = std::make_unique<const gl_shader_int2>(variable_size, uniform_location);
break;
case GL_INT_VEC3:
variable_type = shader_variable_type::int3;
variable = std::make_unique<const gl_shader_int3>(variable_size, uniform_location);
break;
case GL_INT_VEC4:
variable_type = shader_variable_type::int4;
variable = std::make_unique<const gl_shader_int4>(variable_size, uniform_location);
break;
case GL_UNSIGNED_INT:
variable_type = shader_variable_type::uint1;
variable = std::make_unique<const gl_shader_uint>(variable_size, uniform_location);
break;
case GL_UNSIGNED_INT_VEC2:
variable_type = shader_variable_type::uint2;
variable = std::make_unique<const gl_shader_uint2>(variable_size, uniform_location);
break;
case GL_UNSIGNED_INT_VEC3:
variable_type = shader_variable_type::uint3;
variable = std::make_unique<const gl_shader_uint3>(variable_size, uniform_location);
break;
case GL_UNSIGNED_INT_VEC4:
variable_type = shader_variable_type::uint4;
variable = std::make_unique<const gl_shader_uint4>(variable_size, uniform_location);
break;
case GL_FLOAT:
variable_type = shader_variable_type::float1;
variable = std::make_unique<const gl_shader_float>(variable_size, uniform_location);
break;
case GL_FLOAT_VEC2:
variable_type = shader_variable_type::float2;
variable = std::make_unique<const gl_shader_float2>(variable_size, uniform_location);
break;
case GL_FLOAT_VEC3:
variable_type = shader_variable_type::float3;
variable = std::make_unique<const gl_shader_float3>(variable_size, uniform_location);
break;
case GL_FLOAT_VEC4:
variable_type = shader_variable_type::float4;
variable = std::make_unique<const gl_shader_float4>(variable_size, uniform_location);
break;
case GL_FLOAT_MAT2:
variable_type = shader_variable_type::float2x2;
variable = std::make_unique<const gl_shader_float2x2>(variable_size, uniform_location);
break;
case GL_FLOAT_MAT3:
variable_type = shader_variable_type::float3x3;
variable = std::make_unique<const gl_shader_float3x3>(variable_size, uniform_location);
break;
case GL_FLOAT_MAT4:
variable_type = shader_variable_type::float4x4;
variable = std::make_unique<const gl_shader_float4x4>(variable_size, uniform_location);
break;
case GL_SAMPLER_1D:
case GL_SAMPLER_1D_SHADOW:
variable_type = shader_variable_type::texture_1d;
texture_unit = available_texture_unit;
available_texture_unit += uniform_size;
variable = std::make_unique<const gl_shader_texture_1d>(variable_size, uniform_location, texture_index);
texture_index += uniform_size;
break;
case GL_SAMPLER_2D:
case GL_SAMPLER_2D_SHADOW:
variable_type = shader_variable_type::texture_2d;
texture_unit = available_texture_unit;
available_texture_unit += uniform_size;
variable = std::make_unique<const gl_shader_texture_2d>(variable_size, uniform_location, texture_index);
texture_index += uniform_size;
break;
case GL_SAMPLER_3D:
variable_type = shader_variable_type::texture_3d;
texture_unit = available_texture_unit;
available_texture_unit += uniform_size;
variable = std::make_unique<const gl_shader_texture_3d>(variable_size, uniform_location, texture_index);
texture_index += uniform_size;
break;
case GL_SAMPLER_CUBE:
variable_type = shader_variable_type::texture_cube;
texture_unit = available_texture_unit;
available_texture_unit += uniform_size;
variable = std::make_unique<const gl_shader_texture_cube>(variable_size, uniform_location, texture_index);
texture_index += uniform_size;
break;
default:
unsupported = true;
throw std::runtime_error("Unsupported to shader uniform type");
break;
}
// Check if data type is supported
if (unsupported)
{
std::string message = std::string("Shader uniform \"") + std::string(uniform_name) + std::string("\" has unsupported data type.");
throw std::runtime_error(message.c_str());
}
// Get uniform location
GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name);
if (uniform_location == -1)
{
//std::string message = std::string("Unable to get location for uniform \"") + std::string(uniform_name) + std::string("\"");
//throw std::runtime_error(message.c_str());
}
else
{
// Create new shader input
shader_input* input = new shader_input(this, inputs.size(), uniform_location, input_name, variable_type, uniform_size, texture_unit);
input_map[input_name] = input;
inputs.push_back(input);
}
// Map variable to variable key
variable_map.emplace(variable_key, std::move(variable));
}
// Free uniform name buffer
delete[] uniform_name;
}
void shader_program::free_inputs()
{
for (shader_input* input: inputs)
{
delete input;
}
inputs.clear();
}
} // namespace gl

+ 58
- 52
src/engine/gl/shader-program.hpp View File

@ -20,16 +20,18 @@
#ifndef ANTKEEPER_GL_SHADER_PROGRAM_HPP
#define ANTKEEPER_GL_SHADER_PROGRAM_HPP
#include <list>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <cstdint>
#include <memory>
#include <engine/utility/hash/fnv1a.hpp>
namespace gl {
class shader_object;
class rasterizer;
class shader_input;
class shader_variable;
/**
* Shader program which can be linked to shader objects and executed.
@ -51,6 +53,11 @@ public:
*/
~shader_program();
shader_program(const shader_program&) = delete;
shader_program(shader_program&&) = delete;
shader_program& operator=(const shader_program&) = delete;
shader_program& operator=(shader_program&&) = delete;
/**
* Attaches a shader object to the shader program. Attaching a shader object has no effect on a shader program until shader_program::link() is called.
*
@ -63,7 +70,7 @@ public:
*
* @see shader_program::link()
*/
void attach(const shader_object* object);
void attach(const shader_object& object);
/**
* Detaches a shader object from the shader program. Detaching a shader object has no effect on a shader program until shader_program::link() is called.
@ -77,7 +84,7 @@ public:
*
* @see shader_program::link()
*/
void detach(const shader_object* object);
void detach(const shader_object& object);
/**
* Detaches all shader objects from the shader program.
@ -94,65 +101,64 @@ public:
/**
* Links all attached shader objects to create an executable shader program.
*
* @warning All existing pointers to a shader program's shader inputs will be invalidated if the program is re-linked.
* @return `true` if the attached shader objects were successfully linked into the shader program, `false` otherwise.
*
* @return `true` if the attached shader objects were successfully linked into the shader program, `false` otherwise. If linking fails, check the info log via shader_program::get_info_log() for more information.
* @warning All existing of the shader program's variables will be invalidated if the program is re-linked.
*/
bool link();
/// Returns the shader program info log, which is updated when the shader program is linked.
const std::string& get_info_log() const;
/// Returns `true` if the shader program has been successfully linked, `false` otherwise.
bool was_linked() const;
shader_program(const shader_program&) = delete;
shader_program& operator=(const shader_program&) = delete;
const std::list<shader_input*>* get_inputs() const;
const shader_input* get_input(const std::string& name) const;
private:
friend class rasterizer;
unsigned int gl_program_id;
std::string info_log;
bool linked;
std::unordered_set<const shader_object*> attached_objects;
void find_inputs();
void free_inputs();
[[nodiscard]] inline bool linked() const noexcept
{
return m_linked;
}
std::list<shader_input*> inputs;
std::unordered_map<std::string, shader_input*> input_map;
/**
* Returns all active shader variables in the shader program.
*
* @return Map of 32-bit FNV-1a hash values of shader variable names to shader variables.
*/
[[nodiscard]] inline const std::unordered_map<hash::fnv1a32_t, const std::unique_ptr<const shader_variable>>& variables() const noexcept
{
return variable_map;
}
};
inline const std::string& shader_program::get_info_log() const
{
return info_log;
}
inline bool shader_program::was_linked() const
{
return linked;
}
inline const std::list<shader_input*>* shader_program::get_inputs() const
{
return &inputs;
}
inline const shader_input* shader_program::get_input(const std::string& name) const
{
auto it = input_map.find(name);
if (it == input_map.end())
/**
* Returns a pointer to an active shader variable with the given name, or `nullptr` if not found.
*
* @param key 32-bit FNV-1a hash value of a shader variable name.
*
* @return Pointer to the active shader variable with the given name, or `nullptr` if not found.
*/
[[nodiscard]] inline const shader_variable* variable(hash::fnv1a32_t key) const
{
if (auto i = variable_map.find(key); i != variable_map.end())
{
return i->second.get();
}
return nullptr;
}
/**
* Returns the info log that contains debug information when linking fails.
*/
[[nodiscard]] inline const std::string& info() const noexcept
{
return info_log;
}
return it->second;
}
private:
friend class rasterizer;
void load_variables();
unsigned int gl_program_id{0};
bool m_linked{false};
std::unordered_set<const shader_object*> attached_objects;
std::unordered_map<hash::fnv1a32_t, const std::unique_ptr<const shader_variable>> variable_map;
std::string info_log;
};
} // namespace gl

+ 3
- 2
src/engine/gl/shader-stage.hpp View File

@ -20,12 +20,14 @@
#ifndef ANTKEEPER_GL_SHADER_STAGE_HPP
#define ANTKEEPER_GL_SHADER_STAGE_HPP
#include <cstdint>
namespace gl {
/**
* Enumerates all supported shader stages.
*/
enum class shader_stage
enum class shader_stage: std::uint8_t
{
/// Vertex shader stage.
vertex,
@ -40,4 +42,3 @@ enum class shader_stage
} // namespace gl
#endif // ANTKEEPER_GL_SHADER_STAGE_HPP

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

@ -0,0 +1,364 @@
/*
* 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/shader-template.hpp>
#include <algorithm>
#include <engine/gl/shader-object.hpp>
#include <engine/gl/shader-program.hpp>
#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 <sstream>
#include <unordered_set>
namespace gl {
shader_template::shader_template(const text_file& source_code):
template_source{source_code}
{
find_directives();
rehash();
}
shader_template::shader_template(text_file&& source_code):
template_source{source_code}
{
find_directives();
rehash();
}
void shader_template::source(const text_file& source_code)
{
template_source = source_code;
find_directives();
rehash();
}
void shader_template::source(text_file&& source_code)
{
template_source = source_code;
find_directives();
rehash();
}
std::string shader_template::configure(gl::shader_stage stage, const dictionary_type& definitions) const
{
replace_stage_directives(stage);
replace_define_directives(definitions);
// Join vector of source lines into single string
std::string string;
for (const auto& line: template_source.lines)
{
string += line;
string += '\n';
}
return string;
}
std::unique_ptr<gl::shader_object> shader_template::compile(gl::shader_stage stage, const dictionary_type& definitions) const
{
// Generate shader object source
const std::string object_source = configure(stage, definitions);
// Create shader object
std::unique_ptr<gl::shader_object> object = std::make_unique<gl::shader_object>(stage);
// Set shader object source
object->source(object_source);
// Compile shader object
object->compile();
return object;
}
std::unique_ptr<gl::shader_program> shader_template::build(const dictionary_type& definitions) const
{
std::unique_ptr<gl::shader_object> vertex_object;
std::unique_ptr<gl::shader_object> fragment_object;
std::unique_ptr<gl::shader_object> geometry_object;
// Create shader program
std::unique_ptr<gl::shader_program> program = std::make_unique<gl::shader_program>();
if (has_vertex_directive())
{
// Compile vertex shader object and attach to shader program
vertex_object = compile(gl::shader_stage::vertex, definitions);
program->attach(*vertex_object);
}
if (has_fragment_directive())
{
// Compile fragment shader object and attach to shader program
fragment_object = compile(gl::shader_stage::fragment, definitions);
program->attach(*fragment_object);
}
if (has_geometry_directive())
{
// Compile fragment shader object and attach to shader program
geometry_object = compile(gl::shader_stage::geometry, definitions);
program->attach(*geometry_object);
}
// Link attached shader objects into shader program
program->link();
// Detach all attached shader objects
program->detach_all();
return program;
}
void shader_template::find_directives()
{
// Reset directives
vertex_directives.clear();
fragment_directives.clear();
geometry_directives.clear();
define_directives.clear();
// Parse directives
for (std::size_t i = 0; i < template_source.lines.size(); ++i)
{
std::istringstream line_stream(template_source.lines[i]);
std::string token;
// Detect `#pragma` directives
if (line_stream >> token && token == "#pragma")
{
if (line_stream >> token)
{
// Map line numbers of supported directives
if (token == "define")
{
if (line_stream >> token)
{
define_directives.insert({token, i});
}
}
else if (token == "vertex")
{
vertex_directives.insert(i);
}
else if (token == "fragment")
{
fragment_directives.insert(i);
}
else if (token == "geometry")
{
geometry_directives.insert(i);
}
}
}
}
}
void shader_template::rehash()
{
m_hash = 0;
for (const auto& line: template_source.lines)
{
m_hash = hash::combine(m_hash, std::hash<std::string>{}(line));
}
}
void shader_template::replace_stage_directives(gl::shader_stage stage) const
{
// Determine stage directives according to the shader stage being generated
const char* vertex_directive = (stage == gl::shader_stage::vertex) ? "#define __VERTEX__" : "/* #undef __VERTEX__ */";
const char* fragment_directive = (stage == gl::shader_stage::fragment) ? "#define __FRAGMENT__" : "/* #undef __FRAGMENT__ */";
const char* geometry_directive = (stage == gl::shader_stage::geometry) ? "#define __GEOMETRY__" : "/* #undef __GEOMETRY__ */";
// Handle `#pragma <stage>` directives
for (const auto directive_line: vertex_directives)
{
template_source.lines[directive_line] = vertex_directive;
}
for (const auto directive_line: fragment_directives)
{
template_source.lines[directive_line] = fragment_directive;
}
for (const auto directive_line: geometry_directives)
{
template_source.lines[directive_line] = geometry_directive;
}
}
void shader_template::replace_define_directives(const dictionary_type& definitions) const
{
// For each `#pragma define <key>` directive
for (const auto& define_directive: define_directives)
{
// Get a reference to the directive line
std::string& line = template_source.lines[define_directive.second];
// Check if the corresponding definition was given by the configuration
auto definitions_it = definitions.find(define_directive.first);
if (definitions_it != definitions.end())
{
// Definition found, replace `#pragma define <key>` with `#define <key>` or `#define <key> <value>`
line = "#define " + define_directive.first;
if (!definitions_it->second.empty())
{
line += " " + definitions_it->second;
}
}
else
{
// Definition not found, replace `#pragma define <key>` with the comment `/* #undef <key> */`.
line = "/* #undef " + define_directive.first + " */";
}
}
}
bool shader_template::has_vertex_directive() const noexcept
{
return !vertex_directives.empty();
}
bool shader_template::has_fragment_directive() const noexcept
{
return !fragment_directives.empty();
}
bool shader_template::has_geometry_directive() const noexcept
{
return !geometry_directives.empty();
}
bool shader_template::has_define_directive(const std::string& key) const
{
return (define_directives.find(key) != define_directives.end());
}
} // namespace gl
/**
* Scans a text file for the presence of a `#pragma once` directive.
*
* @param source Text file to scan.
*
* @return `true` if the file contains a `#pragma once` directive, `false` otherwise.
*/
static bool has_pragma_once(const text_file& source)
{
for (const auto& line: source.lines)
{
std::istringstream line_stream(line);
std::string token;
// If line contains a `#pragma once` directive
if (line_stream >> token && token == "#pragma")
{
if (line_stream >> token && token == "once")
{
return true;
}
}
}
return false;
}
/**
* Handles `#pragma include` directives by loading the specified text files and inserting them in place.
*/
static void handle_includes(text_file& source, std::unordered_set<std::filesystem::path>& include_once, resource_manager& resource_manager)
{
// For each line in the source
for (std::size_t i = 0; i < source.lines.size(); ++i)
{
std::string token;
std::istringstream line_stream(source.lines[i]);
// If line contains a `#pragma include` directive
if (line_stream >> token && token == "#pragma" &&
line_stream >> token && token == "include")
{
// If third token is enclosed in quotes or angled brackets
if (line_stream >> token && token.size() > 2 &&
((token.front() == '\"' && token.back() == '\"') ||
(token.front() == '<' && token.back() == '>')))
{
// Extract include path
const std::filesystem::path path = token.substr(1, token.length() - 2);
// Skip pre-included files that contain a `#pragma once` directive
if (include_once.contains(path))
{
source.lines[i] = "/* #pragma exclude " + token + " */";
continue;
}
// Load include file
const auto include_file = resource_manager.load<text_file>(path);
if (!include_file)
{
source.lines[i] = "#error file not found: " + path.string();
continue;
}
// If file has `#pragma once` directive
if (has_pragma_once(*include_file))
{
// Add file to set of files to include once
include_once.insert(path);
}
// Create a copy of the include file
text_file include_file_copy = *include_file;
// Handle `#pragma include` directives inside include file
handle_includes(include_file_copy, include_once, resource_manager);
// Replace #pragma include directive with include file contents
source.lines.erase(source.lines.begin() + i);
source.lines.insert(source.lines.begin() + i, include_file_copy.lines.begin(), include_file_copy.lines.end());
i += include_file_copy.lines.size() - 1;
}
else
{
source.lines[i] = "#error malformed include directive: \"" + source.lines[i] + "\"";
}
}
}
}
template <>
std::unique_ptr<gl::shader_template> resource_loader<gl::shader_template>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
// Load shader template source file
const auto source_file = resource_loader<text_file>::load(resource_manager, ctx);
// Make a copy of the shader template source file
text_file source_file_copy = *source_file;
// Handle `#pragma include` directives
std::unordered_set<std::filesystem::path> include_once;
include_once.insert(ctx.path());
handle_includes(source_file_copy, include_once, resource_manager);
// Construct shader template
return std::make_unique<gl::shader_template>(std::move(source_file_copy));
}

src/engine/render/shader-template.hpp → src/engine/gl/shader-template.hpp View File

@ -17,17 +17,20 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP
#define ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP
#ifndef ANTKEEPER_GL_SHADER_TEMPLATE_HPP
#define ANTKEEPER_GL_SHADER_TEMPLATE_HPP
#include <engine/gl/shader-object.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/utility/text-file.hpp>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace render {
namespace gl {
/**
* Template used to for generating one or more shader variants from a single source.
@ -52,48 +55,55 @@ public:
/**
* Constructs a shader template and sets its source code.
*
* @param source_code String containing the shader template source code.
*
* @see shader_template::source(const std::string&)
* @param source_code Shader template source code.
*/
shader_template(const std::string& source_code);
/// @{
explicit shader_template(const text_file& source_code);
explicit shader_template(text_file&& source_code);
/// @}
/**
* Constructs an empty shader template.
*/
shader_template();
shader_template() = default;
/**
* Replaces the source code of the shader template.
*
* @param source_code String containing shader template source code.
* @param source_code Shader template source code.
*/
void source(const std::string& source_code);
/// @{
void source(const text_file& source_code);
void source(text_file&& source_code);
/// @}
/**
* Configures shader object source code given a shader stage and template dictionary.
* Configures shader object source code for a given shader stage and template dictionary.
*
* @param stage Shader stage of the shader object to generate. Instances of `#pragma <stage>` in the template source will be replaced with `#define __<STAGE>__`.
* @param definitions Container of definitions used to replace `#pragma define <key> <value>` directives.
*
* @return Configured shader object source code.
*/
std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const;
[[nodiscard]] std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const;
/**
* Configures and compiles a shader object.
*
* @param stage Shader stage of the shader object to generate. Instances of `#pragma <stage>` in the template source will be replaced with `#define __<STAGE>__`.
* @param definitions Container of definitions used to replace `#pragma define <key> <value>` directives.
*
* @return Compiled shader object.
*
* @exception std::runtime_error Any exceptions thrown by gl::shader_object.
*/
gl::shader_object* compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const;
[[nodiscard]] std::unique_ptr<gl::shader_object> compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const;
/**
* Configures and compiles shader objects, then links them into a shader program. Shader object stages are determined according to the presence of `#pragma <stage>` directives.
*
* @param definitions Container of definitions used to replace `#pragma define <key> <value>` directives.
*
* @return Linked shader program.
*
* @exception std::runtime_error Any exceptions thrown by gl::shader_object or gl::shader_program.
@ -102,44 +112,44 @@ public:
* @see has_fragment_directive() const
* @see has_geometry_directive() const
*/
gl::shader_program* build(const dictionary_type& definitions = {}) const;
[[nodiscard]] std::unique_ptr<gl::shader_program> build(const dictionary_type& definitions = {}) const;
/// Returns `true` if the template source contains one or more `#pragma vertex` directive.
bool has_vertex_directive() const;
[[nodiscard]] bool has_vertex_directive() const noexcept;
/// Returns `true` if the template source contains one or more `#pragma fragment` directive.
bool has_fragment_directive() const;
[[nodiscard]] bool has_fragment_directive() const noexcept;
/// Returns `true` if the template source contains one or more `#pragma geometry` directive.
bool has_geometry_directive() const;
[[nodiscard]] bool has_geometry_directive() const noexcept;
/**
* Returns `true` if the template source contains one or more instance of `#pragma define <key>`.
*
* @param key Definition key.
*/
bool has_define_directive(const std::string& key) const;
[[nodiscard]] bool has_define_directive(const std::string& key) const;
/// Returns a hash of the template source.
std::size_t get_hash() const;
/// Returns a hash of the template source code.
[[nodiscard]] inline std::size_t hash() const noexcept
{
return m_hash;
}
private:
void find_directives();
void rehash();
void replace_stage_directives(gl::shader_stage stage) const;
void replace_define_directives(const dictionary_type& definitions) const;
mutable std::vector<std::string> template_source;
mutable text_file template_source;
std::unordered_set<std::size_t> vertex_directives;
std::unordered_set<std::size_t> fragment_directives;
std::unordered_set<std::size_t> geometry_directives;
std::multimap<std::string, std::size_t> define_directives;
std::size_t hash;
std::size_t m_hash{0};
};
inline std::size_t shader_template::get_hash() const
{
return hash;
}
} // namespace render
} // namespace gl
#endif // ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP
#endif // ANTKEEPER_GL_SHADER_TEMPLATE_HPP

+ 6
- 2
src/engine/gl/shader-variable-type.hpp View File

@ -20,9 +20,14 @@
#ifndef ANTKEEPER_GL_SHADER_VARIABLE_TYPE_HPP
#define ANTKEEPER_GL_SHADER_VARIABLE_TYPE_HPP
#include <cstdint>
namespace gl {
enum class shader_variable_type
/**
* Shader variable data types.
*/
enum class shader_variable_type: std::uint8_t
{
bool1,
bool2,
@ -52,4 +57,3 @@ enum class shader_variable_type
} // namespace gl
#endif // ANTKEEPER_GL_SHADER_VARIABLE_TYPE_HPP

+ 396
- 0
src/engine/gl/shader-variable.cpp View File

@ -0,0 +1,396 @@
/*
* 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/shader-variable.hpp>
#include <stdexcept>
namespace gl {
static const char* type_mismatch_error = "Shader variable type mismatch";
shader_variable::shader_variable(std::size_t size) noexcept:
m_size{size}
{}
void shader_variable::update(bool value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const bool2& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const bool3& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const bool4& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(int value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const int2& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const int3& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const int4& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(unsigned int value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const uint2& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const uint3& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const uint4& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(float value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float2& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float3& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float4& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float2x2& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float3x3& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float4x4& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_1d& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_2d& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_3d& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_cube& value) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(bool value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const bool2& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const bool3& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const bool4& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(int value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const int2& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const int3& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const int4& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(unsigned int value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const uint2& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const uint3& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const uint4& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(float value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float2& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float3& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float4& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float2x2& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float3x3& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const float4x4& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_1d& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_2d& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_3d& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(const texture_cube& value, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const bool> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const bool2> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const bool3> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const bool4> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const int> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const int2> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const int3> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const int4> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const unsigned int> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const uint2> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const uint3> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const uint4> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float2> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float3> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float4> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float2x2> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float3x3> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const float4x4> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const texture_1d* const> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const texture_2d* const> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const texture_3d* const> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const texture_cube* const> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const std::shared_ptr<texture_1d>> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const std::shared_ptr<texture_2d>> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const std::shared_ptr<texture_3d>> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
void shader_variable::update(std::span<const std::shared_ptr<texture_cube>> values, std::size_t index) const
{
throw std::invalid_argument(type_mismatch_error);
}
} // namespace gl

+ 195
- 0
src/engine/gl/shader-variable.hpp View File

@ -0,0 +1,195 @@
/*
* 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_SHADER_INPUT_HPP
#define ANTKEEPER_GL_SHADER_INPUT_HPP
#include <engine/utility/fundamental-types.hpp>
#include <engine/gl/shader-variable-type.hpp>
#include <cstdint>
#include <span>
#include <memory>
namespace gl {
class texture_1d;
class texture_2d;
class texture_3d;
class texture_cube;
/**
* Shader program variable.
*/
class shader_variable
{
public:
/**
* Destructs a shader variable.
*/
virtual ~shader_variable() = default;
/**
* Returns the shader variable data type.
*/
[[nodiscard]] virtual constexpr shader_variable_type type() const noexcept = 0;
/**
* Returns the number of elements in an array variable, or `1` if the variable is not an array.
*/
[[nodiscard]] inline std::size_t size() const noexcept
{
return m_size;
}
/**
* Updates the value of the variable. If the variable is an array, the value of the first element will be updated.
*
* @param value Update value.
*
* @throw std::invalid_argument Shader variable type mismatch.
*/
///@{
virtual void update(bool value) const;
virtual void update(const bool2& value) const;
virtual void update(const bool3& value) const;
virtual void update(const bool4& value) const;
virtual void update(int value) const;
virtual void update(const int2& value) const;
virtual void update(const int3& value) const;
virtual void update(const int4& value) const;
virtual void update(unsigned int value) const;
virtual void update(const uint2& value) const;
virtual void update(const uint3& value) const;
virtual void update(const uint4& value) const;
virtual void update(float value) const;
virtual void update(const float2& value) const;
virtual void update(const float3& value) const;
virtual void update(const float4& value) const;
virtual void update(const float2x2& value) const;
virtual void update(const float3x3& value) const;
virtual void update(const float4x4& value) const;
virtual void update(const texture_1d& value) const;
virtual void update(const texture_2d& value) const;
virtual void update(const texture_3d& value) const;
virtual void update(const texture_cube& value) const;
///@}
/**
* Updates the value of a single element in an array variable.
*
* @param value Update value.
* @param index Index of the element to update.
*
* @throw std::invalid_argument Shader variable type mismatch.
*/
/// @{
virtual void update(bool value, std::size_t index) const;
virtual void update(const bool2& value, std::size_t index) const;
virtual void update(const bool3& value, std::size_t index) const;
virtual void update(const bool4& value, std::size_t index) const;
virtual void update(int value, std::size_t index) const;
virtual void update(const int2& value, std::size_t index) const;
virtual void update(const int3& value, std::size_t index) const;
virtual void update(const int4& value, std::size_t index) const;
virtual void update(unsigned int value, std::size_t index) const;
virtual void update(const uint2& value, std::size_t index) const;
virtual void update(const uint3& value, std::size_t index) const;
virtual void update(const uint4& value, std::size_t index) const;
virtual void update(float value, std::size_t index) const;
virtual void update(const float2& value, std::size_t index) const;
virtual void update(const float3& value, std::size_t index) const;
virtual void update(const float4& value, std::size_t index) const;
virtual void update(const float2x2& value, std::size_t index) const;
virtual void update(const float3x3& value, std::size_t index) const;
virtual void update(const float4x4& value, std::size_t index) const;
virtual void update(const texture_1d& value, std::size_t index) const;
virtual void update(const texture_2d& value, std::size_t index) const;
virtual void update(const texture_3d& value, std::size_t index) const;
virtual void update(const texture_cube& value, std::size_t index) const;
///@}
/**
* Updates the values of one or more elements in an array variable.
*
* @param values Contiguous sequence of update values.
* @param index Index of the first element to update.
*
* @throw std::invalid_argument Shader variable type mismatch.
*/
///@{
virtual void update(std::span<const bool> values, std::size_t index = 0) const;
virtual void update(std::span<const bool2> values, std::size_t index = 0) const;
virtual void update(std::span<const bool3> values, std::size_t index = 0) const;
virtual void update(std::span<const bool4> values, std::size_t index = 0) const;
virtual void update(std::span<const int> values, std::size_t index = 0) const;
virtual void update(std::span<const int2> values, std::size_t index = 0) const;
virtual void update(std::span<const int3> values, std::size_t index = 0) const;
virtual void update(std::span<const int4> values, std::size_t index = 0) const;
virtual void update(std::span<const unsigned int> values, std::size_t index = 0) const;
virtual void update(std::span<const uint2> values, std::size_t index = 0) const;
virtual void update(std::span<const uint3> values, std::size_t index = 0) const;
virtual void update(std::span<const uint4> values, std::size_t index = 0) const;
virtual void update(std::span<const float> values, std::size_t index = 0) const;
virtual void update(std::span<const float2> values, std::size_t index = 0) const;
virtual void update(std::span<const float3> values, std::size_t index = 0) const;
virtual void update(std::span<const float4> values, std::size_t index = 0) const;
virtual void update(std::span<const float2x2> values, std::size_t index = 0) const;
virtual void update(std::span<const float3x3> values, std::size_t index = 0) const;
virtual void update(std::span<const float4x4> values, std::size_t index = 0) const;
virtual void update(std::span<const texture_1d* const> values, std::size_t index = 0) const;
virtual void update(std::span<const texture_2d* const> values, std::size_t index = 0) const;
virtual void update(std::span<const texture_3d* const> values, std::size_t index = 0) const;
virtual void update(std::span<const texture_cube* const> values, std::size_t index = 0) const;
virtual void update(std::span<const std::shared_ptr<texture_1d>> values, std::size_t index = 0) const;
virtual void update(std::span<const std::shared_ptr<texture_2d>> values, std::size_t index = 0) const;
virtual void update(std::span<const std::shared_ptr<texture_3d>> values, std::size_t index = 0) const;
virtual void update(std::span<const std::shared_ptr<texture_cube>> values, std::size_t index = 0) const;
///@}
protected:
/**
* Constructs a shader variable.
*
* @param size Number of elements in the array, or `1` if the variable is not an array.
*/
explicit shader_variable(std::size_t size) noexcept;
private:
const std::size_t m_size;
};
} // namespace gl
#endif // ANTKEEPER_GL_SHADER_VARIABLE_HPP

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

@ -21,14 +21,14 @@
namespace gl {
texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data):
texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data):
texture(width, type, format, color_space, data)
{}
texture_1d::~texture_1d()
{}
void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data)
void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data)
{
texture::resize(width, type, format, color_space, data);
}

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

@ -30,14 +30,14 @@ namespace gl {
class texture_1d: public texture
{
public:
/// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*)
texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr);
/// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*)
texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr);
/// Destructs a 1D texture.
virtual ~texture_1d();
/// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*)
virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data);
/// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*)
virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data);
/// @copydoc texture::set_wrapping(gl::texture_wrapping)
virtual void set_wrapping(gl::texture_wrapping wrap_s);

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

@ -21,19 +21,19 @@
namespace gl {
texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data):
texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data):
texture(width, height, type, format, color_space, data)
{}
texture_2d::~texture_2d()
{}
void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data)
void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data)
{
texture::resize(width, height, type, format, color_space, data);
}
void texture_2d::resize(std::uint16_t width, std::uint16_t height, const void* 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_color_space(), data);
}

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

@ -30,14 +30,14 @@ namespace gl {
class texture_2d: public texture
{
public:
/// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*)
texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr);
/// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*)
texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr);
/// Destructs a 2D texture.
virtual ~texture_2d();
/// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*)
virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data);
/// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*)
virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data);
/**
* Resizes the texture.
@ -46,7 +46,7 @@ public:
* @param height Texture height, in pixels.
* @param data Pointer to pixel data.
*/
void resize(std::uint16_t width, std::uint16_t height, const void* 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)
virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t);

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

@ -21,14 +21,14 @@
namespace gl {
texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data):
texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data):
texture(width, height, depth, type, format, color_space, data)
{}
texture_3d::~texture_3d()
{}
void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data)
void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data)
{
texture::resize(width, height, depth, type, format, color_space, data);
}

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

@ -30,14 +30,14 @@ namespace gl {
class texture_3d: public texture
{
public:
/// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*)
texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr);
/// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*)
texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr);
/// Destructs a 3D texture.
virtual ~texture_3d();
/// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*)
virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data);
/// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*)
virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data);
/// @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);

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

@ -22,8 +22,6 @@
namespace gl {
class shader_input;
/**
* A cube texture which can be uploaded to shaders via shader inputs.
*/
@ -43,9 +41,6 @@ public:
/// Returns the linear size of a cube face, in pixels.
int get_face_size() const;
private:
friend class shader_input;
unsigned int gl_texture_id;
int face_size;
};

+ 4
- 3
src/engine/gl/texture-filter.hpp View File

@ -20,12 +20,14 @@
#ifndef ANTKEEPER_GL_TEXTURE_FILTER_HPP
#define ANTKEEPER_GL_TEXTURE_FILTER_HPP
#include <cstdint>
namespace gl {
/**
* Texture minification filter modes.
*/
enum class texture_min_filter
enum class texture_min_filter: std::uint8_t
{
nearest,
linear,
@ -38,7 +40,7 @@ enum class texture_min_filter
/**
* Texture magnification filter modes.
*/
enum class texture_mag_filter
enum class texture_mag_filter: std::uint8_t
{
nearest,
linear
@ -47,4 +49,3 @@ enum class texture_mag_filter
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_FILTER_HPP

+ 3
- 2
src/engine/gl/texture-wrapping.hpp View File

@ -20,9 +20,11 @@
#ifndef ANTKEEPER_GL_TEXTURE_WRAPPING_HPP
#define ANTKEEPER_GL_TEXTURE_WRAPPING_HPP
#include <cstdint>
namespace gl {
enum class texture_wrapping
enum class texture_wrapping: std::uint8_t
{
clip,
extend,
@ -33,4 +35,3 @@ enum class texture_wrapping
} // namespace gl
#endif // ANTKEEPER_GL_TEXTURE_WRAPPING_HPP

+ 216
- 9
src/engine/gl/texture.cpp View File

@ -18,10 +18,21 @@
*/
#include <engine/gl/texture.hpp>
#include <engine/gl/texture-wrapping.hpp>
#include <engine/gl/texture-1d.hpp>
#include <engine/gl/texture-2d.hpp>
#include <algorithm>
#include <engine/gl/color-space.hpp>
#include <engine/gl/pixel-format.hpp>
#include <engine/gl/pixel-type.hpp>
#include <engine/gl/texture-filter.hpp>
#include <engine/gl/texture-wrapping.hpp>
#include <engine/resources/deserialize-error.hpp>
#include <engine/resources/deserializer.hpp>
#include <engine/utility/image.hpp>
#include <engine/resources/resource-loader.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/utility/json.hpp>
#include <glad/glad.h>
#include <algorithm>
namespace gl {
@ -112,7 +123,7 @@ static constexpr GLenum mag_filter_lut[] =
GL_LINEAR
};
texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data):
texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data):
gl_texture_target((depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D),
gl_texture_id(0),
dimensions({0, 0, 0}),
@ -127,11 +138,11 @@ texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth,
set_max_anisotropy(max_anisotropy);
}
texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data):
texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data):
texture(width, height, 0, type, format, color_space, data)
{}
texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data):
texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data):
texture(width, 0, 0, type, format, color_space, data)
{}
@ -204,7 +215,7 @@ void texture::set_wrapping(gl::texture_wrapping wrap_s)
glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s);
}
void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data)
void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data)
{
dimensions = {width, height, depth};
pixel_type = type;
@ -246,7 +257,7 @@ void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t de
glGenerateMipmap(gl_texture_target);
glTexParameteriv(gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask);
/// TODO: remove this
/// @TODO: remove this
if (format == pixel_format::d)
{
glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
@ -254,14 +265,210 @@ void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t de
}
}
void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data)
void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data)
{
resize(width, height, 0, type, format, color_space, data);
}
void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data)
void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data)
{
resize(width, 0, 0, type, format, color_space, data);
}
} // namespace gl
template <>
std::unique_ptr<gl::texture_1d> resource_loader<gl::texture_1d>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
// Load JSON data
auto json_data = resource_loader<nlohmann::json>::load(resource_manager, ctx);
// Read image filename
std::string image_filename;
if (auto element = json_data->find("image"); element != json_data->end())
image_filename = element.value().get<std::string>();
// Load image
auto image = resource_manager.load<::image>(image_filename);
// Read color space
gl::color_space color_space = gl::color_space::linear;
if (auto element = json_data->find("color_space"); element != json_data->end())
{
std::string value = element.value().get<std::string>();
if (value == "linear")
color_space = gl::color_space::linear;
else if (value == "srgb")
color_space = gl::color_space::srgb;
}
// Read extension mode
gl::texture_wrapping wrapping = gl::texture_wrapping::repeat;
if (auto element = json_data->find("extension"); element != json_data->end())
{
std::string value = element.value().get<std::string>();
if (value == "clip")
wrapping = gl::texture_wrapping::clip;
else if (value == "extend")
wrapping = gl::texture_wrapping::extend;
else if (value == "repeat")
wrapping = gl::texture_wrapping::repeat;
else if (value == "mirrored_repeat")
wrapping = gl::texture_wrapping::mirrored_repeat;
}
// Read interpolation mode
gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear;
gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear;
if (auto element = json_data->find("interpolation"); element != json_data->end())
{
std::string value = element.value().get<std::string>();
if (value == "linear")
{
min_filter = gl::texture_min_filter::linear_mipmap_linear;
mag_filter = gl::texture_mag_filter::linear;
}
else if (value == "closest")
{
min_filter = gl::texture_min_filter::nearest_mipmap_nearest;
mag_filter = gl::texture_mag_filter::nearest;
}
}
// Read max anisotropy
float max_anisotropy = 0.0f;
if (auto element = json_data->find("max_anisotropy"); element != json_data->end())
max_anisotropy = element.value().get<float>();
// Determine pixel type
gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8;
// Determine pixel format
gl::pixel_format format;
if (image->channel_count() == 1)
{
format = gl::pixel_format::r;
}
else if (image->channel_count() == 2)
{
format = gl::pixel_format::rg;
}
else if (image->channel_count() == 3)
{
format = gl::pixel_format::rgb;
}
else if (image->channel_count() == 4)
{
format = gl::pixel_format::rgba;
}
else
{
throw std::runtime_error(std::format("Texture image has unsupported number of channels ({})", image->channel_count()));
}
// Create texture
auto texture = std::make_unique<gl::texture_1d>(image->width(), type, format, color_space, image->data());
texture->set_wrapping(wrapping);
texture->set_filters(min_filter, mag_filter);
texture->set_max_anisotropy(max_anisotropy);
return texture;
}
template <>
std::unique_ptr<gl::texture_2d> resource_loader<gl::texture_2d>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
// Load JSON data
auto json_data = resource_loader<nlohmann::json>::load(resource_manager, ctx);
// Read image filename
std::string image_filename;
if (auto element = json_data->find("image"); element != json_data->end())
image_filename = element.value().get<std::string>();
// Load image
auto image = resource_manager.load<::image>(image_filename);
// Read color space
gl::color_space color_space = gl::color_space::linear;
if (auto element = json_data->find("color_space"); element != json_data->end())
{
std::string value = element.value().get<std::string>();
if (value == "linear")
color_space = gl::color_space::linear;
else if (value == "srgb")
color_space = gl::color_space::srgb;
}
// Read extension mode
gl::texture_wrapping wrapping = gl::texture_wrapping::repeat;
if (auto element = json_data->find("extension"); element != json_data->end())
{
std::string value = element.value().get<std::string>();
if (value == "clip")
wrapping = gl::texture_wrapping::clip;
else if (value == "extend")
wrapping = gl::texture_wrapping::extend;
else if (value == "repeat")
wrapping = gl::texture_wrapping::repeat;
else if (value == "mirrored_repeat")
wrapping = gl::texture_wrapping::mirrored_repeat;
}
// Read interpolation mode
gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear;
gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear;
if (auto element = json_data->find("interpolation"); element != json_data->end())
{
std::string value = element.value().get<std::string>();
if (value == "linear")
{
min_filter = gl::texture_min_filter::linear_mipmap_linear;
mag_filter = gl::texture_mag_filter::linear;
}
else if (value == "closest")
{
min_filter = gl::texture_min_filter::nearest_mipmap_nearest;
mag_filter = gl::texture_mag_filter::nearest;
}
}
// Read max anisotropy
float max_anisotropy = 0.0f;
if (auto element = json_data->find("max_anisotropy"); element != json_data->end())
max_anisotropy = element.value().get<float>();
// Determine pixel type
gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8;
// Determine pixel format
gl::pixel_format format;
if (image->channel_count() == 1)
{
format = gl::pixel_format::r;
}
else if (image->channel_count() == 2)
{
format = gl::pixel_format::rg;
}
else if (image->channel_count() == 3)
{
format = gl::pixel_format::rgb;
}
else if (image->channel_count() == 4)
{
format = gl::pixel_format::rgba;
}
else
{
throw std::runtime_error(std::format("Texture image has unsupported number of channels ({})", image->channel_count()));
}
// Create texture
auto texture = std::make_unique<gl::texture_2d>(image->width(), image->height(), type, format, color_space, image->data());
texture->set_wrapping(wrapping, wrapping);
texture->set_filters(min_filter, mag_filter);
texture->set_max_anisotropy(max_anisotropy);
return texture;
}

+ 15
- 8
src/engine/gl/texture.hpp View File

@ -27,12 +27,16 @@
#include <engine/gl/texture-wrapping.hpp>
#include <array>
#include <cstdint>
#include <cstddef>
#include <tuple>
namespace gl {
class framebuffer;
class shader_input;
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.
@ -54,9 +58,9 @@ public:
* @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading.
*/
/// @{
texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr);
texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr);
texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr);
texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr);
texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr);
texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr);
/// @}
/**
@ -137,14 +141,17 @@ protected:
* @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.
*/
/// @{
virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data);
virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data);
virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data);
virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data);
virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data);
virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data);
/// @}
private:
friend class framebuffer;
friend class shader_input;
friend class gl_shader_texture_1d;
friend class gl_shader_texture_2d;
friend class gl_shader_texture_3d;
friend class gl_shader_texture_cube;
unsigned int gl_texture_target;
unsigned int gl_texture_id;

+ 8
- 12
src/engine/gl/vertex-array.cpp View File

@ -38,8 +38,7 @@ static constexpr GLenum vertex_attribute_type_lut[] =
GL_DOUBLE
};
vertex_array::vertex_array():
gl_array_id(0)
vertex_array::vertex_array()
{
glGenVertexArrays(1, &gl_array_id);
}
@ -61,29 +60,31 @@ void vertex_array::bind(attribute_location_type location, const vertex_attribute
throw std::invalid_argument("Cannot bind vertex attribute that has an unsupported number of components (" + std::to_string(attribute.components) + ")");
}
attributes[location] = attribute;
m_attributes[location] = attribute;
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);
glVertexAttribPointer(
glVertexAttribPointer
(
static_cast<GLuint>(location),
static_cast<GLint>(attribute.components),
gl_type,
GL_FALSE,
static_cast<GLsizei>(attribute.stride),
(const GLvoid*)attribute.offset);
(const GLvoid*)attribute.offset
);
glEnableVertexAttribArray(static_cast<GLuint>(location));
}
void vertex_array::unbind(attribute_location_type location)
{
if (auto it = attributes.find(location); it != attributes.end())
if (auto it = m_attributes.find(location); it != m_attributes.end())
{
glBindVertexArray(gl_array_id);
glDisableVertexAttribArray(static_cast<GLuint>(location));
attributes.erase(it);
m_attributes.erase(it);
}
else
{
@ -91,9 +92,4 @@ void vertex_array::unbind(attribute_location_type location)
}
}
const typename vertex_array::attribute_map_type& vertex_array::get_attributes() const
{
return attributes;
}
} // namespace gl

+ 9
- 4
src/engine/gl/vertex-array.hpp View File

@ -39,7 +39,7 @@ class vertex_array
{
public:
/// Vertex attribute binding location type.
typedef std::uint_fast32_t attribute_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;
@ -51,7 +51,9 @@ public:
~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.
@ -74,13 +76,16 @@ public:
void unbind(attribute_location_type location);
/// Returns a const reference to the map of vertex attributes bound to this vertex array.
const attribute_map_type& get_attributes() const;
[[nodiscard]] inline const attribute_map_type& attributes() const noexcept
{
return m_attributes;
}
private:
friend class rasterizer;
attribute_map_type attributes;
std::uint_fast32_t gl_array_id;
unsigned int gl_array_id{0};
attribute_map_type m_attributes;
};
} // namespace gl

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

@ -27,7 +27,7 @@ namespace gl {
class vertex_buffer;
enum class vertex_attribute_type
enum class vertex_attribute_type: std::uint8_t
{
int_8,
uint_8,

+ 70
- 23
src/engine/gl/vertex-buffer.cpp View File

@ -36,19 +36,27 @@ static constexpr GLenum buffer_usage_lut[] =
GL_DYNAMIC_COPY
};
vertex_buffer::vertex_buffer(std::size_t size, const void* data, buffer_usage usage):
gl_buffer_id(0),
size(size),
usage(usage)
vertex_buffer::vertex_buffer(buffer_usage usage, std::size_t size, std::span<const std::byte> data):
m_usage{usage},
m_size{size}
{
GLenum gl_usage = buffer_usage_lut[static_cast<std::size_t>(usage)];
if (!data.empty())
{
// Bounds check
if (data.size() < size)
{
throw std::out_of_range("Vertex buffer creation 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>(size), data, gl_usage);
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(m_size), data.empty() ? nullptr : data.data(), gl_usage);
}
vertex_buffer::vertex_buffer():
vertex_buffer(0, nullptr, buffer_usage::static_draw)
vertex_buffer(buffer_usage::static_draw, 0)
{}
vertex_buffer::~vertex_buffer()
@ -56,47 +64,86 @@ vertex_buffer::~vertex_buffer()
glDeleteBuffers(1, &gl_buffer_id);
}
void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, const void* data)
void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, std::span<const std::byte> data)
{
this->size = size;
this->usage = usage;
if (!data.empty())
{
// Bounds check
if (data.size() < size)
{
throw std::out_of_range("Vertex buffer resize operation exceeded data bounds.");
}
}
m_usage = usage;
m_size = size;
GLenum gl_usage = buffer_usage_lut[static_cast<std::size_t>(usage)];
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>(size), data, gl_usage);
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, const void* data)
void vertex_buffer::resize(std::size_t size, std::span<const std::byte> data)
{
repurpose(usage, size, data);
repurpose(m_usage, size, data);
}
void vertex_buffer::write(std::size_t offset, std::size_t size, const void* data)
void vertex_buffer::write(std::span<const std::byte> data, std::size_t offset)
{
// Abort empty write operations
if (!size)
// Ignore empty write operations
if (data.empty())
{
return;
}
// Bounds check
if (offset + size > this->size)
if (offset + data.size() > m_size)
{
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>(size), data);
glBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(data.size()), data.data());
}
void vertex_buffer::read(std::size_t offset, std::size_t size, void* data) const
void vertex_buffer::read(std::span<std::byte> data, std::size_t offset) const
{
// Abort empty read operations
if (!size)
if (data.empty())
{
return;
}
// Bounds check
if (offset + size > this->size)
if (offset + data.size() > m_size)
{
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>(size), data);
glGetBufferSubData(GL_ARRAY_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)
{
// Bounds check
if (read_offset + copy_size > read_buffer.m_size)
{
throw std::out_of_range("Vertex buffer copy operation exceeded read buffer bounds.");
}
if (write_offset + copy_size > m_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));
}
} // namespace gl

+ 56
- 36
src/engine/gl/vertex-buffer.hpp View File

@ -21,8 +21,8 @@
#define ANTKEEPER_GL_VERTEX_BUFFER_HPP
#include <cstddef>
#include <cstdint>
#include <engine/gl/buffer-usage.hpp>
#include <span>
namespace gl {
@ -35,86 +35,106 @@ class vertex_buffer
{
public:
/**
* Creates a vertex buffer, settings its size, uploading its data, and setting its usage hint.
* Constructs a vertex buffer.
*
* @param size Size of the buffer's data, in bytes.
* @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied.
* @param usage Buffer usage hint.
* @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.
*/
explicit vertex_buffer(std::size_t size, const void* data = nullptr, buffer_usage usage = buffer_usage::static_draw);
vertex_buffer(buffer_usage usage, std::size_t size, std::span<const std::byte> data = {});
/// Creates an empty vertex buffer.
/**
* Constructs an empty vertex buffer.
*/
vertex_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;
/**
* Repurposes a vertex buffer, changing its usage hint, its size, and replacing its data.
* Repurposes the vertex buffer, changing its usage hint, size, and updating its data.
*
* @param usage New buffer usage hint.
* @param size New buffer size, in bytes.
* @param data New buffer data. If empty, buffer data will not be updated.
*
* @param usage New usage hint for the buffer.
* @param size New size of the buffer's data, in bytes.
* @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied.
* @except std::out_of_range Vertex buffer resize operation exceeded data bounds.
*/
void repurpose(buffer_usage usage, std::size_t size, const void* data = nullptr);
/// @{
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 = {});
/// @}
/**
* Resizes the vertex buffer.
*
* @param size New size of the buffer's data, in bytes.
* @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied.
* @param size New buffer size, in bytes.
* @param data New buffer data. If empty, buffer data will not be updated.
*
* @except std::out_of_range Vertex buffer resize operation exceeded data bounds.
*/
void resize(std::size_t size, const void* data = nullptr);
void resize(std::size_t size, std::span<const std::byte> data = {});
/**
* Writes data into the vertex buffer.
*
* @param offset Offset into the buffer's data, in bytes, where writing will begin.
* @param size Size of the write operation, in bytes.
* @param data Pointer to the data that will be written.
* @param data Data to write into the buffer.
*
* @except std::out_of_range Vertex buffer write operation exceeded buffer bounds.
*/
void write(std::size_t offset, std::size_t size, const void* data);
void write(std::span<const std::byte> data, std::size_t offset = 0);
/**
* Reads a subset of the buffer's data from the GL and returns it to the application.
*
* @param offset Offset into the buffer's data, in bytes, where reading will begin.
* @param size Size of the data to read, in bytes.
* @param data Pointer to where the read bytes will be stored.
* @param data Data buffer where the read bytes will be stored.
*
* @except std::out_of_range Vertex buffer read operation exceeded buffer bounds.
*/
void read(std::size_t offset, std::size_t size, void* data) const;
void read(std::span<std::byte> data, std::size_t offset = 0) const;
/**
* Copies a subset of another vertex buffer's data into this vertex buffer.
*
* @param read_buffer Buffer from which data will be read.
* @param copy_size Number of bytes to copy from the read buffer into this buffer.
* @param read_offset Offset into the read buffer's data, in bytes, where reading will begin.
* @param write_offset Offset into the this buffer's data, in bytes, where writing will begin.
*
* @except std::out_of_range Vertex buffer copy operation exceeded read buffer bounds.
* @except std::out_of_range Vertex buffer copy operation exceeded write buffer bounds.
*/
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.
std::size_t get_size() const;
[[nodiscard]] inline std::size_t size() const noexcept
{
return m_size;
}
/// Return's the buffer's usage hint.
buffer_usage get_usage() const;
[[nodiscard]] inline buffer_usage usage() const noexcept
{
return m_usage;
}
private:
friend class vertex_array;
std::uint_fast32_t gl_buffer_id;
std::size_t size;
buffer_usage usage;
unsigned int gl_buffer_id{0};
buffer_usage m_usage{buffer_usage::static_draw};
std::size_t m_size{0};
};
inline std::size_t vertex_buffer::get_size() const
{
return size;
}
inline buffer_usage vertex_buffer::get_usage() const
{
return usage;
}
} // namespace gl
#endif // ANTKEEPER_GL_VERTEX_BUFFER_HPP

+ 13
- 2
src/engine/i18n/string-map.cpp View File

@ -18,12 +18,13 @@
*/
#include <engine/i18n/string-map.hpp>
#include <engine/utility/hash/fnv1a.hpp>
#include <engine/resources/serializer.hpp>
#include <engine/resources/serialize-error.hpp>
#include <engine/resources/deserializer.hpp>
#include <engine/resources/deserialize-error.hpp>
#include <engine/resources/resource-loader.hpp>
#include <algorithm>
#include <utility>
/**
* Serializes a string map.
@ -76,7 +77,7 @@ void deserializer::deserialize(i18n::string_map& map, deserial
for (std::uint32_t i = 0; i < size; ++i)
{
// Read key
std::uint32_t key = 0;
hash::fnv1a32_t key;
ctx.read32<std::endian::big>(reinterpret_cast<std::byte*>(&key), 1);
// Read string length
@ -95,3 +96,13 @@ void deserializer::deserialize(i18n::string_map& map, deserial
ctx.read8(reinterpret_cast<std::byte*>(iterator->second.data()), length);
}
}
template <>
std::unique_ptr<i18n::string_map> resource_loader<i18n::string_map>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
auto resource = std::make_unique<i18n::string_map>();
deserializer<i18n::string_map>().deserialize(*resource, ctx);
return resource;
}

+ 2
- 1
src/engine/i18n/string-map.hpp View File

@ -22,13 +22,14 @@
#include <string>
#include <unordered_map>
#include <engine/utility/hash/fnv1a.hpp>
namespace i18n {
/**
* Maps 32-bit keys to strings.
*/
typedef std::unordered_map<std::uint32_t, std::string> string_map;
typedef std::unordered_map<hash::fnv1a32_t, std::string> string_map;
} // namespace i18n

+ 80
- 0
src/engine/i18n/string-table.cpp View File

@ -0,0 +1,80 @@
/*
* 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/i18n/string-table.hpp>
#include <engine/resources/deserializer.hpp>
#include <engine/resources/resource-loader.hpp>
/**
* Deserializes a string table.
*
* @param[out] file Text file to serialize.
* @param[in,out] ctx Deserialize context.
*
* @throw deserialize_error Read error.
*/
template <>
void deserializer<i18n::string_table>::deserialize(i18n::string_table& table, deserialize_context& ctx)
{
table.rows.clear();
std::vector<std::string> row;
std::string entry;
char c;
while (ctx.read8(reinterpret_cast<std::byte*>(&c), 1) == 1)
{
if (c == '\t')
{
row.push_back(entry);
entry.clear();
}
else if (c == '\n')
{
row.push_back(entry);
entry.clear();
table.rows.push_back(row);
row.clear();
}
else if (c != '\r')
{
entry.push_back(c);
}
}
if (!entry.empty())
{
row.push_back(entry);
}
if (!row.empty())
{
table.rows.push_back(row);
}
}
template <>
std::unique_ptr<i18n::string_table> resource_loader<i18n::string_table>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
auto resource = std::make_unique<i18n::string_table>();
deserializer<i18n::string_table>().deserialize(*resource, ctx);
return resource;
}

+ 6
- 7
src/engine/i18n/string-table.hpp View File

@ -26,14 +26,13 @@
namespace i18n {
/**
* A single row in a string table.
* Table of strings.
*/
typedef std::vector<std::string> string_table_row;
/**
* A table of strings.
*/
typedef std::vector<string_table_row> string_table;
struct string_table
{
/// Rows of column strings.
std::vector<std::vector<std::string>> rows;
};
} // namespace i18n

src/engine/resources/ephemeris-loader.cpp → src/engine/physics/orbit/ephemeris.cpp View File

@ -17,11 +17,11 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include <engine/resources/resource-loader.hpp>
#include <engine/physics/orbit/ephemeris.hpp>
#include <engine/utility/bit-math.hpp>
#include <bit>
#include <cstdint>
#include <physfs.h>
#include <engine/resources/deserializer.hpp>
#include <engine/resources/resource-loader.hpp>
/// Offset to time data in the JPL DE header, in bytes.
static constexpr std::size_t jpl_de_offset_time = 0xA5C;
@ -109,7 +109,7 @@ enum
};
/// Number of components for each JPL DE item.
static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] =
static constexpr std::uint8_t jpl_de_component_count[jpl_de_max_item_count] =
{
3, // Mercury: x,y,z (km)
3, // Venus: x,y,z (km)
@ -128,48 +128,48 @@ static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] =
1 // TT-TDB: t (seconds)
};
/// Reads and swaps the byte order of 32-bit numbers.
static PHYSFS_sint64 read_swap32(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len)
{
PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len);
for (std::uint32_t* ptr32 = (uint32_t*)buffer; len >= 8; len -= 8, ++ptr32)
*ptr32 = bit::swap32(*ptr32);
return status;
}
/// Reads and swaps the byte order of 64-bit numbers.
static PHYSFS_sint64 read_swap64(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len)
{
PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len);
for (std::uint64_t* ptr64 = (uint64_t*)buffer; len >= 8; len -= 8, ++ptr64)
*ptr64 = bit::swap64(*ptr64);
return status;
}
/**
* Deserializes an ephemeris.
*
* @param[out] file Text file to serialize.
* @param[in,out] ctx Deserialize context.
*
* @throw deserialize_error Read error.
*/
template <>
physics::orbit::ephemeris<double>* resource_loader<physics::orbit::ephemeris<double>>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
void deserializer<physics::orbit::ephemeris<double>>::deserialize(physics::orbit::ephemeris<double>& ephemeris, deserialize_context& ctx)
{
ephemeris.trajectories.clear();
// Init file reading function pointers
PHYSFS_sint64 (*read32)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes;
PHYSFS_sint64 (*read64)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes;
std::size_t (deserialize_context::*read32)(std::byte*, std::size_t) = &deserialize_context::read32<std::endian::native>;
std::size_t (deserialize_context::*read64)(std::byte*, std::size_t) = &deserialize_context::read64<std::endian::native>;
// Read DE version number
std::int32_t denum;
PHYSFS_seek(file, jpl_de_offset_denum);
PHYSFS_readBytes(file, &denum, sizeof(std::int32_t));
ctx.seek(jpl_de_offset_denum);
ctx.read8(reinterpret_cast<std::byte*>(&denum), sizeof(std::int32_t));
// If file endianness does not match host
// If file endianness does not match host endianness
if (denum & jpl_de_denum_endian_mask)
{
// Use endian-swapping read functions
read32 = &read_swap32;
read64 = &read_swap64;
if constexpr (std::endian::native == std::endian::little)
{
read32 = &deserialize_context::read32<std::endian::big>;
read64 = &deserialize_context::read64<std::endian::big>;
}
else
{
read32 = &deserialize_context::read32<std::endian::little>;
read64 = &deserialize_context::read64<std::endian::little>;
}
}
// Read ephemeris time
double ephemeris_time[3];
PHYSFS_seek(file, jpl_de_offset_time);
read64(file, ephemeris_time, sizeof(double) * 3);
ctx.seek(jpl_de_offset_time);
std::invoke(read64, ctx, reinterpret_cast<std::byte*>(ephemeris_time), 3);
// Make time relative to J2000 epoch
const double epoch = 2451545.0;
@ -178,29 +178,31 @@ physics::orbit::ephemeris* resource_loader
// Read number of constants
std::int32_t constant_count;
read32(file, &constant_count, sizeof(std::int32_t));
std::invoke(read32, ctx, reinterpret_cast<std::byte*>(&constant_count), 1);
// Read first coefficient table
std::int32_t coeff_table[jpl_de_max_item_count][3];
PHYSFS_seek(file, jpl_de_offset_table1);
read32(file, coeff_table, sizeof(std::int32_t) * jpl_de_table1_count * 3);
ctx.seek(jpl_de_offset_table1);
std::invoke(read32, ctx, reinterpret_cast<std::byte*>(coeff_table), jpl_de_table1_count * 3);
// Read second coefficient table
PHYSFS_seek(file, jpl_de_offset_table2);
read32(file, &coeff_table[jpl_de_table1_count][0], sizeof(std::int32_t) * jpl_de_table2_count * 3);
ctx.seek(jpl_de_offset_table2);
std::invoke(read32, ctx, reinterpret_cast<std::byte*>(&coeff_table[jpl_de_table1_count][0]), jpl_de_table2_count * 3);
// Seek past extra constant names
if (constant_count > jpl_de_constant_limit)
PHYSFS_seek(file, jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length);
{
ctx.seek(jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length);
}
// Read third coefficient table
read32(file, &coeff_table[jpl_de_table1_count + jpl_de_table2_count][0], sizeof(std::int32_t) * jpl_de_table3_count * 3);
std::invoke(read32, ctx, reinterpret_cast<std::byte*>(&coeff_table[jpl_de_table1_count + jpl_de_table2_count][0]), jpl_de_table3_count * 3);
// Calculate number of coefficients per record
std::int32_t record_coeff_count = 0;
for (int i = 0; i < jpl_de_max_item_count; ++i)
{
std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * jpl_de_component_count[i] - 1;
std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * static_cast<std::int32_t>(jpl_de_component_count[i]) - 1;
record_coeff_count = std::max(record_coeff_count, coeff_count);
}
@ -211,16 +213,17 @@ physics::orbit::ephemeris* resource_loader
// Calculate coefficient strides
std::size_t strides[11];
for (int i = 0; i < 11; ++i)
{
strides[i] = coeff_table[i][2] * coeff_table[i][1] * 3;
}
// Allocate and resize ephemeris to accommodate items 0-10
physics::orbit::ephemeris<double>* ephemeris = new physics::orbit::ephemeris<double>();
ephemeris->resize(11);
// Resize ephemeris to accommodate items 0-10
ephemeris.trajectories.resize(11);
// Init trajectories
for (int i = 0; i < 11; ++i)
{
auto& trajectory = (*ephemeris)[i];
auto& trajectory = ephemeris.trajectories[i];
trajectory.t0 = ephemeris_time[0];
trajectory.t1 = ephemeris_time[1];
trajectory.dt = ephemeris_time[2] / static_cast<double>(coeff_table[i][2]);
@ -232,13 +235,21 @@ physics::orbit::ephemeris* resource_loader
for (std::size_t i = 0; i < record_count; ++i)
{
// Seek to coefficient record
PHYSFS_seek(file, (i + 2) * record_size + 2 * sizeof(double));
ctx.seek((i + 2) * record_size + 2 * sizeof(double));
for (int j = 0; j < 11; ++j)
{
read64(file, &(*ephemeris)[j].a[i * strides[j]], sizeof(double) * strides[j]);
std::invoke(read64, ctx, reinterpret_cast<std::byte*>(&ephemeris.trajectories[j].a[i * strides[j]]), strides[j]);
}
}
}
template <>
std::unique_ptr<physics::orbit::ephemeris<double>> resource_loader<physics::orbit::ephemeris<double>>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
auto resource = std::make_unique<physics::orbit::ephemeris<double>>();
deserializer<physics::orbit::ephemeris<double>>().deserialize(*resource, ctx);
return ephemeris;
return resource;
}

+ 6
- 1
src/engine/physics/orbit/ephemeris.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP
#include <engine/physics/orbit/trajectory.hpp>
#include <vector>
namespace physics {
namespace orbit {
@ -31,7 +32,11 @@ namespace orbit {
* @tparam t Real type.
*/
template <class T>
using ephemeris = std::vector<trajectory<T>>;
struct ephemeris
{
/// Table of orbital trajectories.
std::vector<trajectory<T>> trajectories;
};
} // namespace orbit
} // namespace physics

+ 1
- 1
src/engine/physics/orbit/trajectory.hpp View File

@ -56,7 +56,7 @@ struct trajectory
* @param t Time, on `[t0, t1)`.
* @return Trajectory position at time @p t.
*/
math::vector<T, 3> position(T t) const;
[[nodiscard]] math::vector<T, 3> position(T t) const;
};
template <class T>

+ 2
- 2
src/engine/render/compositor.cpp View File

@ -37,9 +37,9 @@ void compositor::remove_passes()
passes.clear();
}
void compositor::composite(const render::context& ctx, render::queue& queue) const
void compositor::composite(const render::context& ctx, render::queue& queue)
{
for (const pass* pass: passes)
for (pass* pass: passes)
{
if (pass->is_enabled())
{

+ 1
- 1
src/engine/render/compositor.hpp View File

@ -38,7 +38,7 @@ public:
void remove_pass(pass* pass);
void remove_passes();
void composite(const render::context& ctx, render::queue& queue) const;
void composite(const render::context& ctx, render::queue& queue);
const std::list<pass*>* get_passes() const;

src/engine/render/blend-mode.hpp → src/engine/render/material-blend-mode.hpp View File

@ -17,26 +17,28 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_RENDER_BLEND_MODE_HPP
#define ANTKEEPER_RENDER_BLEND_MODE_HPP
#ifndef ANTKEEPER_RENDER_MATERIAL_BLEND_MODE_HPP
#define ANTKEEPER_RENDER_MATERIAL_BLEND_MODE_HPP
#include <cstdint>
namespace render {
/**
* Material blend modes.
*/
enum class blend_mode
enum class material_blend_mode: std::uint8_t
{
/// Fully opaque.
/// Material is fully opaque.
opaque,
/// Binary masked opacity.
/// Material has binary masked opacity.
masked,
/// Translucent.
/// Material is translucent.
translucent
};
} // namespace render
#endif // ANTKEEPER_RENDER_BLEND_MODE_HPP
#endif // ANTKEEPER_RENDER_MATERIAL_BLEND_MODE_HPP

+ 0
- 1
src/engine/render/material-flags.hpp View File

@ -29,4 +29,3 @@
#define MATERIAL_FLAG_WIREFRAME 0x80000000
#endif // ANTKEEPER_MATERIAL_FLAGS_HPP

+ 0
- 460
src/engine/render/material-property.hpp View File

@ -1,460 +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_RENDER_MATERIAL_PROPERTY_HPP
#define ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP
#include <engine/animation/tween.hpp>
#include <engine/gl/shader-variable-type.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/math/interpolation.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <engine/gl/shader-program.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 <cstddef>
namespace render {
class material;
/**
* Abstract base class for material properties.
*/
class material_property_base
{
public:
/**
* Connects the material property to a shader input.
*
* @param input Shader input to which the material property should be connected.
* @return `true` if the property was connected to the input successfully, `false` otherwise.
*/
bool connect(const gl::shader_input* input);
/**
* Disconnects the material property from its shader input.
*/
void disconnect();
/**
* Sets state 0 = state 1.
*/
virtual void update_tweens() = 0;
/**
* Uploads the material property to its shader program.
*
* @param a Interpolation factor. Should be on `[0.0, 1.0]`.
* @return `true` if the property was uploaded successfully, `false` otherwise.
*/
virtual bool upload(double a) const = 0;
/**
* Returns the type of data which the property contains.
*/
virtual gl::shader_variable_type get_data_type() const = 0;
/**
* Returns `true` if the material property is connected to a shader input, `false` otherwise.
*/
bool is_connected() const;
/**
* Creates a copy of this material property.
*/
virtual material_property_base* clone() const = 0;
protected:
material_property_base();
const gl::shader_input* input;
};
inline bool material_property_base::is_connected() const
{
return (input != nullptr);
}
/**
* A property of a material which can be uploaded to a shader program via a shader input.
*
* @tparam T Property data type.
*/
template <class T>
class material_property: public material_property_base
{
public:
typedef tween<T> tween_type;
typedef typename tween<T>::interpolator_type interpolator_type;
/// Default tween interpolator function for this material property type.
static T default_interpolator(const T& x, const T& y, double a);
/**
* Creates a material property.
*
* @param element_count Number of elements in the property array.
*/
material_property(std::size_t element_count);
/**
* Destroys a material property.
*/
virtual ~material_property();
material_property(const material_property<T>&) = delete;
material_property<T>& operator=(const material_property<T>&) = delete;
/// @copydoc material_property_base::update_tweens()
virtual void update_tweens();
/// @copydoc material_property_base::upload() const
virtual bool upload(double a) const;
/**
* Sets the value of this property.
*
* @param value Value to set.
*/
void set_value(const T& value);
/**
* Sets the value of a single element in this array property.
*
* @param index Index of an array element.
* @param value Value to set.
*/
void set_value(std::size_t index, const T& value);
/**
* Sets the values of a range of elements in this array property.
*
* @param index Index of the first array element to set.
* @param values Pointer to an array of values to set.
* @param count Number of elements to set.
*/
void set_values(std::size_t index, const T* values, std::size_t count);
/**
* Sets the tween interpolator function.
*
* @param interpolator Tween interpolator function.
*/
void set_tween_interpolator(interpolator_type interpolator);
/// Returns the value of the first element in this property.
const T& get_value() const;
/**
* Returns the value of the first element in this property.
*
* @param index Index of an array element.
* @return Value of the element at the specified index.
*/
const T& get_value(std::size_t index) const;
/// @copydoc material_property_base::get_data_type() const
virtual gl::shader_variable_type get_data_type() const;
/// @copydoc material_property_base::clone() const
virtual material_property_base* clone() const;
private:
std::size_t element_count;
tween<T>* values;
};
template <typename T>
inline T material_property<T>::default_interpolator(const T& x, const T& y, double a)
{
return y;
}
template <>
inline float material_property<float>::default_interpolator(const float& x, const float& y, double a)
{
return math::lerp<float, float>(x, y, static_cast<float>(a));
}
template <>
inline float2 material_property<float2>::default_interpolator(const float2& x, const float2& y, double a)
{
return math::lerp<float2, float>(x, y, static_cast<float>(a));
}
template <>
inline float3 material_property<float3>::default_interpolator(const float3& x, const float3& y, double a)
{
return math::lerp<float3, float>(x, y, static_cast<float>(a));
}
template <>
inline float4 material_property<float4>::default_interpolator(const float4& x, const float4& y, double a)
{
return math::lerp<float4, float>(x, y, static_cast<float>(a));
}
template <class T>
material_property<T>::material_property(std::size_t element_count):
element_count(element_count),
values(nullptr)
{
values = new tween<T>[element_count];
set_tween_interpolator(default_interpolator);
}
template <class T>
material_property<T>::~material_property()
{
delete[] values;
}
template <class T>
void material_property<T>::update_tweens()
{
for (std::size_t i = 0; i < element_count; ++i)
{
values[i].update();
}
}
template <class T>
bool material_property<T>::upload(double a) const
{
if (!is_connected())
{
return false;
}
if (element_count > 1)
{
for (std::size_t i = 0; i < element_count; ++i)
{
if (!input->upload(i, values[i].interpolate(static_cast<float>(a))))
return false;
}
return true;
}
else
{
return input->upload(values[0].interpolate(static_cast<float>(a)));
}
}
template <class T>
void material_property<T>::set_value(const T& value)
{
values[0][1] = value;
}
template <class T>
void material_property<T>::set_value(std::size_t index, const T& value)
{
values[index][1] = value;
}
template <class T>
void material_property<T>::set_values(std::size_t index, const T* values, std::size_t count)
{
for (std::size_t i = 0; i < count; ++i)
{
this->values[index + i][1] = values[i];
}
}
template <class T>
void material_property<T>::set_tween_interpolator(interpolator_type interpolator)
{
for (std::size_t i = 0; i < element_count; ++i)
{
this->values[i].set_interpolator(interpolator);
}
}
template <class T>
inline const T& material_property<T>::get_value() const
{
return values[0][1];
}
template <class T>
inline const T& material_property<T>::get_value(std::size_t index) const
{
return values[index][1];
}
template <>
inline gl::shader_variable_type material_property<bool>::get_data_type() const
{
return gl::shader_variable_type::bool1;
}
template <>
inline gl::shader_variable_type material_property<bool2>::get_data_type() const
{
return gl::shader_variable_type::bool2;
}
template <>
inline gl::shader_variable_type material_property<bool3>::get_data_type() const
{
return gl::shader_variable_type::bool3;
}
template <>
inline gl::shader_variable_type material_property<bool4>::get_data_type() const
{
return gl::shader_variable_type::bool4;
}
template <>
inline gl::shader_variable_type material_property<int>::get_data_type() const
{
return gl::shader_variable_type::int1;
}
template <>
inline gl::shader_variable_type material_property<int2>::get_data_type() const
{
return gl::shader_variable_type::int2;
}
template <>
inline gl::shader_variable_type material_property<int3>::get_data_type() const
{
return gl::shader_variable_type::int3;
}
template <>
inline gl::shader_variable_type material_property<int4>::get_data_type() const
{
return gl::shader_variable_type::int4;
}
template <>
inline gl::shader_variable_type material_property<unsigned int>::get_data_type() const
{
return gl::shader_variable_type::uint1;
}
template <>
inline gl::shader_variable_type material_property<uint2>::get_data_type() const
{
return gl::shader_variable_type::uint2;
}
template <>
inline gl::shader_variable_type material_property<uint3>::get_data_type() const
{
return gl::shader_variable_type::uint3;
}
template <>
inline gl::shader_variable_type material_property<uint4>::get_data_type() const
{
return gl::shader_variable_type::uint4;
}
template <>
inline gl::shader_variable_type material_property<float>::get_data_type() const
{
return gl::shader_variable_type::float1;
}
template <>
inline gl::shader_variable_type material_property<float2>::get_data_type() const
{
return gl::shader_variable_type::float2;
}
template <>
inline gl::shader_variable_type material_property<float3>::get_data_type() const
{
return gl::shader_variable_type::float3;
}
template <>
inline gl::shader_variable_type material_property<float4>::get_data_type() const
{
return gl::shader_variable_type::float4;
}
template <>
inline gl::shader_variable_type material_property<float2x2>::get_data_type() const
{
return gl::shader_variable_type::float2x2;
}
template <>
inline gl::shader_variable_type material_property<float3x3>::get_data_type() const
{
return gl::shader_variable_type::float3x3;
}
template <>
inline gl::shader_variable_type material_property<float4x4>::get_data_type() const
{
return gl::shader_variable_type::float4x4;
}
template <>
inline gl::shader_variable_type material_property<const gl::texture_1d*>::get_data_type() const
{
return gl::shader_variable_type::texture_1d;
}
template <>
inline gl::shader_variable_type material_property<const gl::texture_2d*>::get_data_type() const
{
return gl::shader_variable_type::texture_2d;
}
template <>
inline gl::shader_variable_type material_property<const gl::texture_3d*>::get_data_type() const
{
return gl::shader_variable_type::texture_3d;
}
template <>
inline gl::shader_variable_type material_property<const gl::texture_cube*>::get_data_type() const
{
return gl::shader_variable_type::texture_cube;
}
template <class T>
material_property_base* material_property<T>::clone() const
{
material_property<T>* property = new material_property<T>(element_count);
for (std::size_t i = 0; i < element_count; ++i)
{
property->values[i][0] = values[i][0];
property->values[i][1] = values[i][1];
}
property->input = input;
return property;
}
} // namespace render
#endif // ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP

src/engine/render/shadow-mode.hpp → src/engine/render/material-shadow-mode.hpp View File

@ -17,23 +17,25 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_RENDER_SHADOW_MODE_HPP
#define ANTKEEPER_RENDER_SHADOW_MODE_HPP
#ifndef ANTKEEPER_RENDER_MATERIAL_SHADOW_MODE_HPP
#define ANTKEEPER_RENDER_MATERIAL_SHADOW_MODE_HPP
#include <cstdint>
namespace render {
/**
* Material shadow casting modes.
*/
enum class shadow_mode
enum class material_shadow_mode: std::uint8_t
{
/// Fully opaque shadow casting.
opaque,
/// Material does not cast shadows.
none,
/// No shadows cast.
none
/// Material casts fully opaque shadows.
opaque
};
} // namespace render
#endif // ANTKEEPER_RENDER_SHADOW_MODE_HPP
#endif // ANTKEEPER_RENDER_MATERIAL_SHADOW_MODE_HPP

src/engine/render/material-property.cpp → src/engine/render/material-variable-type.hpp View File

@ -17,30 +17,43 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include <engine/render/material-property.hpp>
#include <engine/gl/shader-input.hpp>
#ifndef ANTKEEPER_RENDER_MATERIAL_VARIABLE_TYPE_HPP
#define ANTKEEPER_RENDER_MATERIAL_VARIABLE_TYPE_HPP
namespace render {
material_property_base::material_property_base():
input(nullptr)
{}
bool material_property_base::connect(const gl::shader_input* input)
{
if (!input || input->get_data_type() != get_data_type())
{
return false;
}
#include <cstdint>
this->input = input;
return true;
}
namespace render {
void material_property_base::disconnect()
/**
* Material variable data types.
*/
enum class material_variable_type: std::uint8_t
{
this->input = nullptr;
}
bool1,
bool2,
bool3,
bool4,
int1,
int2,
int3,
int4,
uint1,
uint2,
uint3,
uint4,
float1,
float2,
float3,
float4,
float2x2,
float3x3,
float4x4,
texture_1d,
texture_2d,
texture_3d,
texture_cube
};
} // namespace render
#endif // ANTKEEPER_RENDER_MATERIAL_VARIABLE_TYPE_HPP

+ 373
- 0
src/engine/render/material-variable.hpp View File

@ -0,0 +1,373 @@
/*
* 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_RENDER_MATERIAL_VARIABLE_HPP
#define ANTKEEPER_RENDER_MATERIAL_VARIABLE_HPP
#include <engine/utility/fundamental-types.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/render/material-variable-type.hpp>
#include <memory>
#include <vector>
namespace render {
/**
* Abstract base class for material variables.
*/
class material_variable_base
{
public:
/**
* Destructs a material variable base.
*/
virtual ~material_variable_base() = default;
/**
* Returns the material variable data type.
*/
[[nodiscard]] virtual constexpr material_variable_type type() const noexcept = 0;
/**
* Returns the number of elements in an array variable, or `1` if the variable is not an array.
*/
[[nodiscard]] virtual std::size_t size() const noexcept = 0;
/**
* Creates a copy of this material property.
*/
[[nodiscard]] virtual std::unique_ptr<material_variable_base> clone() const = 0;
};
/**
* Material variable.
*
* @tparam T Material variable value type.
*/
template <class T>
class material_variable: public material_variable_base
{
public:
/// Material variable element type.
using element_type = T;
/**
* Constructs a material variable.
*
* @param size Number of elements in the array, or `1` if the variable is not an array.
* @param value Value with which to initialize the elements.
*/
inline material_variable(std::size_t size, const element_type& value = element_type()):
elements(size, value)
{}
/**
* Constructs a material variable with a single element.
*/
inline material_variable():
material_variable(1)
{}
/**
* Constructs a material variable from a list of element values.
*
* @param list List of element values.
*/
inline material_variable(std::initializer_list<element_type> list):
elements(list)
{}
[[nodiscard]] virtual constexpr material_variable_type type() const noexcept override;
[[nodiscard]] inline std::size_t size() const noexcept override
{
return elements.size();
}
[[nodiscard]] inline std::unique_ptr<material_variable_base> clone() const override
{
return std::make_unique<material_variable<T>>(*this);
}
/**
* Sets the value of the the variable, or the value of the first element if the variable is an array.
*
* @param value Value to set.
*/
inline void set(const element_type& value)
{
elements.front() = value;
}
/**
* Sets the value of a single element in an array variable.
*
* @param index Index of an element.
* @param value Value to set.
*/
inline void set(std::size_t index, const element_type& value)
{
elements[index] = value;
}
/**
* Returns a reference to the first element in the array.
*/
[[nodiscard]] inline const element_type& get() const
{
return elements.front();
}
/**
* Returns a reference to the element at a given index.
*
* @param index Index of an element.
*
* @return Reference to the element at @p index.
*/
[[nodiscard]] inline const element_type& get(std::size_t index) const
{
return elements[index];
}
/**
* Returns a pointer to the element array.
*/
[[nodiscard]] inline const element_type* data() const noexcept
{
return elements.data();
}
private:
std::vector<element_type> elements;
};
/// Boolean material variable.
using material_bool = material_variable<bool>;
/// 2-dimensional boolean vector material variable.
using material_bool2 = material_variable<bool2>;
/// 3-dimensional boolean vector material variable.
using material_bool3 = material_variable<bool3>;
/// 4-dimensional boolean vector material variable.
using material_bool4 = material_variable<bool4>;
/// Integer material variable.
using material_int = material_variable<int>;
/// 2-dimensional integer vector material variable.
using material_int2 = material_variable<int2>;
/// 3-dimensional integer vector material variable.
using material_int3 = material_variable<int3>;
/// 4-dimensional integer vector material variable.
using material_int4 = material_variable<int4>;
/// Unsigned integer material variable.
using material_uint = material_variable<unsigned int>;
/// 2-dimensional unsigned integer vector material variable.
using material_uint2 = material_variable<uint2>;
/// 3-dimensional unsigned integer vector material variable.
using material_uint3 = material_variable<uint3>;
/// 4-dimensional unsigned integer vector material variable.
using material_uint4 = material_variable<uint4>;
/// Floating-point material variable.
using material_float = material_variable<float>;
/// 2-dimensional floating-point vector material variable.
using material_float2 = material_variable<float2>;
/// 3-dimensional floating-point vector material variable.
using material_float3 = material_variable<float3>;
/// 4-dimensional floating-point vector material variable.
using material_float4 = material_variable<float4>;
/// 2x2 floating-point matrix material variable.
using material_float2x2 = material_variable<float2x2>;
/// 3x3 floating-point matrix material variable.
using material_float3x3 = material_variable<float3x3>;
/// 4x4 floating-point matrix material variable.
using material_float4x4 = material_variable<float4x4>;
/// 1-dimensional texture material variable.
using material_texture_1d = material_variable<std::shared_ptr<gl::texture_1d>>;
/// 2-dimensional texture material variable.
using material_texture_2d = material_variable<std::shared_ptr<gl::texture_2d>>;
/// 3-dimensional texture material variable.
using material_texture_3d = material_variable<std::shared_ptr<gl::texture_3d>>;
/// Cube texture material variable.
using material_texture_cube = material_variable<std::shared_ptr<gl::texture_cube>>;
template <>
inline constexpr material_variable_type material_bool::type() const noexcept
{
return material_variable_type::bool1;
}
template <>
inline constexpr material_variable_type material_bool2::type() const noexcept
{
return material_variable_type::bool2;
}
template <>
inline constexpr material_variable_type material_bool3::type() const noexcept
{
return material_variable_type::bool3;
}
template <>
inline constexpr material_variable_type material_bool4::type() const noexcept
{
return material_variable_type::bool4;
}
template <>
inline constexpr material_variable_type material_int::type() const noexcept
{
return material_variable_type::int1;
}
template <>
inline constexpr material_variable_type material_int2::type() const noexcept
{
return material_variable_type::int2;
}
template <>
inline constexpr material_variable_type material_int3::type() const noexcept
{
return material_variable_type::int3;
}
template <>
inline constexpr material_variable_type material_int4::type() const noexcept
{
return material_variable_type::int4;
}
template <>
inline constexpr material_variable_type material_uint::type() const noexcept
{
return material_variable_type::uint1;
}
template <>
inline constexpr material_variable_type material_uint2::type() const noexcept
{
return material_variable_type::uint2;
}
template <>
inline constexpr material_variable_type material_uint3::type() const noexcept
{
return material_variable_type::uint3;
}
template <>
inline constexpr material_variable_type material_uint4::type() const noexcept
{
return material_variable_type::uint4;
}
template <>
inline constexpr material_variable_type material_float::type() const noexcept
{
return material_variable_type::float1;
}
template <>
inline constexpr material_variable_type material_float2::type() const noexcept
{
return material_variable_type::float2;
}
template <>
inline constexpr material_variable_type material_float3::type() const noexcept
{
return material_variable_type::float3;
}
template <>
inline constexpr material_variable_type material_float4::type() const noexcept
{
return material_variable_type::float4;
}
template <>
inline constexpr material_variable_type material_float2x2::type() const noexcept
{
return material_variable_type::float2x2;
}
template <>
inline constexpr material_variable_type material_float3x3::type() const noexcept
{
return material_variable_type::float3x3;
}
template <>
inline constexpr material_variable_type material_float4x4::type() const noexcept
{
return material_variable_type::float4x4;
}
template <>
inline constexpr material_variable_type material_texture_1d::type() const noexcept
{
return material_variable_type::texture_1d;
}
template <>
inline constexpr material_variable_type material_texture_2d::type() const noexcept
{
return material_variable_type::texture_2d;
}
template <>
inline constexpr material_variable_type material_texture_3d::type() const noexcept
{
return material_variable_type::texture_3d;
}
template <>
inline constexpr material_variable_type material_texture_cube::type() const noexcept
{
return material_variable_type::texture_cube;
}
} // namespace render
#endif // ANTKEEPER_RENDER_MATERIAL_VARIABLE_HPP

+ 456
- 79
src/engine/render/material.cpp View File

@ -18,140 +18,517 @@
*/
#include <engine/render/material.hpp>
#include <engine/render/material-variable.hpp>
#include <engine/resources/resource-loader.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/render/material-flags.hpp>
#include <engine/utility/json.hpp>
#include <engine/utility/hash/combine.hpp>
#include <utility>
#include <type_traits>
#include <string>
namespace render {
material::material(gl::shader_program* program):
program(program),
flags(0),
blend_mode(blend_mode::opaque),
opacity_threshold(0.5f),
two_sided(false),
shadow_mode(shadow_mode::opaque)
{}
material::material():
material(nullptr)
{}
material::material(const material& other)
{
*this = other;
}
material::~material()
material& material::operator=(const material& other)
{
for (material_property_base* property: properties)
two_sided = other.two_sided;
blend_mode = other.blend_mode;
shadow_mode = other.shadow_mode;
flags = other.flags;
shader_template = other.shader_template;
variable_map.clear();
for (const auto& [key, value]: other.variable_map)
{
delete property;
if (value)
{
variable_map.emplace(key, value->clone());
}
}
m_hash = other.m_hash;
return *this;
}
material& material::operator=(const material& other)
void material::set_two_sided(bool two_sided) noexcept
{
// Remove all properties
for (material_property_base* property: properties)
{
delete property;
}
properties.clear();
property_map.clear();
this->two_sided = two_sided;
rehash();
}
this->program = other.program;
this->flags = other.flags;
this->blend_mode = other.blend_mode;
this->opacity_threshold = other.opacity_threshold;
this->two_sided = other.two_sided;
this->shadow_mode = other.shadow_mode;
for (auto it = other.property_map.begin(); it != other.property_map.end(); ++it)
{
material_property_base* property = it->second->clone();
properties.push_back(property);
property_map[it->first] = property;
}
void material::set_blend_mode(material_blend_mode mode) noexcept
{
blend_mode = mode;
rehash();
}
return *this;
void material::set_shadow_mode(material_shadow_mode mode) noexcept
{
shadow_mode = mode;
rehash();
}
void material::set_flags(std::uint32_t flags) noexcept
{
this->flags = flags;
rehash();
}
void material::set_shader_template(std::shared_ptr<gl::shader_template> shader_template)
{
this->shader_template = shader_template;
rehash();
}
void material::update_tweens()
void material::set_variable(hash::fnv1a32_t key, std::shared_ptr<material_variable_base> value)
{
for (material_property_base* property: properties)
variable_map[key] = std::move(value);
}
std::shared_ptr<material_variable_base> material::get_variable(hash::fnv1a32_t key) const
{
if (auto i = variable_map.find(key); i != variable_map.end())
{
property->update_tweens();
return i->second;
}
return nullptr;
}
std::size_t material::upload(double a) const
void material::rehash() noexcept
{
if (!program)
m_hash = 0;
if (shader_template)
{
return false;
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));
}
std::size_t failed_upload_count = 0;
} // namespace render
for (material_property_base* property: properties)
template <typename T>
static bool read_value(T* value, const nlohmann::json& json, const std::string& name)
{
if (auto element = json.find(name); element != json.end())
{
if (!property->upload(a))
{
++failed_upload_count;
}
*value = element.value().get<T>();
return true;
}
return failed_upload_count;
return false;
}
void material::set_shader_program(gl::shader_program* program)
static bool load_texture_1d_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json)
{
this->program = program;
reconnect_properties();
// If JSON element is an array
if (json.is_array())
{
// Create variable
auto variable = std::make_shared<render::material_texture_1d>(json.size());
// Load textures
std::size_t i = 0;
for (const auto& element: json)
{
variable->set(i, resource_manager.load<gl::texture_1d>(element.get<std::string>()));
++i;
}
material.set_variable(key, variable);
}
else
{
// Create variable
auto variable = std::make_shared<render::material_texture_1d>(json.size());
// Load texture
variable->set(resource_manager.load<gl::texture_1d>(json.get<std::string>()));
material.set_variable(key, variable);
}
return true;
}
void material::set_flags(std::uint32_t flags) noexcept
static bool load_texture_2d_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json)
{
this->flags = flags;
// If JSON element is an array
if (json.is_array())
{
// Create variable
auto variable = std::make_shared<render::material_texture_2d>(json.size());
// Load textures
std::size_t i = 0;
for (const auto& element: json)
{
variable->set(i, resource_manager.load<gl::texture_2d>(element.get<std::string>()));
++i;
}
material.set_variable(key, variable);
}
else
{
// Create variable
auto variable = std::make_shared<render::material_texture_2d>(json.size());
// Load texture
variable->set(resource_manager.load<gl::texture_2d>(json.get<std::string>()));
material.set_variable(key, variable);
}
return true;
}
void material::set_blend_mode(render::blend_mode mode) noexcept
static bool load_texture_cube_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json)
{
blend_mode = mode;
return false;
}
void material::set_opacity_threshold(float threshold) noexcept
template <typename T>
static bool load_scalar_property(render::material& material, hash::fnv1a32_t key, const nlohmann::json& json)
{
opacity_threshold = threshold;
// If JSON element is an array
if (json.is_array())
{
// Determine size of the array
std::size_t array_size = json.size();
// Create variable
auto variable = std::make_shared<render::material_variable<T>>(json.size());
// Set variable values
std::size_t i = 0;
for (const auto& element: json)
{
variable->set(i, element.get<T>());
}
material.set_variable(key, variable);
}
else
{
material.set_variable(key, std::make_shared<render::material_variable<T>>(1, json.get<T>()));
}
return true;
}
void material::set_two_sided(bool two_sided) noexcept
template <typename T>
static bool load_vector_property(render::material& material, hash::fnv1a32_t key, std::size_t vector_size, const nlohmann::json& json)
{
this->two_sided = two_sided;
// If JSON element is an array of arrays
if (json.is_array() && json.begin().value().is_array())
{
// Determine size of the array
std::size_t array_size = json.size();
// Create variable
auto variable = std::make_shared<render::material_variable<T>>(json.size());
// For each vector in the array
std::size_t i = 0;
for (const auto& vector_element: json)
{
// Read vector elements
T value;
std::size_t j = 0;
for (const auto& value_element: vector_element)
value[j++] = value_element.get<typename T::element_type>();
variable->set(i, value);
++i;
}
material.set_variable(key, variable);
}
else
{
// Read vector elements
T value;
std::size_t i = 0;
for (const auto& value_element: json)
value[i++] = value_element.get<typename T::element_type>();
material.set_variable(key, std::make_shared<render::material_variable<T>>(1, value));
}
return true;
}
void material::set_shadow_mode(render::shadow_mode mode) noexcept
template <typename T>
static bool load_matrix_property(render::material& material, hash::fnv1a32_t key, std::size_t column_count, std::size_t row_count, const nlohmann::json& json)
{
shadow_mode = mode;
// If JSON element is an array of arrays of arrays
if (json.is_array() && json.begin().value().is_array())
{
if (json.begin().value().begin().value().is_array())
{
// Create variable
auto variable = std::make_shared<render::material_variable<T>>(json.size());
// For each matrix in the array
std::size_t i = 0;
for (const auto& matrix_element: json)
{
// Read vector elements
T value;
std::size_t j = 0;
for (const auto& column_element: matrix_element)
{
std::size_t k = 0;
for (const auto& row_element: column_element)
{
value[j][k] = row_element.get<typename T::element_type>();
++k;
}
++j;
}
// Set matrix value
variable->set(i, value);
++i;
}
material.set_variable(key, variable);
return true;
}
else
{
// Read matrix elements
T value;
std::size_t i = 0;
for (const auto& column_element: json)
{
std::size_t j = 0;
for (const auto& row_element: column_element)
{
value[i][j] = row_element.get<typename T::element_type>();
++j;
}
++i;
}
material.set_variable(key, std::make_shared<render::material_variable<T>>(1, value));
return true;
}
}
return false;
}
std::size_t material::reconnect_properties()
template <>
std::unique_ptr<render::material> resource_loader<render::material>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
std::size_t disconnected_property_count = properties.size();
for (auto it = property_map.begin(); it != property_map.end(); ++it)
auto material = std::make_unique<render::material>();
// Load JSON data
auto json = resource_loader<nlohmann::json>::load(resource_manager, ctx);
// Read two sided
bool two_sided = false;
read_value(&two_sided, *json, "two_sided");
material->set_two_sided(two_sided);
// Read blend mode
std::string blend_mode;
read_value(&blend_mode, *json, "blend_mode");
if (blend_mode == "opaque")
{
material_property_base* property = it->second;
property->disconnect();
if (program != nullptr)
material->set_blend_mode(render::material_blend_mode::opaque);
}
else if (blend_mode == "masked")
{
material->set_blend_mode(render::material_blend_mode::masked);
}
else if (blend_mode == "translucent")
{
material->set_blend_mode(render::material_blend_mode::translucent);
}
// Read shadow mode
std::string shadow_mode;
read_value(&shadow_mode, *json, "shadow_mode");
if (shadow_mode == "opaque")
{
material->set_shadow_mode(render::material_shadow_mode::opaque);
}
else if (shadow_mode == "none")
{
material->set_shadow_mode(render::material_shadow_mode::none);
}
// Init material flags
std::uint32_t flags = 0;
// Read depth mode
std::string depth_mode;
read_value(&depth_mode, *json, "depth_mode");
if (depth_mode == "in_front")
flags |= MATERIAL_FLAG_X_RAY;
// Read decal mode
std::string decal_mode;
read_value(&decal_mode, *json, "decal_mode");
if (decal_mode == "decal")
flags |= MATERIAL_FLAG_DECAL;
else if (decal_mode == "surface")
flags |= MATERIAL_FLAG_DECAL_SURFACE;
// Set material flags
material->set_flags(flags);
// Read shader template filename
std::string shader_template_filename;
if (read_value(&shader_template_filename, *json, "shader_template"))
{
// Loader shader template
material->set_shader_template(resource_manager.load<gl::shader_template>(shader_template_filename));
}
// Read material variables
if (auto variables_element = json->find("variables"); variables_element != json->end())
{
for (const auto& variable_element: variables_element.value())
{
if (property->connect(program->get_input(it->first)))
// Read variable name
std::string name;
if (!read_value(&name, variable_element, "name"))
{
// Ignore nameless properties
continue;
}
// Read variable type
std::string type;
if (!read_value(&type, variable_element, "type"))
{
// Ignore typeless properties
continue;
}
// Find value element
auto value_element = variable_element.find("value");
if (value_element == variable_element.end())
{
// Ignore valueless properties
continue;
}
// Hash variable name
const hash::fnv1a32_t key = hash::fnv1a32<char>(name);
if (type == "texture_1d")
{
--disconnected_property_count;
load_texture_1d_property(resource_manager, *material, key, value_element.value());
}
else if (type == "texture_2d")
{
load_texture_2d_property(resource_manager, *material, key, value_element.value());
}
else if (type == "texture_cube")
{
load_texture_cube_property(resource_manager, *material, key, value_element.value());
}
// If variable type is a matrix
else if (type[type.size() - 2] == 'x' &&
std::isdigit(type[type.size() - 3]) &&
std::isdigit(type.back()))
{
std::size_t columns = std::stoul(type.substr(type.size() - 3, 1));
std::size_t rows = std::stoul(type.substr(type.size() - 1, 1));
if (type.find("float") != std::string::npos)
{
if (columns == 2 && rows == 2)
load_matrix_property<float2x2>(*material, key, columns, rows, value_element.value());
else if (columns == 3 && rows == 3)
load_matrix_property<float3x3>(*material, key, columns, rows, value_element.value());
else if (columns == 4 && rows == 4)
load_matrix_property<float4x4>(*material, key, columns, rows, value_element.value());
}
}
// If variable type is a vector
else if (std::isdigit(type.back()))
{
std::size_t size = std::stoul(type.substr(type.size() - 1, 1));
if (type.find("float") != std::string::npos)
{
if (size == 2)
load_vector_property<float2>(*material, key, size, value_element.value());
else if (size == 3)
load_vector_property<float3>(*material, key, size, value_element.value());
else if (size == 4)
load_vector_property<float4>(*material, key, size, value_element.value());
}
else if (type.find("uint") != std::string::npos)
{
if (size == 2)
load_vector_property<uint2>(*material, key, size, value_element.value());
else if (size == 3)
load_vector_property<uint3>(*material, key, size, value_element.value());
else if (size == 4)
load_vector_property<uint4>(*material, key, size, value_element.value());
}
else if (type.find("int") != std::string::npos)
{
if (size == 2)
load_vector_property<int2>(*material, key, size, value_element.value());
else if (size == 3)
load_vector_property<int3>(*material, key, size, value_element.value());
else if (size == 4)
load_vector_property<int4>(*material, key, size, value_element.value());
}
else if (type.find("bool") != std::string::npos)
{
if (size == 2)
load_vector_property<bool2>(*material, key, size, value_element.value());
else if (size == 3)
load_vector_property<bool3>(*material, key, size, value_element.value());
else if (size == 4)
load_vector_property<bool4>(*material, key, size, value_element.value());
}
}
// If variable type is a scalar
else
{
if (type.find("float") != std::string::npos)
load_scalar_property<float>(*material, key, value_element.value());
else if (type.find("uint") != std::string::npos)
load_scalar_property<unsigned int>(*material, key, value_element.value());
else if (type.find("int") != std::string::npos)
load_scalar_property<int>(*material, key, value_element.value());
else if (type.find("bool") != std::string::npos)
load_scalar_property<bool>(*material, key, value_element.value());
}
}
}
return disconnected_property_count;
return material;
}
} // namespace render

+ 106
- 159
src/engine/render/material.hpp View File

@ -20,15 +20,13 @@
#ifndef ANTKEEPER_RENDER_MATERIAL_HPP
#define ANTKEEPER_RENDER_MATERIAL_HPP
#include <engine/render/blend-mode.hpp>
#include <engine/render/material-property.hpp>
#include <engine/render/shadow-mode.hpp>
#include <engine/gl/shader-program.hpp>
#include <cstdint>
#include <cstddef>
#include <list>
#include <engine/gl/shader-template.hpp>
#include <engine/render/material-blend-mode.hpp>
#include <engine/render/material-variable.hpp>
#include <engine/render/material-shadow-mode.hpp>
#include <engine/utility/hash/fnv1a.hpp>
#include <unordered_map>
#include <string>
namespace render {
@ -39,29 +37,22 @@ class material
{
public:
/**
* Creates a material.
*
* @param program Shader program with which to associate this material.
*/
explicit material(gl::shader_program* program);
/**
* Creates a material.
* Constructs a material.
*/
material();
material() = default;
/**
* Creates a copy of another material.
* Constructs a copy of another material.
*
* @param other Material to copy.
*/
material(const material& other);
/**
* Destroys a material.
*/
~material();
~material() = default;
/**
* Makes this material a copy of aother material.
*
@ -70,26 +61,30 @@ public:
*/
material& operator=(const material& other);
/// @name Settings
/// @{
/**
* Sets state 0 = state 1 for each material property tween.
* Enables or disables back-face culling of the material surface.
*
* @param two_sided `true` to disable back-face culling, or `false` to enable it.
*/
void update_tweens();
void set_two_sided(bool two_sided) noexcept;
/**
* Uploads each material property to the material's shader program.
* Sets the material blend mode.
*
* @param a Interpolation factor. Should be on `[0.0, 1.0]`.
* @return Number of material property uploads which failed.
* @param mode Blend mode.
*/
std::size_t upload(double a) const;
void set_blend_mode(material_blend_mode mode) noexcept;
/**
* Sets the material's shader program and reconnects all shader properties to their corresponding shader inputs.
* Sets the material shadow mode.
*
* @param program Shader program with which to associate the material.
* @param mode Shadow mode.
*/
void set_shader_program(gl::shader_program* program);
void set_shadow_mode(material_shadow_mode mode) noexcept;
/**
* Sets the material flags.
*
@ -97,158 +92,110 @@ public:
*/
void set_flags(std::uint32_t flags) noexcept;
/// Returns `true` if the material surface is two-sided, and `false` otherwise.
[[nodiscard]] inline bool is_two_sided() const noexcept
{
return two_sided;
}
/// Returns the material blend mode.
[[nodiscard]] inline material_blend_mode get_blend_mode() const noexcept
{
return blend_mode;
}
/// Returns the material shadow mode.
[[nodiscard]] inline material_shadow_mode get_shadow_mode() const noexcept
{
return shadow_mode;
}
/// Returns the material flags.
[[nodiscard]] inline std::uint32_t get_flags() const noexcept
{
return flags;
}
/// @}
/// @name Shading
/// @{
/**
* Sets the material blend mode.
* Sets the material's shader template.
*
* @param mode Blend mode.
* @param shader_template Shader template with which to associate the material.
*/
void set_blend_mode(blend_mode mode) noexcept;
void set_shader_template(std::shared_ptr<gl::shader_template> shader_template);
/**
* Sets the opacity mask threshold value for masked blend mode.
*
* @param threshold Opacity mask threshold value, above which the surface is considered opaque.
*
* @see render::blend_mode::masked
* Returns the shader template with which this material is associated.
*/
void set_opacity_threshold(float threshold) noexcept;
[[nodiscard]] inline const std::shared_ptr<gl::shader_template>& get_shader_template() const noexcept
{
return shader_template;
}
/**
* Enables or disables back-face culling of the material surface.
* Sets the value of a material variable with the given name.
*
* @param two_sided `true` to disable back-face culling, or `false` to enable it.
* @param key 32-bit FNV-1a hash value of the variable name.
* @param value Shared pointer to the material variable value.
*/
void set_two_sided(bool two_sided) noexcept;
void set_variable(hash::fnv1a32_t key, std::shared_ptr<material_variable_base> value);
/**
* Sets the material shadow mode.
* Returns a shared pointer to the material variable with the given name, or `nullptr` if not found.
*
* @param mode Shadow mode.
*/
void set_shadow_mode(shadow_mode mode) noexcept;
/**
* Adds a material array property to the material.
* @param key 32-bit FNV-1a hash value of the variable name.
*
* @param name Name of the material array property.
* @param element_count Number of elements in the array.
* @return Pointer to the added material property.
* @return Shared pointer to the material variable with the given name, or `nullptr` if not found.
*/
template <typename T>
material_property<T>* add_property(const std::string& name, std::size_t element_count = 1);
[[nodiscard]] std::shared_ptr<material_variable_base> get_variable(hash::fnv1a32_t key) const;
/**
* Returns the shader program with which this material is associated.
* Returns all material variables.
*
* @return Map of 32-bit FNV-1a hash values of variable names to variables.
*/
gl::shader_program* get_shader_program() const;
/// Returns the material flags.
std::uint32_t get_flags() const noexcept;
/// Returns the material blend mode.
blend_mode get_blend_mode() const noexcept;
/// Returns the opacity mask threshold value.
float get_opacity_threshold() const noexcept;
[[nodiscard]] inline const std::unordered_map<hash::fnv1a32_t, std::shared_ptr<material_variable_base>>& get_variables() const noexcept
{
return variable_map;
}
/// Returns `true` if the material surface is two-sided, and `false` otherwise.
bool is_two_sided() const noexcept;
/// @}
/// Returns the material shadow mode.
shadow_mode get_shadow_mode() const noexcept;
/**
* Returns the material property with the specified name, or `nullptr` if the material could not be found.
*/
material_property_base* get_property(const std::string& name) const;
/**
* Returns a list of all material properties in the material.
* Returns a hash of the material state.
*
* The followings functions may change the material hash:
*
* * material::set_shader_template
* * material::set_flags
* * material::set_blend_mode
* * material::set_two_sided
* * material::set_shadow_mode
*/
const std::list<material_property_base*>* get_properties() const;
[[nodiscard]] inline std::size_t hash() const noexcept
{
return m_hash;
}
private:
/**
* Attempts to reconnect all material properties to their corresponding shader inputs.
*
* @return Number of disconnected properties.
* Recalculates the material state hash.
*/
std::size_t reconnect_properties();
gl::shader_program* program;
std::uint32_t flags;
blend_mode blend_mode;
float opacity_threshold;
bool two_sided;
shadow_mode shadow_mode;
std::list<material_property_base*> properties;
std::unordered_map<std::string, material_property_base*> property_map;
void rehash() noexcept;
bool two_sided{false};
material_blend_mode blend_mode{material_blend_mode::opaque};
material_shadow_mode shadow_mode{material_shadow_mode::opaque};
std::uint32_t flags{0};
std::shared_ptr<gl::shader_template> shader_template;
std::unordered_map<hash::fnv1a32_t, std::shared_ptr<material_variable_base>> variable_map;
std::size_t m_hash{0};
};
template <typename T>
material_property<T>* material::add_property(const std::string& name, std::size_t element_count)
{
// Allocate property
material_property<T>* property = new material_property<T>(element_count);
// Add to property list and map
properties.push_back(property);
property_map[name] = property;
// Attempt to connect property to its corresponding shader input
if (program)
{
property->connect(program->get_input(name));
}
return property;
}
inline gl::shader_program* material::get_shader_program() const
{
return program;
}
inline std::uint32_t material::get_flags() const noexcept
{
return flags;
}
inline blend_mode material::get_blend_mode() const noexcept
{
return blend_mode;
}
inline float material::get_opacity_threshold() const noexcept
{
return opacity_threshold;
}
inline bool material::is_two_sided() const noexcept
{
return two_sided;
}
inline shadow_mode material::get_shadow_mode() const noexcept
{
return shadow_mode;
}
inline material_property_base* material::get_property(const std::string& name) const
{
if (auto it = property_map.find(name); it != property_map.end())
{
return it->second;
}
return nullptr;
}
inline const std::list<material_property_base*>* material::get_properties() const
{
return &properties;
}
} // namespace render
#endif // ANTKEEPER_RENDER_MATERIAL_HPP

+ 295
- 76
src/engine/render/model.cpp View File

@ -18,103 +18,322 @@
*/
#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/math/numbers.hpp>
#include <engine/utility/hash/fnv1a.hpp>
#include <cstdint>
namespace render {
model::model():
bounds({0, 0, 0}, {0, 0, 0})
{}
model::~model()
model::model()
{
for (model_group* group: groups)
{
delete group;
}
vertex_array = std::make_shared<gl::vertex_array>();
vertex_buffer = std::make_shared<gl::vertex_buffer>();
}
model_group* model::add_group(const std::string& name)
} // 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_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)
{
if (!name.empty())
// Read vertex format
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)
std::uint8_t bones_per_vertex = 0;
if (vertex_format_flags & vertex_attribute_bone)
{
if (auto it = group_map.find(name); it != group_map.end())
{
return it->second;
}
ctx.read8(reinterpret_cast<std::byte*>(&bones_per_vertex), 1);
}
model_group* group = new model_group();
group->index = groups.size();
group->name = name;
group->material = nullptr;
group->drawing_mode = gl::drawing_mode::triangles;
group->start_index = 0;
group->index_count = 0;
groups.push_back(group);
if (!name.empty())
// Read vertex count
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;
if (vertex_format_flags & vertex_attribute_position)
{
group_map[name] = group;
vertex_size += sizeof(float) * 3;
}
return group;
}
bool model::remove_group(const std::string& name)
{
if (auto it = group_map.find(name); it != group_map.end())
if (vertex_format_flags & vertex_attribute_uv)
{
return remove_group(it->second);
vertex_size += sizeof(float) * 2;
}
return false;
}
bool model::remove_group(model_group* group)
{
// Remove from group map
if (!group->name.empty())
if (vertex_format_flags & vertex_attribute_normal)
{
vertex_size += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_tangent)
{
vertex_size += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_color)
{
vertex_size += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_bone)
{
vertex_size += sizeof(std::uint32_t) * bones_per_vertex;
vertex_size += sizeof(float) * bones_per_vertex;
}
if (vertex_format_flags & vertex_attribute_barycentric)
{
vertex_size += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_morph_target)
{
vertex_size += sizeof(float) * 3;
}
// Allocate vertex data
std::vector<std::byte> vertex_data(vertex_count * vertex_size);
// Read vertices
if constexpr (std::endian::native == std::endian::little)
{
if (auto it = group_map.find(group->name); it != group_map.end())
ctx.read8(vertex_data.data(), vertex_count * vertex_size);
}
else
{
std::byte* vertex_data_offset = vertex_data.data();
for (std::uint32_t i = 0; i < vertex_count; ++i)
{
group_map.erase(it);
if (vertex_format_flags & vertex_attribute_position)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_uv)
{
ctx.read32<std::endian::little>(vertex_data_offset, 2);
vertex_data_offset += sizeof(float) * 2;
}
if (vertex_format_flags & vertex_attribute_normal)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_tangent)
{
ctx.read32<std::endian::little>(vertex_data_offset, 4);
vertex_data_offset += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_color)
{
ctx.read32<std::endian::little>(vertex_data_offset, 4);
vertex_data_offset += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_bone)
{
ctx.read32<std::endian::little>(vertex_data_offset, bones_per_vertex);
ctx.read32<std::endian::little>(vertex_data_offset, bones_per_vertex);
vertex_data_offset += sizeof(std::uint32_t) * bones_per_vertex;
vertex_data_offset += sizeof(float) * bones_per_vertex;
}
if (vertex_format_flags & vertex_attribute_barycentric)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_morph_target)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
}
}
// Adjust indices of groups after this group
for (std::size_t i = group->index + 1; i < groups.size(); ++i)
// 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);
// 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;
if (vertex_format_flags & vertex_attribute_position)
{
--groups[i]->index;
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::position, attribute);
attribute.offset += sizeof(float) * attribute.components;
}
// Remove from groups
groups.erase(groups.begin() + group->index);
// Deallocate group
delete group;
return true;
}
const model_group* model::get_group(const std::string& name) const
{
if (auto it = group_map.find(name); it != group_map.end())
if (vertex_format_flags & vertex_attribute_uv)
{
return it->second;
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 2;
vao.bind(render::vertex_attribute::uv, attribute);
attribute.offset += sizeof(float) * attribute.components;
}
return nullptr;
}
model_group* model::get_group(const std::string& name)
{
if (auto it = group_map.find(name); it != group_map.end())
if (vertex_format_flags & vertex_attribute_normal)
{
return it->second;
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::normal, attribute);
attribute.offset += sizeof(float) * attribute.components;
}
return nullptr;
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;
}
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;
}
if (vertex_format_flags & vertex_attribute_bone)
{
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::uint32_t) * attribute.components;
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;
}
if (vertex_format_flags & vertex_attribute_barycentric)
{
attribute.type = gl::vertex_attribute_type::float_32;
attribute.components = 3;
vao.bind(render::vertex_attribute::barycentric, attribute);
attribute.offset += sizeof(float) * attribute.components;
}
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;
}
// Read model bounds
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(model->get_bounds().min_point.data()), 3);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(model->get_bounds().max_point.data()), 3);
// Read material count
std::uint16_t material_count = 0;
ctx.read16<std::endian::little>(reinterpret_cast<std::byte*>(&material_count), 1);
// Allocate material groups
model->get_groups().resize(material_count);
// Read materials
for (auto& group: model->get_groups())
{
// Read material name length
std::uint8_t material_name_length = 0;
ctx.read8(reinterpret_cast<std::byte*>(&material_name_length), 1);
// Read material name
std::string material_name(static_cast<std::size_t>(material_name_length), '\0');
ctx.read8(reinterpret_cast<std::byte*>(material_name.data()), material_name_length);
// 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;
// Read offset to index of first vertex
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&group.start_index), 1);
// Read vertex count
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&group.index_count), 1);
// Slugify material filename
std::string material_filename = material_name + ".mtl";
std::replace(material_filename.begin(), material_filename.end(), '_', '-');
// Load group material
group.material = resource_manager.load<render::material>(material_filename);
}
// Read skeleton
if (vertex_format_flags & vertex_attribute_bone)
{
::skeleton& skeleton = model->get_skeleton();
pose& bind_pose = skeleton.bind_pose;
// Read bone count
std::uint16_t bone_count = 0;
ctx.read16<std::endian::little>(reinterpret_cast<std::byte*>(&bone_count), 1);
// Read bones
for (std::uint16_t i = 0; i < bone_count; ++i)
{
// Read bone name length
std::uint8_t bone_name_length = 0;
ctx.read8(reinterpret_cast<std::byte*>(&bone_name_length), 1);
// Read and hash bone name
std::string bone_name(static_cast<std::size_t>(bone_name_length), '\0');
ctx.read8(reinterpret_cast<std::byte*>(bone_name.data()), bone_name_length);
hash::fnv1a32_t bone_key = hash::fnv1a32<char>(bone_name);
// Read parent bone index
std::uint16_t parent_bone_index = i;
ctx.read16<std::endian::little>(reinterpret_cast<std::byte*>(&parent_bone_index), 1);
// Construct bone identifier
::bone bone = make_bone(i, parent_bone_index);
// Add bone to bone map
skeleton.bone_map[bone_key] = bone;
// Get reference to the bone's bind pose transform
auto& bone_transform = bind_pose[bone];
// Read bone translation
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(bone_transform.translation.data()), 3);
// Read bone rotation
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&bone_transform.rotation.r), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(bone_transform.rotation.i.data()), 3);
// Set bone scale
bone_transform.scale = {1, 1, 1};
// Read bone length
float bone_length = 0.0f;
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&bone_length), 1);
}
// Calculate inverse skeleton-space bind pose
::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose);
::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose);
}
return model;
}
} // namespace render

+ 87
- 154
src/engine/render/model.hpp View File

@ -21,12 +21,14 @@
#define ANTKEEPER_RENDER_MODEL_HPP
#include <engine/animation/skeleton.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/render/material.hpp>
#include <engine/geom/aabb.hpp>
#include <unordered_map>
#include <engine/utility/hash/fnv1a.hpp>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
@ -35,176 +37,107 @@ namespace render {
/**
* Part of a model which is associated with exactly one material.
*/
class model_group
struct model_group
{
public:
void set_material(material* material);
void set_drawing_mode(gl::drawing_mode mode);
void set_start_index(std::size_t index);
void set_index_count(std::size_t count);
std::size_t get_index() const;
const std::string& get_name() const;
const material* get_material() const;
material* get_material();
gl::drawing_mode get_drawing_mode() const;
std::size_t get_start_index() const;
std::size_t get_index_count() const;
private:
friend class model;
std::size_t index;
std::string name;
material* material;
hash::fnv1a32_t id;
gl::drawing_mode drawing_mode;
std::size_t start_index;
std::size_t index_count;
std::uint32_t start_index;
std::uint32_t index_count;
std::shared_ptr<material> material;
};
inline void model_group::set_material(render::material* material)
{
this->material = material;
}
inline void model_group::set_drawing_mode(gl::drawing_mode mode)
{
this->drawing_mode = mode;
}
inline void model_group::set_start_index(std::size_t index)
{
this->start_index = index;
}
inline void model_group::set_index_count(std::size_t count)
{
this->index_count = count;
}
inline std::size_t model_group::get_index() const
{
return index;
}
inline const std::string& model_group::get_name() const
{
return name;
}
inline const material* model_group::get_material() const
{
return material;
}
inline material* model_group::get_material()
{
return material;
}
inline gl::drawing_mode model_group::get_drawing_mode() const
{
return drawing_mode;
}
inline std::size_t model_group::get_start_index() const
{
return start_index;
}
inline std::size_t model_group::get_index_count() const
{
return index_count;
}
/**
*
*
*/
class model
{
public:
/// AABB type.
typedef geom::aabb<float> aabb_type;
/**
* Constructs a model.
*/
model();
~model();
void set_bounds(const aabb_type& bounds);
model_group* add_group(const std::string& name = std::string());
bool remove_group(const std::string& name);
bool remove_group(model_group* group);
/**
* Returns the vertex array associated with this model.
*/
/// @{
[[nodiscard]] inline const std::shared_ptr<gl::vertex_array>& get_vertex_array() const noexcept
{
return vertex_array;
}
[[nodiscard]] inline std::shared_ptr<gl::vertex_array>& get_vertex_array() noexcept
{
return vertex_array;
}
/// @}
const aabb_type& get_bounds() const;
const model_group* get_group(const std::string& name) const;
model_group* get_group(const std::string& name);
const std::vector<model_group*>* get_groups() const;
const gl::vertex_array* get_vertex_array() const;
gl::vertex_array* get_vertex_array();
const gl::vertex_buffer* get_vertex_buffer() const;
gl::vertex_buffer* get_vertex_buffer();
/**
* Returns the vertex buffer associated with this model.
*/
/// @{
[[nodiscard]] inline const std::shared_ptr<gl::vertex_buffer>& get_vertex_buffer() const noexcept
{
return vertex_buffer;
}
[[nodiscard]] inline std::shared_ptr<gl::vertex_buffer>& get_vertex_buffer() noexcept
{
return vertex_buffer;
}
/// @}
/**
* Returns the bounds of the model.
*/
/// @{
[[nodiscard]] inline const aabb_type& get_bounds() const noexcept
{
return bounds;
}
[[nodiscard]] inline aabb_type& get_bounds() noexcept
{
return bounds;
}
/// @}
/**
* Returns the model's model groups.
*/
/// @{
[[nodiscard]] inline const std::vector<model_group>& get_groups() const noexcept
{
return groups;
}
[[nodiscard]] inline std::vector<model_group>& get_groups() noexcept
{
return groups;
}
/// @}
/**
* Returns the skeleton associated with this model.
*/
/// @{
[[nodiscard]] inline const ::skeleton& get_skeleton() const noexcept
{
return skeleton;
}
[[nodiscard]] inline ::skeleton& get_skeleton() noexcept
{
return skeleton;
}
/// @}
const skeleton& get_skeleton() const;
skeleton& get_skeleton();
private:
aabb_type bounds;
std::vector<model_group*> groups;
std::unordered_map<std::string, model_group*> group_map;
gl::vertex_array vao;
gl::vertex_buffer vbo;
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;
};
inline void model::set_bounds(const aabb_type& bounds)
{
this->bounds = bounds;
}
inline const typename model::aabb_type& model::get_bounds() const
{
return bounds;
}
inline const std::vector<model_group*>* model::get_groups() const
{
return &groups;
}
inline const gl::vertex_array* model::get_vertex_array() const
{
return &vao;
}
inline gl::vertex_array* model::get_vertex_array()
{
return &vao;
}
inline const gl::vertex_buffer* model::get_vertex_buffer() const
{
return &vbo;
}
inline gl::vertex_buffer* model::get_vertex_buffer()
{
return &vbo;
}
inline const skeleton& model::get_skeleton() const
{
return skeleton;
}
inline skeleton& model::get_skeleton()
{
return skeleton;
}
} // namespace render
#endif // ANTKEEPER_RENDER_MODEL_HPP

+ 1
- 1
src/engine/render/pass.hpp View File

@ -36,7 +36,7 @@ public:
pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer);
virtual ~pass();
virtual void render(const render::context& ctx, render::queue& queue) const = 0;
virtual void render(const render::context& ctx, render::queue& queue) = 0;
void set_enabled(bool enabled);
bool is_enabled() const;

+ 146
- 103
src/engine/render/passes/bloom-pass.cpp View File

@ -22,7 +22,7 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.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>
@ -46,7 +46,7 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma
corrected_filter_radius{filter_radius, filter_radius}
{
// Load downsample shader template
downsample_shader_template = resource_manager->load<shader_template>("bloom-downsample.glsl");
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
@ -55,40 +55,37 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma
{"KARIS_AVERAGE", std::string()}
}
);
downsample_karis_source_texture_input = downsample_karis_shader->get_input("source_texture");
// Build downsample shader program without Karis averaging
downsample_shader = downsample_shader_template->build();
downsample_source_texture_input = downsample_shader->get_input("source_texture");
// Load upsample shader template
upsample_shader_template = resource_manager->load<shader_template>("bloom-upsample.glsl");
auto upsample_shader_template = resource_manager->load<gl::shader_template>("bloom-upsample.glsl");
// Build upsample shader program
upsample_shader = upsample_shader_template->build();
upsample_source_texture_input = upsample_shader->get_input("source_texture");
upsample_filter_radius_input = upsample_shader->get_input("filter_radius");
const float vertex_data[] =
const float2 vertex_positions[] =
{
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
{-1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, 1.0f},
{ 1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, -1.0f}
};
const auto vertex_data = std::as_bytes(std::span{vertex_positions});
std::size_t vertex_size = 2;
std::size_t vertex_stride = sizeof(float) * vertex_size;
std::size_t vertex_count = 6;
quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data);
quad_vao = new gl::vertex_array();
quad_vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data);
quad_vao = std::make_unique<gl::vertex_array>();
// Define position vertex attribute
gl::vertex_attribute position_attribute;
position_attribute.buffer = quad_vbo;
position_attribute.buffer = quad_vbo.get();
position_attribute.offset = 0;
position_attribute.stride = vertex_stride;
position_attribute.type = gl::vertex_attribute_type::float_32;
@ -98,79 +95,12 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma
quad_vao->bind(render::vertex_attribute::position, position_attribute);
}
bloom_pass::~bloom_pass()
void bloom_pass::render(const render::context& ctx, render::queue& queue)
{
delete quad_vao;
delete quad_vbo;
set_mip_chain_length(0);
delete downsample_karis_shader;
delete downsample_shader;
delete upsample_shader;
/// @TODO
//resource_manager->unload("bloom-downsample.glsl");
//resource_manager->unload("bloom-upsample.glsl");
}
void bloom_pass::render(const render::context& ctx, render::queue& queue) const
{
if (!source_texture || !mip_chain_length)
return;
// Disable depth testing
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
// Enable back-face culling
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Disable blending
glDisable(GL_BLEND);
// Downsample first mip with Karis average
{
rasterizer->use_program(*downsample_karis_shader);
downsample_karis_source_texture_input->upload(source_texture);
rasterizer->use_framebuffer(*framebuffers[0]);
rasterizer->set_viewport(0, 0, textures[0]->get_width(), textures[0]->get_height());
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
// Downsample remaining mips
rasterizer->use_program(*downsample_shader);
for (int i = 1; i < static_cast<int>(mip_chain_length); ++i)
// Execute command buffer
for (const auto& command: command_buffer)
{
rasterizer->use_framebuffer(*framebuffers[i]);
rasterizer->set_viewport(0, 0, textures[i]->get_width(), textures[i]->get_height());
// Use previous downsample texture as downsample source
downsample_source_texture_input->upload(textures[i - 1]);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
// Enable additive blending
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
// Upsample
rasterizer->use_program(*upsample_shader);
upsample_filter_radius_input->upload(corrected_filter_radius);
for (int i = static_cast<int>(mip_chain_length) - 1; i > 0; --i)
{
const int j = i - 1;
rasterizer->use_framebuffer(*framebuffers[j]);
rasterizer->set_viewport(0, 0, textures[j]->get_width(), textures[j]->get_height());
upsample_source_texture_input->upload(textures[i]);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
command();
}
}
@ -226,11 +156,13 @@ void bloom_pass::set_source_texture(const gl::texture_2d* texture)
{
source_texture = texture;
resize();
rebuild_command_buffer();
}
}
else
{
source_texture = texture;
source_texture = nullptr;
rebuild_command_buffer();
}
}
}
@ -256,33 +188,30 @@ void bloom_pass::set_mip_chain_length(unsigned int length)
unsigned int mip_height = std::max<unsigned int>(1, source_height >> (i + 1));
// Generate mip texture
gl::texture_2d* texture = new gl::texture_2d(mip_width, mip_height, gl::pixel_type::float_16, gl::pixel_format::rgb);
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);
textures.push_back(texture);
// Generate mip framebuffer
gl::framebuffer* framebuffer = new gl::framebuffer(mip_width, mip_height);
framebuffer->attach(gl::framebuffer_attachment_type::color, texture);
framebuffers.push_back(framebuffer);
auto framebuffer = std::make_unique<gl::framebuffer>(mip_width, mip_height);
framebuffer->attach(gl::framebuffer_attachment_type::color, texture.get());
textures.push_back(std::move(texture));
framebuffers.emplace_back(std::move(framebuffer));
}
}
else if (length < mip_chain_length)
{
// Free excess framebuffers
while (framebuffers.size() > length)
{
delete framebuffers.back();
framebuffers.pop_back();
delete textures.back();
textures.pop_back();
}
framebuffers.resize(length);
textures.resize(length);
}
// Update mip chain length
mip_chain_length = length;
// Rebuild command buffer
rebuild_command_buffer();
}
void bloom_pass::set_filter_radius(float radius)
@ -300,4 +229,118 @@ void bloom_pass::set_filter_radius(float radius)
corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius};
}
void bloom_pass::rebuild_command_buffer()
{
command_buffer.clear();
if (!source_texture ||
!mip_chain_length ||
!downsample_karis_shader ||
!downsample_shader ||
!upsample_shader)
{
return;
}
// Setup downsample state
command_buffer.emplace_back
(
[]()
{
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
}
);
// Downsample first mip with Karis average
if (auto source_texture_var = downsample_karis_shader->variable("source_texture"))
{
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());
source_texture_var->update(*source_texture);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
);
}
// Downsample remaining mips
if (mip_chain_length > 1)
{
if (auto source_texture_var = downsample_shader->variable("source_texture"))
{
command_buffer.emplace_back([&](){rasterizer->use_program(*downsample_shader);});
for (int i = 1; i < static_cast<int>(mip_chain_length); ++i)
{
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());
// Use previous downsample texture as downsample source
source_texture_var->update(*textures[i - 1]);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
);
}
}
}
// Setup upsample state
command_buffer.emplace_back
(
[&]()
{
// Enable additive blending
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
// Bind upsample shader
rasterizer->use_program(*upsample_shader);
}
);
// Update upsample filter radius
if (auto filter_radius_var = upsample_shader->variable("filter_radius"))
{
command_buffer.emplace_back([&, filter_radius_var](){filter_radius_var->update(corrected_filter_radius);});
}
// Upsample
if (auto source_texture_var = upsample_shader->variable("source_texture"))
{
for (int i = static_cast<int>(mip_chain_length) - 1; i > 0; --i)
{
const int j = i - 1;
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());
source_texture_var->update(*textures[i]);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
);
}
}
}
} // namespace render

+ 17
- 25
src/engine/render/passes/bloom-pass.hpp View File

@ -21,12 +21,14 @@
#define ANTKEEPER_RENDER_BLOOM_PASS_HPP
#include <engine/render/pass.hpp>
#include <engine/render/shader-template.hpp>
#include <engine/gl/shader-template.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#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 <memory>
#include <functional>
class resource_manager;
@ -49,18 +51,13 @@ public:
*/
bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager);
/**
* Destructs a bloom pass.
*/
virtual ~bloom_pass();
/**
* Renders a bloom texture.
*
* @param ctx Render context.
* @param queue Render queue.
*/
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
/**
* Resizes the mip chain resolution according to the resolution of the source texture.
@ -94,34 +91,29 @@ public:
const gl::texture_2d* get_bloom_texture() const;
private:
const gl::texture_2d* source_texture;
void rebuild_command_buffer();
shader_template* downsample_shader_template;
shader_template* upsample_shader_template;
gl::shader_program* downsample_karis_shader;
const gl::shader_input* downsample_karis_source_texture_input;
gl::shader_program* downsample_shader;
const gl::shader_input* downsample_source_texture_input;
const gl::texture_2d* source_texture;
gl::shader_program* upsample_shader;
const gl::shader_input* upsample_source_texture_input;
const gl::shader_input* upsample_filter_radius_input;
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;
gl::vertex_buffer* quad_vbo;
gl::vertex_array* quad_vao;
std::unique_ptr<gl::vertex_buffer> quad_vbo;
std::unique_ptr<gl::vertex_array> quad_vao;
unsigned int mip_chain_length;
std::vector<gl::framebuffer*> framebuffers;
std::vector<gl::texture_2d*> textures;
std::vector<std::unique_ptr<gl::framebuffer>> framebuffers;
std::vector<std::unique_ptr<gl::texture_2d>> textures;
float filter_radius;
float2 corrected_filter_radius;
std::vector<std::function<void()>> command_buffer;
};
inline const gl::texture_2d* bloom_pass::get_bloom_texture() const
{
return textures.empty() ? nullptr : textures.front();
return textures.empty() ? nullptr : textures.front().get();
}
} // namespace render

+ 78
- 20
src/engine/render/passes/clear-pass.cpp View File

@ -34,27 +34,12 @@ clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb
clear_stencil(0)
{}
clear_pass::~clear_pass()
{}
void clear_pass::render(const render::context& ctx, render::queue& queue) const
void clear_pass::render(const render::context& ctx, render::queue& queue)
{
if (clear_color_buffer)
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
if (clear_depth_buffer)
glDepthMask(GL_TRUE);
if (clear_stencil_buffer)
glStencilMask(0xFF);
rasterizer->use_framebuffer(*framebuffer);
auto viewport = framebuffer->get_dimensions();
rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport));
rasterizer->set_clear_color(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
rasterizer->set_clear_depth(clear_depth);
rasterizer->set_clear_stencil(clear_stencil);
rasterizer->clear_framebuffer(clear_color_buffer, clear_depth_buffer, clear_stencil_buffer);
for (const auto& command: command_buffer)
{
command();
}
}
void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil)
@ -62,6 +47,8 @@ void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil)
clear_color_buffer = color;
clear_depth_buffer = depth;
clear_stencil_buffer = stencil;
rebuild_command_buffer();
}
void clear_pass::set_clear_color(const float4& color)
@ -79,4 +66,75 @@ void clear_pass::set_clear_stencil(int stencil)
clear_stencil = stencil;
}
void clear_pass::rebuild_command_buffer()
{
command_buffer.clear();
if (!clear_color_buffer &&
!clear_depth_buffer &&
!clear_stencil_buffer)
{
return;
}
command_buffer.emplace_back
(
[&]()
{
rasterizer->use_framebuffer(*framebuffer);
auto viewport = framebuffer->get_dimensions();
rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport));
}
);
if (clear_color_buffer)
{
// Update color buffer clear state
command_buffer.emplace_back
(
[&]()
{
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
rasterizer->set_clear_color(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
}
);
}
if (clear_depth_buffer)
{
// Update depth buffer clear state
command_buffer.emplace_back
(
[&]()
{
glDepthMask(GL_TRUE);
rasterizer->set_clear_depth(clear_depth);
}
);
}
if (clear_stencil_buffer)
{
// Update stencil buffer clear state
command_buffer.emplace_back
(
[&]()
{
glStencilMask(0xFF);
rasterizer->set_clear_stencil(clear_stencil);
}
);
}
// Clear buffers
command_buffer.emplace_back
(
[&]()
{
rasterizer->clear_framebuffer(clear_color_buffer, clear_depth_buffer, clear_stencil_buffer);
}
);
}
} // namespace render

+ 7
- 3
src/engine/render/passes/clear-pass.hpp View File

@ -22,6 +22,7 @@
#include <engine/render/pass.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <functional>
namespace render {
@ -32,8 +33,8 @@ class clear_pass: public pass
{
public:
clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer);
virtual ~clear_pass();
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
/**
* Sets the buffers to be cleared.
@ -66,15 +67,18 @@ public:
void set_clear_stencil(int stencil);
private:
void rebuild_command_buffer();
bool clear_color_buffer;
bool clear_depth_buffer;
bool clear_stencil_buffer;
float4 clear_color;
float clear_depth;
int clear_stencil;
std::vector<std::function<void()>> command_buffer;
};
} // namespace render
#endif // ANTKEEPER_RENDER_CLEAR_PASS_HPP

+ 104
- 68
src/engine/render/passes/final-pass.cpp View File

@ -22,7 +22,7 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.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>
@ -34,6 +34,7 @@
#include <engine/render/context.hpp>
#include <cmath>
#include <glad/glad.h>
#include <engine/utility/hash/fnv1a.hpp>
namespace render {
@ -45,39 +46,31 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb
blue_noise_texture(nullptr),
blue_noise_scale(1.0f)
{
// Load shader template
shader_template = resource_manager->load<render::shader_template>("final.glsl");
// Build shader program
// Load shader template and build shader program
auto shader_template = resource_manager->load<gl::shader_template>("final.glsl");
shader_program = shader_template->build();
color_texture_input = shader_program->get_input("color_texture");
bloom_texture_input = shader_program->get_input("bloom_texture");
bloom_weight_input = shader_program->get_input("bloom_weight");
blue_noise_texture_input = shader_program->get_input("blue_noise_texture");
blue_noise_scale_input = shader_program->get_input("blue_noise_scale");
resolution_input = shader_program->get_input("resolution");
time_input = shader_program->get_input("time");
const float vertex_data[] =
const float2 vertex_positions[] =
{
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
{-1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, 1.0f},
{ 1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, -1.0f}
};
const auto vertex_data = std::as_bytes(std::span{vertex_positions});
std::size_t vertex_size = 2;
std::size_t vertex_stride = sizeof(float) * vertex_size;
std::size_t vertex_count = 6;
quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data);
quad_vao = new gl::vertex_array();
quad_vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data);
quad_vao = std::make_unique<gl::vertex_array>();
// Define position vertex attribute
gl::vertex_attribute position_attribute;
position_attribute.buffer = quad_vbo;
position_attribute.buffer = quad_vbo.get();
position_attribute.offset = 0;
position_attribute.stride = vertex_stride;
position_attribute.type = gl::vertex_attribute_type::float_32;
@ -87,62 +80,34 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb
quad_vao->bind(render::vertex_attribute::position, position_attribute);
}
final_pass::~final_pass()
{
delete quad_vao;
delete quad_vbo;
delete shader_program;
/// @TODO
// resource_manager->unload("final.glsl");
}
void final_pass::render(const render::context& ctx, render::queue& queue) const
void final_pass::render(const render::context& ctx, render::queue& queue)
{
rasterizer->use_framebuffer(*framebuffer);
// Update resolution
const auto viewport_size = framebuffer->get_dimensions();
resolution = {static_cast<float>(std::get<0>(viewport_size)), static_cast<float>(std::get<1>(viewport_size))};
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
auto viewport = framebuffer->get_dimensions();
rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport));
// Update time
time = ctx.t;
float2 resolution = {std::get<0>(viewport), std::get<1>(viewport)};
// Change shader program
rasterizer->use_program(*shader_program);
// Upload shader parameters
color_texture_input->upload(color_texture);
if (bloom_texture && bloom_texture_input)
bloom_texture_input->upload(bloom_texture);
if (bloom_weight_input)
bloom_weight_input->upload(bloom_weight);
if (blue_noise_texture && blue_noise_texture_input)
blue_noise_texture_input->upload(blue_noise_texture);
if (blue_noise_scale_input)
blue_noise_scale_input->upload(blue_noise_scale);
if (resolution_input)
resolution_input->upload(resolution);
if (time_input)
time_input->upload(ctx.t);
// Draw quad
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
// Execute render commands
for (const auto& command: command_buffer)
{
command();
}
}
void final_pass::set_color_texture(const gl::texture_2d* texture)
{
this->color_texture = texture;
rebuild_command_buffer();
}
void final_pass::set_bloom_texture(const gl::texture_2d* texture) noexcept
{
this->bloom_texture = texture;
rebuild_command_buffer();
}
void final_pass::set_bloom_weight(float weight) noexcept
@ -150,10 +115,81 @@ void final_pass::set_bloom_weight(float weight) noexcept
this->bloom_weight = weight;
}
void final_pass::set_blue_noise_texture(const gl::texture_2d* texture)
void final_pass::set_blue_noise_texture(std::shared_ptr<gl::texture_2d> texture)
{
this->blue_noise_texture = texture;
blue_noise_scale = 1.0f / static_cast<float>(texture->get_dimensions()[0]);
rebuild_command_buffer();
}
void final_pass::rebuild_command_buffer()
{
command_buffer.clear();
command_buffer.emplace_back
(
[&]()
{
rasterizer->use_framebuffer(*framebuffer);
rasterizer->set_viewport(0, 0, static_cast<int>(resolution.x()), static_cast<int>(resolution.y()));
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
rasterizer->use_program(*shader_program);
}
);
if (color_texture)
{
if (const auto var = shader_program->variable("color_texture"))
{
command_buffer.emplace_back([&, var](){var->update(*color_texture);});
}
}
if (bloom_texture)
{
if (const auto var = shader_program->variable("bloom_texture"))
{
command_buffer.emplace_back([&, var](){var->update(*bloom_texture);});
}
}
if (blue_noise_texture)
{
if (const auto var = shader_program->variable("blue_noise_texture"))
{
command_buffer.emplace_back([&, var](){var->update(*blue_noise_texture);});
}
}
if (const auto var = shader_program->variable("bloom_weight"))
{
command_buffer.emplace_back([&, var](){var->update(bloom_weight);});
}
if (const auto var = shader_program->variable("blue_noise_scale"))
{
command_buffer.emplace_back([&, var](){var->update(blue_noise_scale);});
}
if (const auto var = shader_program->variable("resolution"))
{
command_buffer.emplace_back([&, var](){var->update(resolution);});
}
if (const auto var = shader_program->variable("time"))
{
command_buffer.emplace_back([&, var](){var->update(time);});
}
command_buffer.emplace_back
(
[&]()
{
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
);
}
} // namespace render

+ 15
- 17
src/engine/render/passes/final-pass.hpp View File

@ -21,12 +21,14 @@
#define ANTKEEPER_RENDER_FINAL_PASS_HPP
#include <engine/render/pass.hpp>
#include <engine/render/shader-template.hpp>
#include <engine/gl/shader-template.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#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 <functional>
#include <memory>
class resource_manager;
@ -39,33 +41,29 @@ class final_pass: public pass
{
public:
final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~final_pass();
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
void set_color_texture(const gl::texture_2d* texture);
void set_bloom_texture(const gl::texture_2d* texture) noexcept;
void set_bloom_weight(float weight) noexcept;
void set_blue_noise_texture(const gl::texture_2d* texture);
void set_blue_noise_texture(std::shared_ptr<gl::texture_2d> texture);
private:
render::shader_template* shader_template;
void rebuild_command_buffer();
gl::shader_program* shader_program;
const gl::shader_input* color_texture_input;
const gl::shader_input* bloom_texture_input;
const gl::shader_input* bloom_weight_input;
const gl::shader_input* blue_noise_texture_input;
const gl::shader_input* blue_noise_scale_input;
const gl::shader_input* resolution_input;
const gl::shader_input* time_input;
gl::vertex_buffer* quad_vbo;
gl::vertex_array* quad_vao;
std::unique_ptr<gl::shader_program> shader_program;
std::unique_ptr<gl::vertex_buffer> quad_vbo;
std::unique_ptr<gl::vertex_array> quad_vao;
const gl::texture_2d* color_texture;
const gl::texture_2d* bloom_texture;
float bloom_weight;
const gl::texture_2d* blue_noise_texture;
std::shared_ptr<gl::texture_2d> blue_noise_texture;
float blue_noise_scale;
float2 resolution;
float time;
std::vector<std::function<void()>> command_buffer;
};
} // namespace render

+ 71
- 48
src/engine/render/passes/fxaa-pass.cpp View File

@ -22,7 +22,7 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.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>
@ -36,37 +36,35 @@
namespace render {
fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager):
pass(rasterizer, framebuffer),
source_texture(nullptr)
pass(rasterizer, framebuffer)
{
// Load FXAA shader template
shader_template = resource_manager->load<render::shader_template>("fxaa.glsl");
auto shader_template = resource_manager->load<gl::shader_template>("fxaa.glsl");
// Build FXAA shader program
shader = shader_template->build();
source_texture_input = shader->get_input("source_texture");
texel_size_input = shader->get_input("texel_size");
const float vertex_data[] =
const float2 vertex_positions[] =
{
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
{-1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, 1.0f},
{ 1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, -1.0f}
};
const auto vertex_data = std::as_bytes(std::span{vertex_positions});
std::size_t vertex_size = 2;
std::size_t vertex_stride = sizeof(float) * vertex_size;
std::size_t vertex_count = 6;
quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data);
quad_vao = new gl::vertex_array();
quad_vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data);
quad_vao = std::make_unique<gl::vertex_array>();
// Define position vertex attribute
gl::vertex_attribute position_attribute;
position_attribute.buffer = quad_vbo;
position_attribute.buffer = quad_vbo.get();
position_attribute.offset = 0;
position_attribute.stride = vertex_stride;
position_attribute.type = gl::vertex_attribute_type::float_32;
@ -76,47 +74,72 @@ fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuf
quad_vao->bind(render::vertex_attribute::position, position_attribute);
}
fxaa_pass::~fxaa_pass()
void fxaa_pass::render(const render::context& ctx, render::queue& queue)
{
delete quad_vao;
delete quad_vbo;
delete shader;
/// @TODO
// resource_manager->unload("fxaa.glsl");
for (const auto& command: command_buffer)
{
command();
}
}
void fxaa_pass::render(const render::context& ctx, render::queue& queue) const
void fxaa_pass::set_source_texture(const gl::texture_2d* texture)
{
if (!source_texture)
return;
source_texture = texture;
rebuild_command_buffer();
}
void fxaa_pass::rebuild_command_buffer()
{
command_buffer.clear();
// Set rasterizer state
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
if (!source_texture || !shader)
{
return;
}
// Render FXAA
rasterizer->use_framebuffer(*framebuffer);
rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]);
rasterizer->use_program(*shader);
source_texture_input->upload(source_texture);
// Setup FXAA state
command_buffer.emplace_back
(
[&]()
{
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
// Render FXAA
rasterizer->use_framebuffer(*framebuffer);
rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]);
rasterizer->use_program(*shader);
}
);
if (texel_size_input)
// Update shader variables
if (auto source_texture_var = shader->variable("source_texture"))
{
command_buffer.emplace_back([&, source_texture_var](){source_texture_var->update(*source_texture);});
}
if (auto texel_size_var = shader->variable("texel_size"))
{
const float2 texel_size = 1.0f / float2{static_cast<float>(source_texture->get_width()), static_cast<float>(source_texture->get_height())};
texel_size_input->upload(texel_size);
command_buffer.emplace_back
(
[&, texel_size_var]()
{
const float2 texel_size = 1.0f / float2{static_cast<float>(source_texture->get_width()), static_cast<float>(source_texture->get_height())};
texel_size_var->update(texel_size);
}
);
}
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
void fxaa_pass::set_source_texture(const gl::texture_2d* texture)
{
source_texture = texture;
// Draw quad
command_buffer.emplace_back
(
[&]()
{
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
);
}
} // namespace render

+ 11
- 15
src/engine/render/passes/fxaa-pass.hpp View File

@ -21,12 +21,13 @@
#define ANTKEEPER_RENDER_FXAA_PASS_HPP
#include <engine/render/pass.hpp>
#include <engine/render/shader-template.hpp>
#include <engine/gl/shader-template.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#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 <functional>
class resource_manager;
@ -49,18 +50,13 @@ public:
*/
fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
/**
* Destructs an FXAA pass.
*/
virtual ~fxaa_pass();
/**
* Renders FXAA.
*
* @param ctx Render context.
* @param queue Render queue.
*/
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
/**
* Sets the FXAA source texture.
@ -70,15 +66,15 @@ public:
void set_source_texture(const gl::texture_2d* texture);
private:
const gl::texture_2d* source_texture;
void rebuild_command_buffer();
std::unique_ptr<gl::shader_program> shader;
std::unique_ptr<gl::vertex_buffer> quad_vbo;
std::unique_ptr<gl::vertex_array> quad_vao;
render::shader_template* shader_template;
gl::shader_program* shader;
const gl::shader_input* source_texture_input;
const gl::shader_input* texel_size_input;
const gl::texture_2d* source_texture{nullptr};
gl::vertex_buffer* quad_vbo;
gl::vertex_array* quad_vao;
std::vector<std::function<void()>> command_buffer;
};
} // namespace render

+ 33
- 34
src/engine/render/passes/ground-pass.cpp View File

@ -22,7 +22,7 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.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>
@ -58,8 +58,9 @@ ground_pass::ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fram
ground_pass::~ground_pass()
{}
void ground_pass::render(const render::context& ctx, render::queue& queue) const
void ground_pass::render(const render::context& ctx, render::queue& queue)
{
/*
if (!ground_model)
return;
@ -91,8 +92,6 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const
const float4x4& view_projection = ctx.view_projection;
float4x4 model_view_projection = projection * model_view;
float3 ambient_light_color = {0.0f, 0.0f, 0.0f};
float3 directional_light_color = {0.0f, 0.0f, 0.0f};
float3 directional_light_direction = {0.0f, 0.0f, 0.0f};
@ -140,41 +139,40 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const
// Draw ground
rasterizer->use_program(*shader_program);
if (model_view_projection_input)
model_view_projection_input->upload(model_view_projection);
if (view_projection_input)
view_projection_input->upload(view_projection);
if (camera_position_input)
camera_position_input->upload(ctx.camera_transform.translation);
if (directional_light_colors_input)
directional_light_colors_input->upload(0, &directional_light_color, 1);
if (directional_light_directions_input)
directional_light_directions_input->upload(0, &directional_light_direction, 1);
if (ambient_light_colors_input)
ambient_light_colors_input->upload(0, &ambient_light_color, 1);
ground_material->upload(ctx.alpha);
if (model_view_projection_var)
model_view_projection_var->update(model_view_projection);
if (view_projection_var)
view_projection_var->update(view_projection);
if (camera_position_var)
camera_position_var->update(ctx.camera_transform.translation);
if (directional_light_colors_var)
directional_light_colors_var->update(0, &directional_light_color, 1);
if (directional_light_directions_var)
directional_light_directions_var->update(0, &directional_light_direction, 1);
if (ambient_light_colors_var)
ambient_light_colors_var->update(0, &ambient_light_color, 1);
ground_material->update(ctx.alpha);
rasterizer->draw_arrays(*ground_model_vao, ground_model_drawing_mode, ground_model_start_index, ground_model_index_count);
*/
}
void ground_pass::set_ground_model(const model* model)
void ground_pass::set_ground_model(std::shared_ptr<render::model> model)
{
/*
ground_model = model;
if (ground_model)
{
ground_model_vao = model->get_vertex_array();
const std::vector<model_group*>& groups = *model->get_groups();
for (model_group* group: groups)
ground_model_vao = model->get_vertex_array().get();
for (const auto& group: model->get_groups())
{
ground_material = group->get_material();
ground_model_drawing_mode = group->get_drawing_mode();
ground_model_start_index = group->get_start_index();
ground_model_index_count = group->get_index_count();
ground_material = group.material;
ground_model_drawing_mode = group.drawing_mode;
ground_model_start_index = group.start_index;
ground_model_index_count = group.index_count;
}
if (ground_material)
@ -183,12 +181,12 @@ void ground_pass::set_ground_model(const model* model)
if (shader_program)
{
model_view_projection_input = shader_program->get_input("model_view_projection");
view_projection_input = shader_program->get_input("view_projection");
camera_position_input = shader_program->get_input("camera.position");
directional_light_colors_input = shader_program->get_input("directional_light_colors");
directional_light_directions_input = shader_program->get_input("directional_light_directions");
ambient_light_colors_input = shader_program->get_input("ambient_light_colors");
model_view_projection_var = shader_program->get_var("model_view_projection");
view_projection_var = shader_program->get_var("view_projection");
camera_position_var = shader_program->get_var("camera.position");
directional_light_colors_var = shader_program->get_var("directional_light_colors");
directional_light_directions_var = shader_program->get_var("directional_light_directions");
ambient_light_colors_var = shader_program->get_var("ambient_light_colors");
}
}
}
@ -196,6 +194,7 @@ void ground_pass::set_ground_model(const model* model)
{
ground_model_vao = nullptr;
}
*/
}
} // namespace render

+ 11
- 11
src/engine/render/passes/ground-pass.hpp View File

@ -24,7 +24,7 @@
#include <engine/utility/fundamental-types.hpp>
#include <engine/animation/tween.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/gl/shader-variable.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/drawing-mode.hpp>
@ -44,20 +44,20 @@ class ground_pass: public pass
public:
ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~ground_pass();
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
void set_ground_model(const model* model);
void set_ground_model(std::shared_ptr<render::model> model);
private:
gl::shader_program* shader_program;
const gl::shader_input* model_view_projection_input;
const gl::shader_input* view_projection_input;
const gl::shader_input* camera_position_input;
const gl::shader_input* directional_light_colors_input;
const gl::shader_input* directional_light_directions_input;
const gl::shader_input* ambient_light_colors_input;
std::shared_ptr<gl::shader_program> shader_program;
const gl::shader_variable* model_view_projection_var;
const gl::shader_variable* view_projection_var;
const gl::shader_variable* camera_position_var;
const gl::shader_variable* directional_light_colors_var;
const gl::shader_variable* directional_light_directions_var;
const gl::shader_variable* ambient_light_colors_var;
const model* ground_model;
std::shared_ptr<model> ground_model;
const material* ground_material;
const gl::vertex_array* ground_model_vao;
gl::drawing_mode ground_model_drawing_mode;

+ 826
- 534
src/engine/render/passes/material-pass.cpp
File diff suppressed because it is too large
View File


+ 86
- 75
src/engine/render/passes/material-pass.hpp View File

@ -24,8 +24,9 @@
#include <engine/render/material.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/gl/shader-variable.hpp>
#include <engine/gl/texture-2d.hpp>
#include <functional>
#include <unordered_map>
class resource_manager;
@ -39,92 +40,102 @@ class material_pass: public pass
{
public:
material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~material_pass();
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
/// Sets the material to be used when a render operation is missing a material. If no fallback material is specified, render operations without materials will not be processed.
void set_fallback_material(const material* fallback);
void set_fallback_material(std::shared_ptr<render::material> fallback);
private:
/**
* Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter.
*/
struct parameter_set
struct shader_cache_entry
{
const gl::shader_input* time;
const gl::shader_input* mouse;
const gl::shader_input* resolution;
const gl::shader_input* camera_position;
const gl::shader_input* camera_exposure;
const gl::shader_input* model;
const gl::shader_input* view;
const gl::shader_input* projection;
const gl::shader_input* model_view;
const gl::shader_input* view_projection;
const gl::shader_input* model_view_projection;
const gl::shader_input* normal_model;
const gl::shader_input* normal_model_view;
const gl::shader_input* clip_depth;
const gl::shader_input* log_depth_coef;
std::unique_ptr<gl::shader_program> shader_program;
const gl::shader_input* ambient_light_count;
const gl::shader_input* ambient_light_colors;
const gl::shader_input* point_light_count;
const gl::shader_input* point_light_colors;
const gl::shader_input* point_light_positions;
const gl::shader_input* point_light_attenuations;
const gl::shader_input* directional_light_count;
const gl::shader_input* directional_light_colors;
const gl::shader_input* directional_light_directions;
const gl::shader_input* directional_light_textures;
const gl::shader_input* directional_light_texture_matrices;
const gl::shader_input* directional_light_texture_opacities;
const gl::shader_input* spot_light_count;
const gl::shader_input* spot_light_colors;
const gl::shader_input* spot_light_positions;
const gl::shader_input* spot_light_directions;
const gl::shader_input* spot_light_attenuations;
const gl::shader_input* spot_light_cutoffs;
/// Command buffer which enables the shader and updates render state-related shader variables.
std::vector<std::function<void()>> shader_command_buffer;
const gl::shader_input* shadow_map_directional;
const gl::shader_input* shadow_bias_directional;
const gl::shader_input* shadow_splits_directional;
const gl::shader_input* shadow_matrices_directional;
/// Command buffer which updates geometry-related shader variables.
std::vector<std::function<void()>> geometry_command_buffer;
const gl::shader_input* skinning_palette;
/// Map of materials to command buffers which update corresponding material shader variables.
std::unordered_map<const material*, std::vector<std::function<void()>>> material_command_buffers;
};
const parameter_set* load_parameter_set(const gl::shader_program* program) const;
mutable std::unordered_map<const gl::shader_program*, parameter_set*> parameter_sets;
const material* fallback_material;
/// Map of state hashes to shader cache entries.
std::unordered_map<std::size_t, shader_cache_entry> shader_cache;
/**
* Evaluates the active camera and stores camera information in local variables to be passed to shaders.
*/
void evaluate_camera(const render::context& ctx);
/**
* Evaluates scene lights and stores lighting information in local variables to be passed to shaders.
*/
void evaluate_lighting(const render::context& ctx);
void evaluate_misc(const render::context& ctx);
[[nodiscard]] std::unique_ptr<gl::shader_program> generate_shader_program(const gl::shader_template& shader_template) const;
void build_shader_command_buffer(std::vector<std::function<void()>>& command_buffer, const gl::shader_program& shader_program) const;
void build_geometry_command_buffer(std::vector<std::function<void()>>& command_buffer, const gl::shader_program& shader_program) const;
void build_material_command_buffer(std::vector<std::function<void()>>& command_buffer, const gl::shader_program& shader_program, const material& material) const;
// Camera
const float4x4* view;
const float4x4* projection;
const float4x4* view_projection;
const float3* camera_position;
float camera_exposure;
float2 clip_depth;
float log_depth_coef;
// Ambient lights
std::vector<float3> ambient_light_colors;
std::size_t ambient_light_count;
// Point lights
std::vector<float3> point_light_colors;
std::vector<float3> point_light_positions;
std::vector<float3> point_light_attenuations;
std::size_t point_light_count;
// Directional lights
std::vector<float3> directional_light_colors;
std::vector<float3> directional_light_directions;
std::size_t directional_light_count;
// Directional shadows
std::vector<const gl::texture_2d*> directional_shadow_maps;
std::vector<float> directional_shadow_biases;
std::vector<const std::vector<float>*> directional_shadow_splits;
std::vector<const std::vector<float4x4>*> directional_shadow_matrices;
std::size_t directional_shadow_count;
// Spot lights
std::vector<float3> spot_light_colors;
std::vector<float3> spot_light_positions;
std::vector<float3> spot_light_directions;
std::vector<float3> spot_light_attenuations;
std::vector<float2> spot_light_cutoffs;
std::size_t spot_light_count;
// Misc
float time;
float timestep;
unsigned int frame{0};
float subframe;
float2 resolution;
float2 mouse_position;
int max_ambient_light_count;
int max_point_light_count;
int max_directional_light_count;
int max_spot_light_count;
int max_bone_count;
// Geometry
const float4x4* model;
mutable int ambient_light_count;
mutable int point_light_count;
mutable int directional_light_count;
mutable int spot_light_count;
float3* ambient_light_colors;
float3* point_light_colors;
float3* point_light_positions;
float3* point_light_attenuations;
float3* directional_light_colors;
float3* directional_light_directions;
const gl::texture_2d** directional_light_textures;
float4x4* directional_light_texture_matrices;
float* directional_light_texture_opacities;
float3* spot_light_colors;
float3* spot_light_positions;
float3* spot_light_directions;
float3* spot_light_attenuations;
float2* spot_light_cutoffs;
/// Hash of the lighting state.
std::size_t lighting_state_hash;
std::shared_ptr<render::material> fallback_material;
};
} // namespace render

+ 23
- 17
src/engine/render/passes/outline-pass.cpp View File

@ -22,7 +22,7 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.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>
@ -31,6 +31,7 @@
#include <engine/render/context.hpp>
#include <engine/render/material.hpp>
#include <engine/render/material-flags.hpp>
#include <engine/gl/shader-template.hpp>
#include <engine/scene/camera.hpp>
#include <cmath>
#include <glad/glad.h>
@ -42,21 +43,24 @@ outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fr
fill_shader(nullptr),
stroke_shader(nullptr)
{
// Load fill shader
fill_shader = resource_manager->load<gl::shader_program>("outline-fill-unskinned.glsl");
fill_model_view_projection_input = fill_shader->get_input("model_view_projection");
// Load fill shader template
auto fill_shader_template = resource_manager->load<gl::shader_template>("outline-fill-unskinned.glsl");
// Load stroke shader
stroke_shader = resource_manager->load<gl::shader_program>("outline-stroke-unskinned.glsl");
stroke_model_view_projection_input = stroke_shader->get_input("model_view_projection");
stroke_width_input = stroke_shader->get_input("width");
stroke_color_input = stroke_shader->get_input("color");
// Build fill shader
fill_shader = fill_shader_template->build({});
fill_model_view_projection_var = fill_shader->variable("model_view_projection");
// Load stroke shader template
auto stroke_shader_template = resource_manager->load<gl::shader_template>("outline-stroke-unskinned.glsl");
// Build stroke shader
stroke_shader = stroke_shader_template->build({});
stroke_model_view_projection_var = stroke_shader->variable("model_view_projection");
stroke_width_var = stroke_shader->variable("width");
stroke_color_var = stroke_shader->variable("color");
}
outline_pass::~outline_pass()
{}
void outline_pass::render(const render::context& ctx, render::queue& queue) const
void outline_pass::render(const render::context& ctx, render::queue& queue)
{
rasterizer->use_framebuffer(*framebuffer);
@ -91,10 +95,12 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons
{
const render::material* material = operation.material;
if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE))
{
continue;
}
model_view_projection = view_projection * operation.transform;
fill_model_view_projection_input->upload(model_view_projection);
fill_model_view_projection_var->update(model_view_projection);
rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count);
}
@ -119,8 +125,8 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons
// Setup stroke shader
rasterizer->use_program(*stroke_shader);
stroke_width_input->upload(outline_width);
stroke_color_input->upload(outline_color);
stroke_width_var->update(outline_width);
stroke_color_var->update(outline_color);
// Render strokes
for (const render::operation& operation: queue)
@ -130,7 +136,7 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons
continue;
model_view_projection = view_projection * operation.transform;
stroke_model_view_projection_input->upload(model_view_projection);
stroke_model_view_projection_var->update(model_view_projection);
rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count);
}

+ 10
- 9
src/engine/render/passes/outline-pass.hpp View File

@ -23,7 +23,8 @@
#include <engine/render/pass.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/gl/shader-variable.hpp>
#include <memory>
class resource_manager;
@ -36,20 +37,20 @@ class outline_pass: public pass
{
public:
outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~outline_pass();
virtual void render(const render::context& ctx, render::queue& queue) const final;
virtual ~outline_pass() = default;
void render(const render::context& ctx, render::queue& queue) override;
void set_outline_width(float width);
void set_outline_color(const float4& color);
private:
gl::shader_program* fill_shader;
const gl::shader_input* fill_model_view_projection_input;
std::unique_ptr<gl::shader_program> fill_shader;
const gl::shader_variable* fill_model_view_projection_var;
gl::shader_program* stroke_shader;
const gl::shader_input* stroke_model_view_projection_input;
const gl::shader_input* stroke_width_input;
const gl::shader_input* stroke_color_input;
std::unique_ptr<gl::shader_program> stroke_shader;
const gl::shader_variable* stroke_model_view_projection_var;
const gl::shader_variable* stroke_width_var;
const gl::shader_variable* stroke_color_var;
float outline_width;
float4 outline_color;

+ 63
- 42
src/engine/render/passes/resample-pass.cpp View File

@ -22,7 +22,7 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.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>
@ -36,35 +36,35 @@ namespace render {
resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager):
pass(rasterizer, framebuffer),
source_texture(nullptr)
source_texture{nullptr}
{
// Load resample shader template
shader_template = resource_manager->load<render::shader_template>("resample.glsl");
auto shader_template = resource_manager->load<gl::shader_template>("resample.glsl");
// Build resample shader program
shader = shader_template->build();
source_texture_input = shader->get_input("source_texture");
const float vertex_data[] =
const float2 vertex_positions[] =
{
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
{-1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, 1.0f},
{ 1.0f, 1.0f},
{-1.0f, -1.0f},
{ 1.0f, -1.0f}
};
const auto vertex_data = std::as_bytes(std::span{vertex_positions});
std::size_t vertex_size = 2;
std::size_t vertex_stride = sizeof(float) * vertex_size;
std::size_t vertex_count = 6;
quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data);
quad_vao = new gl::vertex_array();
quad_vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data);
quad_vao = std::make_unique<gl::vertex_array>();
// Define position vertex attribute
gl::vertex_attribute position_attribute;
position_attribute.buffer = quad_vbo;
position_attribute.buffer = quad_vbo.get();
position_attribute.offset = 0;
position_attribute.stride = vertex_stride;
position_attribute.type = gl::vertex_attribute_type::float_32;
@ -74,40 +74,61 @@ resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer*
quad_vao->bind(render::vertex_attribute::position, position_attribute);
}
resample_pass::~resample_pass()
void resample_pass::render(const render::context& ctx, render::queue& queue)
{
delete quad_vao;
delete quad_vbo;
delete shader;
/// @TODO
// resource_manager->unload("resample.glsl");
for (const auto& command: command_buffer)
{
command();
}
}
void resample_pass::render(const render::context& ctx, render::queue& queue) const
void resample_pass::set_source_texture(const gl::texture_2d* texture)
{
if (!source_texture)
return;
// Set rasterizer state
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
source_texture = texture;
// Render FXAA
rasterizer->use_framebuffer(*framebuffer);
rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]);
rasterizer->use_program(*shader);
source_texture_input->upload(source_texture);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
rebuild_command_buffer();
}
void resample_pass::set_source_texture(const gl::texture_2d* texture)
void resample_pass::rebuild_command_buffer()
{
source_texture = texture;
command_buffer.clear();
if (!source_texture || !shader)
{
return;
}
// Setup resample state
command_buffer.emplace_back
(
[&]()
{
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
rasterizer->use_framebuffer(*framebuffer);
rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]);
rasterizer->use_program(*shader);
}
);
// Update shader variables
if (auto source_texture_var = shader->variable("source_texture"))
{
command_buffer.emplace_back([&, source_texture_var](){source_texture_var->update(*source_texture);});
}
// Draw quad
command_buffer.emplace_back
(
[&]()
{
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6);
}
);
}
} // namespace render

+ 12
- 14
src/engine/render/passes/resample-pass.hpp View File

@ -21,12 +21,14 @@
#define ANTKEEPER_RENDER_RESAMPLE_PASS_HPP
#include <engine/render/pass.hpp>
#include <engine/render/shader-template.hpp>
#include <engine/gl/shader-template.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#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 <functional>
#include <memory>
class resource_manager;
@ -47,18 +49,13 @@ public:
*/
resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
/**
* Destructs a resample pass.
*/
virtual ~resample_pass();
/**
* Resamples a texture.
*
* @param ctx Render context.
* @param queue Render queue.
*/
virtual void render(const render::context& ctx, render::queue& queue) const final;
void render(const render::context& ctx, render::queue& queue) override;
/**
* Sets the resample source texture.
@ -68,14 +65,15 @@ public:
void set_source_texture(const gl::texture_2d* texture);
private:
const gl::texture_2d* source_texture;
void rebuild_command_buffer();
std::unique_ptr<gl::shader_program> shader;
std::unique_ptr<gl::vertex_buffer> quad_vbo;
std::unique_ptr<gl::vertex_array> quad_vao;
render::shader_template* shader_template;
gl::shader_program* shader;
const gl::shader_input* source_texture_input;
const gl::texture_2d* source_texture;
gl::vertex_buffer* quad_vbo;
gl::vertex_array* quad_vao;
std::vector<std::function<void()>> command_buffer;
};
} // namespace render

+ 41
- 28
src/engine/render/passes/shadow-map-pass.cpp View File

@ -22,10 +22,10 @@
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/shader-program.hpp>
#include <engine/gl/shader-input.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/render/context.hpp>
#include <engine/render/material.hpp>
#include <engine/gl/shader-template.hpp>
#include <engine/scene/camera.hpp>
#include <engine/scene/collection.hpp>
#include <engine/scene/light.hpp>
@ -47,13 +47,19 @@ static bool operation_compare(const render::operation& a, const render::operatio
shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager):
pass(rasterizer, nullptr)
{
// Load skinned shader program
unskinned_shader_program = resource_manager->load<gl::shader_program>("depth-unskinned.glsl");
unskinned_model_view_projection_input = unskinned_shader_program->get_input("model_view_projection");
// Load unskinned shader template
auto unskinned_shader_template = resource_manager->load<gl::shader_template>("depth-unskinned.glsl");
// Load unskinned shader program
skinned_shader_program = resource_manager->load<gl::shader_program>("depth-skinned.glsl");
skinned_model_view_projection_input = skinned_shader_program->get_input("model_view_projection");
// Build unskinned shader program
unskinned_shader_program = unskinned_shader_template->build({});
unskinned_model_view_projection_var = unskinned_shader_program->variable("model_view_projection");
// Load skinned shader template
auto skinned_shader_template = resource_manager->load<gl::shader_template>("depth-skinned.glsl");
// Build skinned shader program
skinned_shader_program = skinned_shader_template->build({});
skinned_model_view_projection_var = skinned_shader_program->variable("model_view_projection");
// Calculate bias-tile matrices
float4x4 bias_matrix = math::translate(math::matrix4<float>::identity(), float3{0.5f, 0.5f, 0.5f}) * math::scale(math::matrix4<float>::identity(), float3{0.5f, 0.5f, 0.5f});
@ -67,35 +73,40 @@ shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* r
}
}
shadow_map_pass::~shadow_map_pass()
{}
void shadow_map_pass::render(const render::context& ctx, render::queue& queue) const
void shadow_map_pass::render(const render::context& ctx, render::queue& queue)
{
// Collect lights
// For each light
const std::list<scene::object_base*>* lights = ctx.collection->get_objects(scene::light::object_type_id);
for (const scene::object_base* object: *lights)
{
// Ignore inactive lights
if (!object->is_active())
continue;
{
continue;
}
// Ignore non-directional lights
const scene::light* light = static_cast<const scene::light*>(object);
if (light->get_light_type() != scene::light_type::directional)
const scene::light& light = static_cast<const scene::light&>(*object);
if (light.get_light_type() != scene::light_type::directional)
{
continue;
}
// Ignore non-shadow casters
const scene::directional_light* directional_light = static_cast<const scene::directional_light*>(light);
if (!directional_light->is_shadow_caster())
const scene::directional_light& directional_light = static_cast<const scene::directional_light&>(light);
if (!directional_light.is_shadow_caster())
{
continue;
}
// Ignore improperly-configured lights
if (!directional_light->get_shadow_cascade_count() || !directional_light->get_shadow_framebuffer())
if (!directional_light.get_shadow_cascade_count() || !directional_light.get_shadow_framebuffer())
{
continue;
}
// Render cascaded shadow maps for light
render_csm(*directional_light, ctx, queue);
// Render cascaded shadow maps
render_csm(directional_light, ctx, queue);
}
}
@ -132,8 +143,10 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re
const float shadow_clip_far = math::lerp(camera_clip_near, camera_clip_far, light.get_shadow_cascade_coverage());
const unsigned int cascade_count = light.get_shadow_cascade_count();
float* cascade_distances = light.get_shadow_cascade_distances();
float4x4* cascade_matrices = light.get_shadow_cascade_matrices();
/// @TODO: don't const_cast
auto& cascade_distances = const_cast<std::vector<float>&>(light.get_shadow_cascade_distances());
auto& cascade_matrices = const_cast<std::vector<float4x4>&>(light.get_shadow_cascade_matrices());
// Calculate cascade far clipping plane distances
cascade_distances[cascade_count - 1] = shadow_clip_far;
@ -235,7 +248,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re
if (material)
{
// Skip materials which don't cast shadows
if (material->get_shadow_mode() == shadow_mode::none)
if (material->get_shadow_mode() == material_shadow_mode::none)
continue;
if (material->is_two_sided() != two_sided)
@ -254,7 +267,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re
}
// Switch shader programs if necessary
gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program : unskinned_shader_program;
gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program.get() : unskinned_shader_program.get();
if (active_shader_program != shader_program)
{
active_shader_program = shader_program;
@ -265,13 +278,13 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re
model_view_projection = cropped_view_projection * operation.transform;
// Upload operation-dependent parameters to shader program
if (active_shader_program == unskinned_shader_program)
if (active_shader_program == unskinned_shader_program.get())
{
unskinned_model_view_projection_input->upload(model_view_projection);
unskinned_model_view_projection_var->update(model_view_projection);
}
else if (active_shader_program == skinned_shader_program)
else if (active_shader_program == skinned_shader_program.get())
{
skinned_model_view_projection_input->upload(model_view_projection);
skinned_model_view_projection_var->update(model_view_projection);
}
// Draw geometry

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

Loading…
Cancel
Save