diff options
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/engine.cpp | 205 | ||||
| -rw-r--r-- | src/math/utils.hpp | 23 | ||||
| -rw-r--r-- | src/o3d/mesh.cpp | 10 | ||||
| -rw-r--r-- | src/o3d/mesh.hpp | 4 | ||||
| -rw-r--r-- | src/o3d/polygon.hpp | 69 | ||||
| -rw-r--r-- | src/renderer.cpp | 4 | ||||
| -rw-r--r-- | src/shaders/simple_shaders.cpp | 16 | ||||
| -rw-r--r-- | src/shaders/simple_shaders.hpp | 29 | ||||
| -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 = ®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<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 |
