aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/engine.cpp205
-rw-r--r--src/math/utils.hpp23
-rw-r--r--src/o3d/mesh.cpp10
-rw-r--r--src/o3d/mesh.hpp4
-rw-r--r--src/o3d/polygon.hpp69
-rw-r--r--src/renderer.cpp4
-rw-r--r--src/shaders/simple_shaders.cpp16
-rw-r--r--src/shaders/simple_shaders.hpp29
-rw-r--r--src/stb_image.cpp (renamed from src/stb_image.c)0
10 files changed, 282 insertions, 82 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d014f5a..8515e67 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,7 +9,7 @@ engine_SOURCES = \
renderer.hpp renderer.cpp \
obj_parser.hpp obj_parser.cpp \
vulkan_utils.hpp \
- stb_image.c \
+ stb_image.cpp \
fb/fb.hpp \
fb/chfb.hpp fb/chfb.cpp \
fb/pixfb.hpp fb/pixfb.cpp \
@@ -46,6 +46,8 @@ engine_CPPFLAGS += $(NCURSES_CFLAGS)
engine_LDADD += $(NCURSES_LIBS)
endif
+engine-stb_image.$(OBJEXT): CXXFLAGS += -Wno-unused-but-set-variable
+
shader_spv_SOURCES = shaders/shader.slang
shader_spv_SPVFLAGS = -target spirv -profile spirv_1_4 -emit-spirv-directly -fvk-use-entrypoint-name -entry vert_main -entry frag_main
diff --git a/src/engine.cpp b/src/engine.cpp
index 305d254..91f7447 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -46,6 +46,7 @@
#include "math/mat4.hpp"
#include "math/quat.hpp"
#include "math/tform.hpp"
+#include "math/utils.hpp"
#include "ctrl/keyboard.hpp"
#include "ctrl/mouse.hpp"
#include "shaders/shaders.hpp"
@@ -313,15 +314,15 @@ static int main_term(Scene& scene) {
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
-static const std::vector<const char*> validation_layers = {
- "VK_LAYER_KHRONOS_validation",
+constexpr std::array validation_layers {
+ static_cast<const char*>("VK_LAYER_KHRONOS_validation"),
};
-static const std::vector<const char*> physical_device_ext_names = {
- VK_KHR_SWAPCHAIN_EXTENSION_NAME,
- VK_KHR_SPIRV_1_4_EXTENSION_NAME,
- VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
- VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME,
+constexpr std::array physical_device_ext_names {
+ static_cast<const char*>(VK_KHR_SWAPCHAIN_EXTENSION_NAME),
+ static_cast<const char*>(VK_KHR_SPIRV_1_4_EXTENSION_NAME),
+ static_cast<const char*>(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME),
+ static_cast<const char*>(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME),
};
#ifdef NDEBUG
@@ -613,7 +614,7 @@ static void copy_buf_to_img(VkCommandBuffer cmd_buf, VkBuffer src, VkImage dst,
}
static std::tuple<VkImage, VkDeviceMemory>
-create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, uint32_t height, VkFormat format,
+create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, uint32_t height, uint32_t mip_lvls, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags mem_flags) {
auto texture_img = [&] {
VkImageCreateInfo img_ci {
@@ -624,7 +625,7 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui
// TODO: check if this format is supported
.format = format,
.extent = { .width = width, .height = height, .depth = 1 },
- .mipLevels = 1,
+ .mipLevels = mip_lvls,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = tiling,
@@ -669,7 +670,7 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui
static std::tuple<VkImage, VkDeviceMemory, VkImageView>
create_depth_resources(VkPhysicalDevice physical_device, VkDevice device, VkExtent2D swapchain_extent, VkFormat depth_format) {
- auto [depth_img, depth_img_device_mem] = create_img(physical_device, device, swapchain_extent.width, swapchain_extent.height, depth_format,
+ auto [depth_img, depth_img_device_mem] = create_img(physical_device, device, swapchain_extent.width, swapchain_extent.height, 1, depth_format,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
auto depth_img_view = [&] {
VkImageViewCreateInfo img_view_ci {
@@ -704,7 +705,7 @@ static void destroy_depth_resources(VkDevice device, VkImage depth_img, VkDevice
vkDestroyImage(device, depth_img, nullptr);
}
-static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img,
+static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img, uint32_t mip_lvls,
VkImageLayout old_layout, VkImageLayout new_layout,
VkAccessFlags2 src_access_mask, VkAccessFlags2 dst_access_mask,
VkPipelineStageFlags2 src_stage_mask, VkPipelineStageFlags2 dst_stage_mask) {
@@ -723,7 +724,7 @@ static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
- .levelCount = 1,
+ .levelCount = mip_lvls,
.baseArrayLayer = 0,
.layerCount = 1,
},
@@ -742,6 +743,154 @@ static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img,
vkCmdPipelineBarrier2(cmd_buf, &dependency_info);
}
+static void generate_mipmaps(VkCommandBuffer cmd_buf, VkPhysicalDevice physical_device, VkImage img, VkFormat fmt, int w, int h, uint32_t mip_lvls) noexcept {
+ if (mip_lvls == 1) return;
+
+ VkFormatProperties2 format_props {};
+ format_props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
+ format_props.pNext = nullptr;
+ vkGetPhysicalDeviceFormatProperties2(physical_device, fmt, &format_props);
+ // TODO: I'm not sure if we should check for these 3 flags, maybe we only need to check for the
+ // first and the other two are implied
+ if ((format_props.formatProperties.optimalTilingFeatures
+ & (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT))
+ != (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
+ std::println(stderr, "texture format {} doesn't support vkCmdBlitImage2", string_VkFormat(fmt));
+ exit(EXIT_FAILURE);
+ }
+
+ int32_t mip_w = static_cast<int32_t>(w);
+ int32_t mip_h = static_cast<int32_t>(h);
+ for (uint32_t i = 0; i < mip_lvls - 1; i++) {
+ {
+ VkImageMemoryBarrier2 img_mem_barrier {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = img,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = i,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ VkDependencyInfo dependency_info {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pNext = nullptr,
+ .dependencyFlags = {},
+ .memoryBarrierCount = 0,
+ .pMemoryBarriers = {},
+ .bufferMemoryBarrierCount = 0,
+ .pBufferMemoryBarriers = {},
+ .imageMemoryBarrierCount = 1,
+ .pImageMemoryBarriers = &img_mem_barrier,
+ };
+ vkCmdPipelineBarrier2(cmd_buf, &dependency_info);
+ }
+ int32_t next_mip_w = std::max(mip_w / 2, 1);
+ int32_t next_mip_h = std::max(mip_h / 2, 1);
+ {
+ VkImageBlit2 region {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
+ .pNext = nullptr,
+ .srcSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = i,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ .srcOffsets = { { 0, 0, 0 }, { mip_w, mip_h, 1 } },
+ .dstSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = i + 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ .dstOffsets = { { 0, 0, 0 }, { next_mip_w, next_mip_h, 1 } },
+ };
+ VkBlitImageInfo2 blit_img_info {
+ .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
+ .pNext = nullptr,
+ .srcImage = img,
+ .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .dstImage = img,
+ .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .regionCount = 1,
+ .pRegions = &region,
+ .filter = VK_FILTER_LINEAR,
+ };
+ vkCmdBlitImage2(cmd_buf, &blit_img_info);
+ }
+ mip_w = next_mip_w;
+ mip_h = next_mip_h;
+ }
+ {
+ std::array img_mem_barriers {
+ VkImageMemoryBarrier2 {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = img,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = mip_lvls - 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ },
+ VkImageMemoryBarrier2 {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = img,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = mip_lvls - 1,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ },
+ };
+ VkDependencyInfo dependency_info {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pNext = nullptr,
+ .dependencyFlags = {},
+ .memoryBarrierCount = 0,
+ .pMemoryBarriers = {},
+ .bufferMemoryBarrierCount = 0,
+ .pBufferMemoryBarriers = {},
+ .imageMemoryBarrierCount = img_mem_barriers.size(),
+ .pImageMemoryBarriers = img_mem_barriers.data(),
+ };
+ vkCmdPipelineBarrier2(cmd_buf, &dependency_info);
+ }
+}
+
static int main_graphical(Scene& scene) {
// init window
glfwInit();
@@ -1664,10 +1813,12 @@ static int main_graphical(Scene& scene) {
std::tie(depth_img, depth_img_device_mem, depth_img_view) = create_depth_resources(physical_device, device, swapchain_extent, depth_format);
// create texture image
- auto [texture_img, texture_img_device_mem, texture_w, texture_h, texture_pixels] = [&] {
+ auto [texture_img, texture_mip_lvls, texture_img_device_mem, texture_w, texture_h, texture_pixels] = [&] {
int w, h, channels;
stbi_uc* pixels = stbi_load(DATADIR "/assets/textures/viking_room.png", &w, &h, &channels, STBI_rgb_alpha);
+ uint32_t texture_mip_lvls = engine::math::utils::log2_floored(static_cast<uint32_t>(std::max(w, h))) + 1;
+
// TODO: we're also using this size as the host pixels buffer size, I don't know if mixing
// the two will cause problems later. Right now they are the same, so it doesn't
VkDeviceSize img_size = w * h * 4;
@@ -1694,27 +1845,25 @@ static int main_graphical(Scene& scene) {
vkUnmapMemory(device, staging_buf_device_mem);
}
- auto [texture_img, texture_img_device_mem] = create_img(physical_device, device, static_cast<uint32_t>(w), static_cast<uint32_t>(h), VK_FORMAT_R8G8B8A8_SRGB,
- VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ auto [texture_img, texture_img_device_mem] = create_img(physical_device, device, static_cast<uint32_t>(w), static_cast<uint32_t>(h),
+ texture_mip_lvls, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
{
auto cmd_buf = begin_single_time_cmds(device, cmd_pool);
- transition_image_layout(cmd_buf, texture_img,
+ transition_image_layout(cmd_buf, texture_img, texture_mip_lvls,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
{}, VK_ACCESS_2_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_2_TRANSFER_BIT);
copy_buf_to_img(cmd_buf, staging_buf, texture_img, static_cast<uint32_t>(w), static_cast<uint32_t>(h));
- transition_image_layout(cmd_buf, texture_img,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
- VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_ACCESS_2_SHADER_READ_BIT,
- VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT);
+ generate_mipmaps(cmd_buf, physical_device, texture_img, VK_FORMAT_R8G8B8A8_SRGB, w, h, texture_mip_lvls);
end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf);
}
vkFreeMemory(device, staging_buf_device_mem, nullptr);
vkDestroyBuffer(device, staging_buf, nullptr);
- return std::tuple { texture_img, texture_img_device_mem, w, h, pixels };
+ return std::tuple { texture_img, texture_mip_lvls, texture_img_device_mem, w, h, pixels };
}();
// create texture image view
@@ -1733,7 +1882,7 @@ static int main_graphical(Scene& scene) {
.subresourceRange = { // VkImageSubresourceRange
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
- .levelCount = 1,
+ .levelCount = texture_mip_lvls,
.baseArrayLayer = 0,
.layerCount = 1,
},
@@ -1763,9 +1912,9 @@ static int main_graphical(Scene& scene) {
.maxAnisotropy = physical_device_props.properties.limits.maxSamplerAnisotropy,
.compareEnable = VK_FALSE,
// TODO: check if it has to be VK_COMPARE_OP_ALWAYS instead of VK_COMPARE_OP_NEVER
- .compareOp = VK_COMPARE_OP_NEVER,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
.minLod = 0.f,
- .maxLod = 0.f,
+ .maxLod = VK_LOD_CLAMP_NONE,
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
@@ -2232,7 +2381,7 @@ static int main_graphical(Scene& scene) {
// render_and_present_frame
[&](const Matrix4& view_mat, const Matrix4& proj_mat, const Matrix4& inv_view_mat, float /* time */, float ellapsed_time) {
- if (show_fps)
+ if constexpr (show_fps)
std::cout << "\rfps: " << (1.f / ellapsed_time) << " ";
if (VkResult res = vkWaitForFences(device, 1, &fences_in_flight[frame_idx], VK_TRUE, std::numeric_limits<uint64_t>::max()); res != VK_SUCCESS) {
@@ -2381,7 +2530,7 @@ static int main_graphical(Scene& scene) {
}
{
- VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, (transparent_window ? 0.f : 1.f) } } };
+ VkClearValue clear_color_val { .color { .float32 { 0.f, 0.f, 0.f, (transparent_window ? 0.f : 1.f) } } };
VkClearValue clear_depth_val { .depthStencil { .depth = 1.f, .stencil = 0 } };
VkRenderingAttachmentInfo rendering_color_info {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
@@ -2461,7 +2610,7 @@ static int main_graphical(Scene& scene) {
vkCmdEndRendering(cmd_bufs[frame_idx]);
- transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx],
+ transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx], 1,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, {},
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
@@ -2597,7 +2746,7 @@ static int main_graphical(Scene& scene) {
}
);
- if (show_fps)
+ if constexpr (show_fps)
std::cout << std::endl;
if (VkResult res = vkDeviceWaitIdle(device); res != VK_SUCCESS) {
diff --git a/src/math/utils.hpp b/src/math/utils.hpp
index 5c8c62d..ac19ddc 100644
--- a/src/math/utils.hpp
+++ b/src/math/utils.hpp
@@ -3,6 +3,7 @@
#include <array>
#include <utility>
+#include <concepts>
#include "math/vector.hpp"
namespace engine::math {
@@ -35,6 +36,28 @@ constexpr float map(float x, float from1, float from2, float to1, float to2) {
return to1 + (x - from1) * (to2 - to1) / (from2 - from1);
}
+template<typename UInt>
+constexpr UInt log2_floored(UInt n) noexcept
+requires std::same_as<UInt, unsigned> || std::same_as<UInt, unsigned long> || std::same_as<UInt, unsigned long long>
+{
+#ifdef __GNUG__
+ if constexpr (std::is_same_v<UInt, unsigned>) {
+ return static_cast<UInt>(sizeof(UInt) * 8 - 1) - __builtin_clz(n);
+ } else if constexpr (std::is_same_v<UInt, unsigned long>) {
+ return static_cast<UInt>(sizeof(UInt) * 8 - 1) - __builtin_clzl(n);
+ } else { // unsigned long long
+ return static_cast<UInt>(sizeof(UInt) * 8 - 1) - __builtin_clzll(n);
+ }
+#else
+ UInt ret = 0;
+ while (n & 1) {
+ n >>= 1;
+ ret++;
+ }
+ return ret;
+#endif
+}
+
}
#endif // MATH_UTILS_HPP
diff --git a/src/o3d/mesh.cpp b/src/o3d/mesh.cpp
index fc5ff78..c3314b4 100644
--- a/src/o3d/mesh.cpp
+++ b/src/o3d/mesh.cpp
@@ -21,7 +21,7 @@ struct VertexIndicesHasher {
}
};
-Mesh Mesh::plane(float width, float height) {
+Mesh Mesh::plane(float width, float height) noexcept {
const float w2 = width / 2,
h2 = height / 2;
return {
@@ -50,18 +50,18 @@ Mesh Mesh::plane(float width, float height) {
};
}
-std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> Mesh::linearize_indices() const & {
+std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> Mesh::linearize_indices() const & noexcept {
std::unordered_map<std::array<size_t, 3>, size_t, VertexIndicesHasher<3>> unique_vertices;
std::vector<engine::vk::Vertex> linearized_vertices;
std::vector<uint16_t> linearized_indices;
- for (const auto& triangle_indices : this->indices) {
+ for (const auto& triangle_indices : indices) {
for (const auto& vertex_indices : triangle_indices) {
auto it = unique_vertices.find(vertex_indices);
if (it == unique_vertices.end()) {
- linearized_vertices.emplace_back(this->vertices[vertex_indices[0]], this->normals[vertex_indices[1]], this->uvs[vertex_indices[2]]);
+ linearized_vertices.emplace_back(vertices[vertex_indices[0]], normals[vertex_indices[1]], uvs[vertex_indices[2]]);
size_t idx = linearized_vertices.size() - 1;
- unique_vertices.emplace(vertex_indices, idx);
+ unique_vertices[vertex_indices] = idx;
linearized_indices.emplace_back(idx);
} else {
linearized_indices.emplace_back((*it).second);
diff --git a/src/o3d/mesh.hpp b/src/o3d/mesh.hpp
index d1d3f1c..cac0f62 100644
--- a/src/o3d/mesh.hpp
+++ b/src/o3d/mesh.hpp
@@ -13,7 +13,7 @@
namespace engine::o3d {
struct Mesh {
- static Mesh plane(float width, float height);
+ static Mesh plane(float width, float height) noexcept;
std::vector<engine::math::Vector3> vertices;
std::vector<engine::math::Vector3> normals;
@@ -22,7 +22,7 @@ struct Mesh {
// TODO: find a better way to do this. This workaround is due to the fact that vulkan only
// accepts a single index, not an index for each attributes
- std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> linearize_indices() const &;
+ std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> linearize_indices() const & noexcept;
};
}
diff --git a/src/o3d/polygon.hpp b/src/o3d/polygon.hpp
index 5ede910..47ecafe 100644
--- a/src/o3d/polygon.hpp
+++ b/src/o3d/polygon.hpp
@@ -17,11 +17,39 @@ concept PolygonVectorTypeConcept = engine::math::VectorTypeConcept<T> && (std::i
template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count> struct Polygon;
+class ToPolygon {
+ public:
+ constexpr ToPolygon() {}
+};
+
template<engine::math::VectorTypeConcept VectorType>
-constexpr Polygon<VectorType, 3> from_triangle_derived(const engine::o3d::TriangleDerived<VectorType>& triangle_derived);
+constexpr Polygon<VectorType, 3> operator|(const engine::o3d::TriangleDerived<VectorType>& triangle_derived, ToPolygon);
template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count, engine::math::vector_coords::VectorCoord coord_id, bool keep_geq>
-constexpr Polygon<VectorType, max_points_count + 1> clip_aligned(float boundary, const Polygon<VectorType, max_points_count>& polygon);
+class ClipAligned {
+ public:
+ constexpr ClipAligned(float boundary) : m_boundary { boundary } {}
+
+ constexpr float boundary() const & {
+ return m_boundary;
+ }
+ private:
+ float m_boundary;
+};
+
+template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count, engine::math::vector_coords::VectorCoord coord_id, bool keep_geq>
+constexpr Polygon<VectorType, max_points_count + 1> operator|(const Polygon<VectorType, max_points_count>& polygon,
+ ClipAligned<VectorType, max_points_count, coord_id, keep_geq> clip_aligned);
+
+class DivByW {
+ public:
+ constexpr DivByW() {}
+};
+
+class SignedAreaXY {
+ public:
+ constexpr SignedAreaXY() {}
+};
template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count>
struct Polygon {
@@ -33,19 +61,19 @@ struct Polygon {
: points_count { points_count }, points { points } {}
constexpr Polygon<VectorType, max_points_count + 2> clip_z(float z1, float z2) const & {
- return
- clip_aligned<VectorType, max_points_count + 1, engine::math::vector_coords::VectorCoord::z, false>(z2,
- clip_aligned<VectorType, max_points_count + 0, engine::math::vector_coords::VectorCoord::z, true >(z1,
- *this));
+ return *this
+ | ClipAligned<VectorType, max_points_count + 0, engine::math::vector_coords::VectorCoord::z, true >(z1)
+ | ClipAligned<VectorType, max_points_count + 1, engine::math::vector_coords::VectorCoord::z, false>(z2)
+ ;
}
constexpr Polygon<VectorType, max_points_count + 4> clip_xy(float x1, float y1, float x2, float y2) const & {
- return
- clip_aligned<VectorType, max_points_count + 3, engine::math::vector_coords::VectorCoord::y, false>(y2,
- clip_aligned<VectorType, max_points_count + 2, engine::math::vector_coords::VectorCoord::x, false>(x2,
- clip_aligned<VectorType, max_points_count + 1, engine::math::vector_coords::VectorCoord::y, true >(y1,
- clip_aligned<VectorType, max_points_count + 0, engine::math::vector_coords::VectorCoord::x, true >(x1,
- *this))));
+ return *this
+ | ClipAligned<VectorType, max_points_count + 0, engine::math::vector_coords::VectorCoord::x, true >(x1)
+ | ClipAligned<VectorType, max_points_count + 1, engine::math::vector_coords::VectorCoord::y, true >(y1)
+ | ClipAligned<VectorType, max_points_count + 2, engine::math::vector_coords::VectorCoord::x, false>(x2)
+ | ClipAligned<VectorType, max_points_count + 3, engine::math::vector_coords::VectorCoord::y, false>(y2)
+ ;
}
constexpr Polygon<VectorType, max_points_count> map_xy(
@@ -89,12 +117,13 @@ struct Polygon {
};
template<engine::math::VectorTypeConcept VectorType>
-constexpr Polygon<VectorType, 3> from_triangle_derived(const engine::o3d::TriangleDerived<VectorType>& triangle_derived) {
+constexpr Polygon<VectorType, 3> operator|(const engine::o3d::TriangleDerived<VectorType>& triangle_derived, ToPolygon) {
return { 3, { triangle_derived.derived_vertex1, triangle_derived.derived_vertex2, triangle_derived.derived_vertex3 } };
}
template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count, engine::math::vector_coords::VectorCoord coord_id, bool keep_geq>
-constexpr Polygon<VectorType, max_points_count + 1> clip_aligned(float boundary, const Polygon<VectorType, max_points_count>& polygon)
+constexpr Polygon<VectorType, max_points_count + 1> operator|(const Polygon<VectorType, max_points_count>& polygon,
+ ClipAligned<VectorType, max_points_count, coord_id, keep_geq> clip_aligned)
{
using transposition = engine::math::vector_coords::transpose<engine::math::vector_coords::VectorCoord::x, coord_id>;
constexpr auto y_coord_id = transposition::template id<engine::math::vector_coords::VectorCoord::y>();
@@ -107,10 +136,10 @@ constexpr Polygon<VectorType, max_points_count + 1> clip_aligned(float boundary,
for (size_t i = 0; i < polygon.points_count; i++) {
const auto& prev = polygon.points[(i + polygon.points_count - 1) % polygon.points_count];
const auto& cur = polygon.points[i];
- if (is_in(prev, boundary) != is_in(cur, boundary)) {
- float fac = (boundary - prev.vertex.template v<coord_id>()) / (cur.vertex.template v<coord_id>() - prev.vertex.template v<coord_id>());
+ if (is_in(prev, clip_aligned.boundary()) != is_in(cur, clip_aligned.boundary())) {
+ float fac = (clip_aligned.boundary() - prev.vertex.template v<coord_id>()) / (cur.vertex.template v<coord_id>() - prev.vertex.template v<coord_id>());
auto& new_point = new_polygon.points[new_polygon.points_count++];
- new_point.vertex.template v<coord_id>() = boundary;
+ new_point.vertex.template v<coord_id>() = clip_aligned.boundary();
new_point.vertex.template v<y_coord_id>() = engine::math::utils::lerp(prev.vertex.template v<y_coord_id>(), cur.vertex.template v<y_coord_id>(), fac);
if constexpr (std::is_same_v<VectorType, engine::math::Vector3>) {
// called new_w because it represents w, but because we only need x, y and w, we use
@@ -127,14 +156,14 @@ constexpr Polygon<VectorType, max_points_count + 1> clip_aligned(float boundary,
new_point.b1 = engine::math::utils::lerp(prev.b1, cur.b1, fac);
}
}
- if (is_in(cur, boundary))
+ if (is_in(cur, clip_aligned.boundary()))
new_polygon.points[new_polygon.points_count++] = cur;
}
return new_polygon;
}
template<std::size_t max_points_count>
-constexpr Polygon<engine::math::Vector3, max_points_count> div_by_w(const Polygon<engine::math::Vector4, max_points_count>& polygon) {
+constexpr Polygon<engine::math::Vector3, max_points_count> operator|(const Polygon<engine::math::Vector4, max_points_count>& polygon, DivByW) {
Polygon<engine::math::Vector3, max_points_count> new_polygon;
new_polygon.points_count = polygon.points_count;
for (std::size_t i = 0; i < polygon.points_count; i++)
@@ -143,7 +172,7 @@ constexpr Polygon<engine::math::Vector3, max_points_count> div_by_w(const Polygo
}
template<std::size_t max_points_count>
-constexpr float signed_area_xy(const Polygon<engine::math::Vector3, max_points_count>& polygon) {
+constexpr float operator|(const Polygon<engine::math::Vector3, max_points_count>& polygon, SignedAreaXY) {
float res = 0.f;
if (polygon.points_count > 0) {
for (std::size_t i = 0; i < polygon.points_count - 1; i++)
diff --git a/src/renderer.cpp b/src/renderer.cpp
index 9447119..3cd721d 100644
--- a/src/renderer.cpp
+++ b/src/renderer.cpp
@@ -51,8 +51,8 @@ enum class TriangleSide { top, bottom };
template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
void Renderer<FrameBuffer, Shaders>::draw_triangle(const Triangle& triangle) {
- const auto& polygon = engine::o3d::polygon::div_by_w(engine::o3d::polygon::from_triangle_derived(triangle.to_derived()).clip_z(0.f, 1.f));
- if (engine::o3d::polygon::signed_area_xy(polygon) >= 0) return;
+ const auto& polygon = (triangle.to_derived() | engine::o3d::polygon::ToPolygon()).clip_z(0.f, 1.f) | engine::o3d::polygon::DivByW();
+ if ((polygon | engine::o3d::polygon::SignedAreaXY()) >= 0) return;
const auto& [final_triangles_count, final_triangles]
= polygon.clip_xy(-1.f, -1.f, 1.f, 1.f)
.map_xy({ -1.f, -1.f }, { 1.f, 1.f }, { -.5f, -.5f }, { static_cast<float>(fb.width()) - .5f, static_cast<float>(fb.height()) - .5f })
diff --git a/src/shaders/simple_shaders.cpp b/src/shaders/simple_shaders.cpp
index ebbec31..af7bb79 100644
--- a/src/shaders/simple_shaders.cpp
+++ b/src/shaders/simple_shaders.cpp
@@ -1,10 +1,22 @@
#include "shaders/simple_shaders.hpp"
+#include <vector>
+#include <cstddef>
+#include <utility>
+#include "math/vector.hpp"
#include "math/mat4.hpp"
using namespace engine::shaders;
-using engine::math::Matrix4;
+using
+ engine::math::Vector4,
+ engine::math::Matrix4;
-SimpleShaders::SimpleShaders(int w, int h, unsigned char* pixels) : w { w }, h { h }, pixels { pixels } {
+SimpleShaders::SimpleShaders(int w, int h, unsigned char* pixels) : w { w }, h { h }, pixels {} {
+ this->pixels.reserve(w * h);
+ for (size_t i = 0; i < static_cast<std::size_t>(w * h); i++) {
+ [&]<std::size_t... j>(std::index_sequence<j...>) {
+ this->pixels.emplace_back(static_cast<float>(pixels[i * 4 + j]) / 255.f ...);
+ }(std::make_index_sequence<4> {});
+ }
}
void SimpleShaders::set_view(const Matrix4& view_mat, const Matrix4& proj_mat) {
diff --git a/src/shaders/simple_shaders.hpp b/src/shaders/simple_shaders.hpp
index c308ab3..831f969 100644
--- a/src/shaders/simple_shaders.hpp
+++ b/src/shaders/simple_shaders.hpp
@@ -3,6 +3,7 @@
#include <algorithm>
#include <cmath>
+#include <vector>
#include "fb/pixfb.hpp"
#include "math/vector.hpp"
#include "math/mat4.hpp"
@@ -16,7 +17,7 @@ class SimpleShaders {
engine::math::Matrix4 model_mat, view_mat, proj_mat, final_mat;
int w, h;
- unsigned char* pixels;
+ std::vector<engine::math::Vector4> pixels;
SimpleShaders(int w, int h, unsigned char* pixels);
void set_view(const engine::math::Matrix4& view_mat, const engine::math::Matrix4& proj_mat);
@@ -54,27 +55,11 @@ class SimpleShaders {
int tx1 = std::clamp(static_cast<int>(txf) + 1, 0, w);
int ty0 = std::clamp(static_cast<int>(tyf) + 0, 0, h);
int ty1 = std::clamp(static_cast<int>(tyf) + 1, 0, h);
- float r00 = static_cast<float>(pixels[(tx0 + ty0 * w) * 4 + 0]) / 255.f;
- float r10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 0]) / 255.f;
- float r01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 0]) / 255.f;
- float r11 = static_cast<float>(pixels[(tx1 + ty1 * w) * 4 + 0]) / 255.f;
- float r = (1.f - fac_x) * (1.f - fac_y) * r00 + fac_x * (1.f - fac_y) * r10 + (1.f - fac_x) * fac_y * r01 + fac_x * fac_y * r11;
- float g00 = static_cast<float>(pixels[(tx0 + ty0 * w) * 4 + 1]) / 255.f;
- float g10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 1]) / 255.f;
- float g01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 1]) / 255.f;
- float g11 = static_cast<float>(pixels[(tx1 + ty1 * w) * 4 + 1]) / 255.f;
- float g = (1.f - fac_x) * (1.f - fac_y) * g00 + fac_x * (1.f - fac_y) * g10 + (1.f - fac_x) * fac_y * g01 + fac_x * fac_y * g11;
- float b00 = static_cast<float>(pixels[(tx0 + ty0 * w) * 4 + 2]) / 255.f;
- float b10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 2]) / 255.f;
- float b01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 2]) / 255.f;
- float b11 = static_cast<float>(pixels[(tx1 + ty1 * w) * 4 + 2]) / 255.f;
- float b = (1.f - fac_x) * (1.f - fac_y) * b00 + fac_x * (1.f - fac_y) * b10 + (1.f - fac_x) * fac_y * b01 + fac_x * fac_y * b11;
- float a00 = static_cast<float>(pixels[(tx0 + ty0 * w) * 4 + 3]) / 255.f;
- float a10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 3]) / 255.f;
- float a01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 3]) / 255.f;
- float a11 = static_cast<float>(pixels[(tx1 + ty1 * w) * 4 + 3]) / 255.f;
- float a = (1.f - fac_x) * (1.f - fac_y) * a00 + fac_x * (1.f - fac_y) * a10 + (1.f - fac_x) * fac_y * a01 + fac_x * fac_y * a11;
- return engine::math::Vector4 { r, g, b, a };
+ return
+ ((1.f - fac_x) * (1.f - fac_y)) * pixels[tx0 + ty0 * w]
+ + (fac_x * (1.f - fac_y)) * pixels[tx1 + ty0 * w]
+ + ((1.f - fac_x) * fac_y ) * pixels[tx0 + ty1 * w]
+ + (fac_x * fac_y ) * pixels[tx1 + ty1 * w];
}
};
diff --git a/src/stb_image.c b/src/stb_image.cpp
index cf4a748..cf4a748 100644
--- a/src/stb_image.c
+++ b/src/stb_image.cpp