From cbf7d23623b5bb2d2092cb6c86bc965138b4ea75 Mon Sep 17 00:00:00 2001 From: vimene Date: Tue, 3 Feb 2026 20:26:01 +0100 Subject: added mipmaps for the hw renderer, improvements 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 --- src/Makefile.am | 4 +- src/engine.cpp | 205 +++++++++++++++++++++++++++++++++++------ src/math/utils.hpp | 23 +++++ src/o3d/mesh.cpp | 10 +- src/o3d/mesh.hpp | 4 +- src/o3d/polygon.hpp | 69 ++++++++++---- src/renderer.cpp | 4 +- src/shaders/simple_shaders.cpp | 16 +++- src/shaders/simple_shaders.hpp | 29 ++---- src/stb_image.c | 3 - src/stb_image.cpp | 3 + 11 files changed, 285 insertions(+), 85 deletions(-) delete mode 100644 src/stb_image.c create mode 100644 src/stb_image.cpp 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 validation_layers = { - "VK_LAYER_KHRONOS_validation", +constexpr std::array validation_layers { + static_cast("VK_LAYER_KHRONOS_validation"), }; -static const std::vector 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(VK_KHR_SWAPCHAIN_EXTENSION_NAME), + static_cast(VK_KHR_SPIRV_1_4_EXTENSION_NAME), + static_cast(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME), + static_cast(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 -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 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(w); + int32_t mip_h = static_cast(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 = ®ion, + .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(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(w), static_cast(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(w), static_cast(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(w), static_cast(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::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 #include +#include #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 +constexpr UInt log2_floored(UInt n) noexcept +requires std::same_as || std::same_as || std::same_as +{ +#ifdef __GNUG__ + if constexpr (std::is_same_v) { + return static_cast(sizeof(UInt) * 8 - 1) - __builtin_clz(n); + } else if constexpr (std::is_same_v) { + return static_cast(sizeof(UInt) * 8 - 1) - __builtin_clzl(n); + } else { // unsigned long long + return static_cast(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> Mesh::linearize_indices() const & { +std::tuple, std::vector> Mesh::linearize_indices() const & noexcept { std::unordered_map, size_t, VertexIndicesHasher<3>> unique_vertices; std::vector linearized_vertices; std::vector 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 vertices; std::vector 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> linearize_indices() const &; + std::tuple, std::vector> 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 && (std::i template struct Polygon; +class ToPolygon { + public: + constexpr ToPolygon() {} +}; + template -constexpr Polygon from_triangle_derived(const engine::o3d::TriangleDerived& triangle_derived); +constexpr Polygon operator|(const engine::o3d::TriangleDerived& triangle_derived, ToPolygon); template -constexpr Polygon clip_aligned(float boundary, const Polygon& polygon); +class ClipAligned { + public: + constexpr ClipAligned(float boundary) : m_boundary { boundary } {} + + constexpr float boundary() const & { + return m_boundary; + } + private: + float m_boundary; +}; + +template +constexpr Polygon operator|(const Polygon& polygon, + ClipAligned clip_aligned); + +class DivByW { + public: + constexpr DivByW() {} +}; + +class SignedAreaXY { + public: + constexpr SignedAreaXY() {} +}; template struct Polygon { @@ -33,19 +61,19 @@ struct Polygon { : points_count { points_count }, points { points } {} constexpr Polygon clip_z(float z1, float z2) const & { - return - clip_aligned(z2, - clip_aligned(z1, - *this)); + return *this + | ClipAligned(z1) + | ClipAligned(z2) + ; } constexpr Polygon clip_xy(float x1, float y1, float x2, float y2) const & { - return - clip_aligned(y2, - clip_aligned(x2, - clip_aligned(y1, - clip_aligned(x1, - *this)))); + return *this + | ClipAligned(x1) + | ClipAligned(y1) + | ClipAligned(x2) + | ClipAligned(y2) + ; } constexpr Polygon map_xy( @@ -89,12 +117,13 @@ struct Polygon { }; template -constexpr Polygon from_triangle_derived(const engine::o3d::TriangleDerived& triangle_derived) { +constexpr Polygon operator|(const engine::o3d::TriangleDerived& triangle_derived, ToPolygon) { return { 3, { triangle_derived.derived_vertex1, triangle_derived.derived_vertex2, triangle_derived.derived_vertex3 } }; } template -constexpr Polygon clip_aligned(float boundary, const Polygon& polygon) +constexpr Polygon operator|(const Polygon& polygon, + ClipAligned clip_aligned) { using transposition = engine::math::vector_coords::transpose; constexpr auto y_coord_id = transposition::template id(); @@ -107,10 +136,10 @@ constexpr Polygon 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()) / (cur.vertex.template v() - prev.vertex.template v()); + if (is_in(prev, clip_aligned.boundary()) != is_in(cur, clip_aligned.boundary())) { + float fac = (clip_aligned.boundary() - prev.vertex.template v()) / (cur.vertex.template v() - prev.vertex.template v()); auto& new_point = new_polygon.points[new_polygon.points_count++]; - new_point.vertex.template v() = boundary; + new_point.vertex.template v() = clip_aligned.boundary(); new_point.vertex.template v() = engine::math::utils::lerp(prev.vertex.template v(), cur.vertex.template v(), fac); if constexpr (std::is_same_v) { // called new_w because it represents w, but because we only need x, y and w, we use @@ -127,14 +156,14 @@ constexpr Polygon 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 -constexpr Polygon div_by_w(const Polygon& polygon) { +constexpr Polygon operator|(const Polygon& polygon, DivByW) { Polygon 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 div_by_w(const Polygo } template -constexpr float signed_area_xy(const Polygon& polygon) { +constexpr float operator|(const Polygon& 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 void Renderer::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(fb.width()) - .5f, static_cast(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 +#include +#include +#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(w * h); i++) { + [&](std::index_sequence) { + this->pixels.emplace_back(static_cast(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 #include +#include #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 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(txf) + 1, 0, w); int ty0 = std::clamp(static_cast(tyf) + 0, 0, h); int ty1 = std::clamp(static_cast(tyf) + 1, 0, h); - float r00 = static_cast(pixels[(tx0 + ty0 * w) * 4 + 0]) / 255.f; - float r10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 0]) / 255.f; - float r01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 0]) / 255.f; - float r11 = static_cast(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(pixels[(tx0 + ty0 * w) * 4 + 1]) / 255.f; - float g10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 1]) / 255.f; - float g01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 1]) / 255.f; - float g11 = static_cast(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(pixels[(tx0 + ty0 * w) * 4 + 2]) / 255.f; - float b10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 2]) / 255.f; - float b01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 2]) / 255.f; - float b11 = static_cast(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(pixels[(tx0 + ty0 * w) * 4 + 3]) / 255.f; - float a10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 3]) / 255.f; - float a01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 3]) / 255.f; - float a11 = static_cast(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.c deleted file mode 100644 index cf4a748..0000000 --- a/src/stb_image.c +++ /dev/null @@ -1,3 +0,0 @@ -#define STB_IMAGE_IMPLEMENTATION -#define STBI_FAILURE_USERMSG -#include diff --git a/src/stb_image.cpp b/src/stb_image.cpp new file mode 100644 index 0000000..cf4a748 --- /dev/null +++ b/src/stb_image.cpp @@ -0,0 +1,3 @@ +#define STB_IMAGE_IMPLEMENTATION +#define STBI_FAILURE_USERMSG +#include -- cgit v1.2.3