#include "geometry/normal_estimation.hpp" #include #include void estimate_normals( std::span vertices, std::span> triangles, std::vector& normals ) { normals.resize(vertices.size()); std::ranges::fill(normals, std::array{ 0.0f, 0.0f, 0.0f }); for (const auto& triangle : triangles) { auto abc = std::array{}; std::ranges::transform( triangle, abc.begin(), [&](const auto& index) { const auto& [ x, y, z ] = vertices[index]; return glm::vec3{ x, y, z }; } ); const auto [ A, B, C ] = abc; // TODO normalization can be done more efficiently const auto normal = glm::normalize(glm::vec3{ A.y * B.z - A.z * B.y, A.z * B.x - A.x * B.z, A.x * B.y - A.y * B.x }); const auto a_length = glm::length(B - C); const auto b_length = glm::length(C - A); const auto c_length = glm::length(A - B); const auto area = 0.25f * std::sqrt( (a_length + b_length + c_length) * (-a_length + b_length + c_length) * (a_length - b_length + c_length) * (a_length + b_length - c_length) ); const auto weighted_normal = normal * area; for (const auto& index : triangle) { auto& normal_avg = normals[index]; for (int i{}; i != 3; ++i) { normal_avg[i] += weighted_normal[i]; } } } using normal_component_type = components::mesh_vertex::normal::component_type; constexpr auto epsilon = std::numeric_limits::epsilon(); for (auto& [ x, y, z ] : normals) { const auto length = glm::length(glm::vec3{ x, y, z }); if (length <= epsilon) { x = 0; y = 1; z = 0; } else { const auto scale = 1.0f / length; x *= scale; y *= scale; z *= scale; } } }