971{
972 const uint32_t seg = std::max(radial_segments, 3U);
973 const auto n_pts = static_cast<uint32_t>(std::max<size_t>(path.size(), 2));
974
975 std::vector<Kakshya::MeshVertex> verts;
976 std::vector<uint32_t> indices;
977 verts.reserve(uint32_t(n_pts * (seg + 1) + (capped ? 2 * (seg + 1) : 0)));
978 indices.reserve(uint32_t((n_pts - 1) * seg * 6 + (capped ? 2 * seg * 3 : 0)));
979
980 glm::vec3 tangent = glm::normalize(path[1] - path[0]);
981 glm::vec3 u_axis;
982 if (std::abs(tangent.y) < 0.9F) {
983 u_axis = glm::normalize(glm::cross(tangent, glm::vec3(0.0F, 1.0F, 0.0F)));
984 } else {
985 u_axis = glm::normalize(glm::cross(tangent, glm::vec3(1.0F, 0.0F, 0.0F)));
986 }
987
988 glm::vec3 v_axis = glm::normalize(glm::cross(tangent, u_axis));
989
990 const float total_len = [&]() {
991 float l = 0.0F;
992 for (uint32_t i = 1; i < n_pts; ++i)
993 l += glm::distance(path[i], path[i - 1]);
994 return l;
995 }();
996
997 float arc = 0.0F;
998
999 for (uint32_t pi = 0; pi < n_pts; ++pi) {
1000 if (pi > 0) {
1001 const glm::vec3 new_tan = (pi + 1 < n_pts)
1002 ? glm::normalize(path[pi + 1] - path[pi - 1])
1003 : glm::normalize(path[pi] - path[pi - 1]);
1004 const glm::vec3 axis = glm::cross(tangent, new_tan);
1005 const float axis_len = glm::length(axis);
1006 if (axis_len > 1e-6F) {
1007 const float angle = std::asin(glm::clamp(axis_len, 0.0F, 1.0F));
1008 const glm::mat4 rot = glm::rotate(glm::mat4(1.0F), angle,
1009 glm::normalize(axis));
1010 u_axis = glm::vec3(rot * glm::vec4(u_axis, 0.0F));
1011 v_axis = glm::vec3(rot * glm::vec4(v_axis, 0.0F));
1012 tangent = new_tan;
1013 }
1014 arc += glm::distance(path[pi], path[pi - 1]);
1015 }
1016
1017 const float t = (total_len > 1e-6F) ? (arc / total_len) : 0.0F;
1018 const float r = radius_fn(t);
1019 const float angle_step = glm::two_pi<float>() / static_cast<float>(seg);
1020
1021 for (uint32_t s = 0; s <= seg; ++s) {
1022 const float a =
static_cast<float>(s) * angle_step;
1023 const float ca = std::cos(
a);
1024 const float sa = std::sin(
a);
1025 const glm::vec3
radial = ca * u_axis + sa * v_axis;
1026 verts.push_back({
1027 .position = path[pi] +
radial * r,
1028 .uv = { static_cast<float>(s) / static_cast<float>(seg), t },
1030 });
1031 }
1032 }
1033
1034 const uint32_t ring = seg + 1;
1035 for (uint32_t pi = 0; pi < n_pts - 1; ++pi) {
1036 for (uint32_t s = 0; s < seg; ++s) {
1037 const uint32_t
a = pi * ring + s;
1038 const uint32_t
b =
a + 1;
1039 const uint32_t c =
a + ring;
1040 const uint32_t d = c + 1;
1041 indices.insert(indices.end(), {
a,
b, c,
b, d, c });
1042 }
1043 }
1044
1045 if (capped) {
1046 for (int end = 0; end < 2; ++end) {
1047 const uint32_t ring_base = (end == 0) ? 0 : (n_pts - 1) * ring;
1048 const float t_cap = (end == 0) ? 0.0F : 1.0F;
1049 const glm::vec3 cap_nrm = (end == 0) ? -tangent : tangent;
1050 const glm::vec3 cap_pos = path[(end == 0) ? 0 : n_pts - 1];
1051
1052 const auto centre_idx = static_cast<uint32_t>(verts.size());
1053 verts.push_back({
1054 .position = cap_pos,
1055 .uv = { 0.5F, t_cap },
1056 .normal = cap_nrm,
1057 });
1058
1059 for (uint32_t s = 0; s < seg; ++s) {
1060 const uint32_t
a = ring_base + s;
1061 const uint32_t
b = ring_base + s + 1;
1062 if (end == 0) {
1063 indices.insert(indices.end(), { centre_idx,
b,
a });
1064 } else {
1065 indices.insert(indices.end(), { centre_idx,
a,
b });
1066 }
1067 }
1068 }
1069 }
1070
1071 auto data = Kakshya::MeshData::empty();
1072 Kakshya::MeshInsertion ins(data.vertex_variant, data.index_variant);
1073 ins.insert_flat(
1074 std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(verts.data()),
1075 verts.size() * sizeof(Kakshya::MeshVertex)),
1076 std::span<const uint32_t>(indices),
1077 Kakshya::VertexLayout::for_meshes(sizeof(Kakshya::MeshVertex)));
1078 data.layout = Kakshya::VertexLayout::for_meshes(sizeof(Kakshya::MeshVertex));
1079 data.layout.vertex_count = static_cast<uint32_t>(verts.size());
1080 return data;
1081}