aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2026-02-03 20:26:01 +0100
committervimene <vincent.menegaux@gmail.com>2026-02-03 20:26:01 +0100
commitcbf7d23623b5bb2d2092cb6c86bc965138b4ea75 (patch)
tree7234b255b871cecca24aadc03c8d0857202f48e4
parent0d10b77f77459333c5549711334f417623ab1f3e (diff)
downloadengine-cbf7d23623b5bb2d2092cb6c86bc965138b4ea75.tar.gz
added mipmaps for the hw renderer, improvementsHEADmain
This commit add mipmaps, but for now, mipmaps are generated on the gpu at runtime. We should generate them in advance. - added mipmaps for the hardware renderer - renamed stb_image.c to stb_image.cpp - add compiler flag to stb_image.cpp to prevent warning - added pipe notation for various objects to have all clipping functions be left to right. We need this for the time being because dot notation would considerably complicate the current implementation - small improvements
-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