1127{
1128 const uint32_t seg = std::max(radial_segments, 3U);
1129 const auto n_pts = static_cast<uint32_t>(std::max<size_t>(path.size(), 2));
1130
1131 std::vector<Kakshya::MeshVertex> verts;
1132 std::vector<uint32_t>
indices;
1133 verts.reserve(uint32_t(n_pts * (seg + 1) + (capped ? 2 * (seg + 1) : 0)));
1134 indices.reserve(uint32_t((n_pts - 1) * seg * 6 + (capped ? 2 * seg * 3 : 0)));
1135
1136 constexpr float k_min_segment = 1e-6F;
1137
1138 glm::vec3
tangent(0.0F, 0.0F, 1.0F);
1139 for (size_t i = 1; i < path.size(); ++i) {
1140 const glm::vec3 segment = path[i] - path[i - 1];
1141 if (glm::length(segment) > k_min_segment) {
1142 tangent = glm::normalize(segment);
1143 break;
1144 }
1145 }
1146
1147 glm::vec3 u_axis;
1148 if (std::abs(
tangent.y) < 0.9F) {
1149 u_axis = glm::normalize(glm::cross(
tangent, glm::vec3(0.0F, 1.0F, 0.0F)));
1150 } else {
1151 u_axis = glm::normalize(glm::cross(
tangent, glm::vec3(1.0F, 0.0F, 0.0F)));
1152 }
1153
1154 glm::vec3 v_axis = glm::normalize(glm::cross(
tangent, u_axis));
1155
1156 const float total_len = [&]() {
1157 float l = 0.0F;
1158 for (uint32_t i = 1; i < n_pts; ++i)
1159 l += glm::distance(path[i], path[i - 1]);
1160 return l;
1161 }();
1162
1163 float arc = 0.0F;
1164
1165 for (uint32_t pi = 0; pi < n_pts; ++pi) {
1166 if (pi > 0) {
1167 const glm::vec3 direction = (pi + 1 < n_pts)
1168 ? path[pi + 1] - path[pi - 1]
1169 : path[pi] - path[pi - 1];
1170 const glm::vec3 new_tan = glm::length(direction) > k_min_segment
1171 ? glm::normalize(direction)
1173 const glm::vec3 axis = glm::cross(
tangent, new_tan);
1174 const float axis_len = glm::length(axis);
1175 if (axis_len > 1e-6F) {
1176 const float angle = std::asin(glm::clamp(axis_len, 0.0F, 1.0F));
1177 const glm::mat4 rot = glm::rotate(glm::mat4(1.0F), angle,
1178 glm::normalize(axis));
1179 u_axis = glm::vec3(rot * glm::vec4(u_axis, 0.0F));
1180 v_axis = glm::vec3(rot * glm::vec4(v_axis, 0.0F));
1182 }
1183 arc += glm::distance(path[pi], path[pi - 1]);
1184 }
1185
1186 const float t = (total_len > 1e-6F) ? (arc / total_len) : 0.0F;
1187 const float r = radius_fn(t);
1188 const float angle_step = glm::two_pi<float>() / static_cast<float>(seg);
1189
1190 for (uint32_t s = 0; s <= seg; ++s) {
1191 const float a =
static_cast<float>(s) * angle_step;
1192 const float ca = std::cos(
a);
1193 const float sa = std::sin(
a);
1194 const glm::vec3
radial = ca * u_axis + sa * v_axis;
1195 verts.push_back({
1196 .position = path[pi] +
radial * r,
1197 .uv = { static_cast<float>(s) / static_cast<float>(seg), t },
1199 });
1200 }
1201 }
1202
1203 const uint32_t ring = seg + 1;
1204 for (uint32_t pi = 0; pi < n_pts - 1; ++pi) {
1205 for (uint32_t s = 0; s < seg; ++s) {
1206 const uint32_t
a = pi * ring + s;
1207 const uint32_t
b =
a + 1;
1208 const uint32_t c =
a + ring;
1209 const uint32_t d = c + 1;
1211 }
1212 }
1213
1214 if (capped) {
1215 for (int end = 0; end < 2; ++end) {
1216 const uint32_t ring_base = (end == 0) ? 0 : (n_pts - 1) * ring;
1217 const float t_cap = (end == 0) ? 0.0F : 1.0F;
1219 const glm::vec3 cap_pos = path[(end == 0) ? 0 : n_pts - 1];
1220
1221 const auto centre_idx = static_cast<uint32_t>(verts.size());
1222 verts.push_back({
1223 .position = cap_pos,
1224 .uv = { 0.5F, t_cap },
1225 .normal = cap_nrm,
1226 });
1227
1228 for (uint32_t s = 0; s < seg; ++s) {
1229 const uint32_t
a = ring_base + s;
1230 const uint32_t
b = ring_base + s + 1;
1231 if (end == 0) {
1233 } else {
1235 }
1236 }
1237 }
1238 }
1239
1240 auto data = Kakshya::MeshData::empty();
1241 Kakshya::MeshInsertion ins(data.vertex_variant, data.index_variant);
1242 ins.insert_flat(
1243 std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(verts.data()),
1244 verts.size() * sizeof(Kakshya::MeshVertex)),
1245 std::span<const uint32_t>(
indices),
1246 Kakshya::VertexLayout::for_meshes(sizeof(Kakshya::MeshVertex)));
1247 data.layout = Kakshya::VertexLayout::for_meshes(sizeof(Kakshya::MeshVertex));
1248 data.layout.vertex_count = static_cast<uint32_t>(verts.size());
1249 return data;
1250}
std::vector< uint32_t > indices