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