5#include <glm/gtc/constants.hpp>
11static inline void make_basis(
const Vec3 &dir,
Vec3 &u,
Vec3 &v) {
12 Vec3 arbitrary = (std::abs(glm::dot(dir,
Vec3(0.0f, 1.0f, 0.0f))) < 0.999f)
13 ?
Vec3(0.0f, 1.0f, 0.0f)
14 :
Vec3(1.0f, 0.0f, 0.0f);
15 u = glm::normalize(glm::cross(arbitrary, dir));
16 v = glm::cross(dir, u);
22 segments = std::max(3, segments);
29 int n_rings = std::max(2, segments);
30 int verts_per_ring = segments;
32 uint32_t north_idx = 0;
36 for (
int ring = 1; ring < n_rings; ++ring) {
37 float phi = glm::pi<float>() *
static_cast<float>(ring) /
38 static_cast<float>(segments);
39 float y = radius * std::cos(phi);
40 float r = radius * std::sin(phi);
42 float step = glm::two_pi<float>() /
static_cast<float>(verts_per_ring);
43 for (
int j = 0; j < verts_per_ring; ++j) {
44 float theta = step *
static_cast<float>(j);
46 center +
Vec3(r * std::cos(theta), y, r * std::sin(theta)));
51 uint32_t south_idx =
static_cast<uint32_t
>(m.
vertices.size());
52 m.
vertices.push_back(center +
Vec3(0.0f, -radius, 0.0f));
56 for (
size_t i = 0; i < m.
vertices.size(); ++i) {
61 for (
int j = 0; j < verts_per_ring; ++j) {
62 uint32_t j_next = 1 +
static_cast<uint32_t
>((j + 1) % verts_per_ring);
63 m.
triangles.push_back({north_idx, j_next, 1 +
static_cast<uint32_t
>(j)});
67 for (
int ring = 0; ring < n_rings - 2; ++ring) {
68 uint32_t ring_start = 1 +
static_cast<uint32_t
>(ring) * verts_per_ring;
70 1 +
static_cast<uint32_t
>(ring + 1) * verts_per_ring;
72 for (
int j = 0; j < verts_per_ring; ++j) {
73 uint32_t a = ring_start + j;
74 uint32_t b = ring_start + (j + 1) % verts_per_ring;
75 uint32_t c = next_start + (j + 1) % verts_per_ring;
76 uint32_t d = next_start + j;
85 1 +
static_cast<uint32_t
>(n_rings - 2) * verts_per_ring;
86 for (
int j = 0; j < verts_per_ring; ++j) {
87 uint32_t j_next = last_start + (j + 1) % verts_per_ring;
88 m.
triangles.push_back({south_idx, last_start + j, j_next});
96 int segments,
const Color &color) {
97 segments = std::max(3, segments);
100 Vec3 dir = glm::normalize(end - start);
102 make_basis(dir, uu, vv);
105 std::vector<Vec3> radials(segments);
106 std::vector<Vec3> bottom_ring(segments);
107 std::vector<Vec3> top_ring(segments);
109 float step = glm::two_pi<float>() /
static_cast<float>(segments);
110 for (
int i = 0; i < segments; ++i) {
111 float a = step *
static_cast<float>(i);
112 radials[i] = std::cos(a) * uu + std::sin(a) * vv;
113 bottom_ring[i] = start + radius * radials[i];
114 top_ring[i] = end + radius * radials[i];
118 for (
int i = 0; i < segments; ++i) {
119 m.
vertices.push_back(bottom_ring[i]);
120 m.
normals.push_back(radials[i]);
121 m.
colors.push_back(color);
123 for (
int i = 0; i < segments; ++i) {
125 m.
normals.push_back(radials[i]);
126 m.
colors.push_back(color);
129 for (
int i = 0; i < segments; ++i) {
131 uint32_t b = (i + 1) % segments;
132 uint32_t c = segments + (i + 1) % segments;
133 uint32_t d = segments + i;
141 uint32_t bottom_cap_offset =
static_cast<uint32_t
>(m.
vertices.size());
146 m.
colors.push_back(color);
149 for (
int i = 0; i < segments; ++i) {
150 m.
vertices.push_back(bottom_ring[i]);
152 m.
colors.push_back(color);
155 for (
int i = 0; i < segments; ++i) {
156 uint32_t center_idx = bottom_cap_offset;
157 uint32_t edge_idx = bottom_cap_offset + 1 + i;
158 uint32_t next_edge_idx = bottom_cap_offset + 1 + ((i + 1) % segments);
161 m.
triangles.push_back({center_idx, next_edge_idx, edge_idx});
165 uint32_t top_cap_offset =
static_cast<uint32_t
>(m.
vertices.size());
170 m.
colors.push_back(color);
173 for (
int i = 0; i < segments; ++i) {
176 m.
colors.push_back(color);
179 for (
int i = 0; i < segments; ++i) {
180 uint32_t center_idx = top_cap_offset;
181 uint32_t edge_idx = top_cap_offset + 1 + i;
182 uint32_t next_edge_idx = top_cap_offset + 1 + ((i + 1) % segments);
185 m.
triangles.push_back({center_idx, edge_idx, next_edge_idx});
193 int segments,
const Color &color) {
194 segments = std::max(3, segments);
197 Vec3 dir_vec = tip - base;
198 float height = glm::length(dir_vec);
201 Vec3 dir = (height > 1e-8f) ? (dir_vec / height) :
Vec3(0, 1, 0);
204 make_basis(dir, uu, vv);
206 float step = glm::two_pi<float>() /
static_cast<float>(segments);
209 uint32_t body_offset = 0;
210 for (
int i = 0; i < segments; ++i) {
211 float a = step *
static_cast<float>(i);
212 Vec3 radial = std::cos(a) * uu + std::sin(a) * vv;
215 Vec3 slope_normal = glm::normalize(radial * height + dir * radius);
218 m.
vertices.push_back(base + radius * radial);
219 m.
normals.push_back(slope_normal);
220 m.
colors.push_back(color);
224 m.
normals.push_back(slope_normal);
225 m.
colors.push_back(color);
228 for (
int i = 0; i < segments; ++i) {
229 uint32_t base_idx = body_offset + i * 2;
230 uint32_t tip_idx = body_offset + i * 2 + 1;
231 uint32_t next_base_idx = body_offset + ((i + 1) % segments) * 2;
234 m.
triangles.push_back({base_idx, next_base_idx, tip_idx});
238 uint32_t cap_offset =
static_cast<uint32_t
>(m.
vertices.size());
243 m.
colors.push_back(color);
246 for (
int i = 0; i < segments; ++i) {
247 float a = step *
static_cast<float>(i);
248 Vec3 radial = std::cos(a) * uu + std::sin(a) * vv;
250 m.
vertices.push_back(base + radius * radial);
252 m.
colors.push_back(color);
255 for (
int i = 0; i < segments; ++i) {
256 uint32_t center_idx = cap_offset;
257 uint32_t edge_idx = cap_offset + 1 + i;
258 uint32_t next_edge_idx = cap_offset + 1 + ((i + 1) % segments);
261 m.
triangles.push_back({center_idx, next_edge_idx, edge_idx});
269 float head_radius,
float head_length,
int segments,
270 const Color &color) {
271 Vec3 dir_vec = to - from;
272 float total_len = glm::length(dir_vec);
273 if (total_len < 1e-8f)
276 Vec3 dir = dir_vec / total_len;
277 float hl = std::min(head_length, total_len * 0.8f);
279 Vec3 head_base = to - hl * dir;
289 uint32_t offset =
static_cast<uint32_t
>(dst.
vertices.size());
297 {tri.v0 + offset, tri.v1 + offset, tri.v2 + offset});
302 const std::vector<float> &radii,
303 const std::vector<Color> &colors,
306 size_t n = centers.size();
307 for (
size_t i = 0; i < n; ++i) {
308 float r = (i < radii.size()) ? radii[i] : radii[0];
309 Color c = (i < colors.size()) ? colors[i] : colors[0];
317 const std::vector<Vec3> &ends,
318 const std::vector<float> &radii,
319 const std::vector<Color> &colors,
322 size_t n = starts.size();
323 for (
size_t i = 0; i < n; ++i) {
324 float r = (i < radii.size()) ? radii[i] : radii[0];
325 Color c = (i < colors.size()) ? colors[i] : colors[0];
333 const Color &color) {
339 m.
colors.assign(24, color);
342 float x = half.x, y = half.y, z = half.z;
346 center +
Vec3(-x, -y, -z),
347 center +
Vec3( x, -y, -z),
348 center +
Vec3( x, y, -z),
349 center +
Vec3(-x, y, -z),
350 center +
Vec3(-x, -y, z),
351 center +
Vec3( x, -y, z),
352 center +
Vec3( x, y, z),
353 center +
Vec3(-x, y, z),
363 {4, 5, 6, 7,
Vec3( 0, 0, 1)},
364 {1, 0, 3, 2,
Vec3( 0, 0, -1)},
365 {0, 1, 5, 4,
Vec3( 0, -1, 0)},
366 {7, 6, 2, 3,
Vec3( 0, 1, 0)},
367 {1, 2, 6, 5,
Vec3( 1, 0, 0)},
368 {0, 4, 7, 3,
Vec3(-1, 0, 0)}
372 for (
int i = 0; i < 6; i++) {
374 m.
vertices.push_back(v[faces[i].v0]);
375 m.
vertices.push_back(v[faces[i].v1]);
376 m.
vertices.push_back(v[faces[i].v2]);
377 m.
vertices.push_back(v[faces[i].v3]);
380 for (
int j = 0; j < 4; j++) {
381 m.
normals.push_back(faces[i].normal);
385 m.
triangles.push_back({index, index + 1, index + 2});
386 m.
triangles.push_back({index, index + 2, index + 3});
396 float hw,
const Color &color) {
402 m.
colors.assign(16, color);
406 Vec3 p0 = base_center +
Vec3(-hw, 0, -hw);
407 Vec3 p1 = base_center +
Vec3( hw, 0, -hw);
408 Vec3 p2 = base_center +
Vec3( hw, 0, hw);
409 Vec3 p3 = base_center +
Vec3(-hw, 0, hw);
412 std::vector<std::array<Vec3, 3>> side_faces = {
420 for (
const auto& face : side_faces) {
421 Vec3 A = face[0], B = face[1], C = face[2];
424 Vec3 normal = glm::normalize(glm::cross(B - A, C - A));
435 m.
triangles.push_back({index, index + 1, index + 2});
440 Vec3 base_normal =
Vec3(0.0f, -1.0f, 0.0f);
447 for (
int i = 0; i < 4; i++) {
448 m.
normals.push_back(base_normal);
452 m.
triangles.push_back({index, index + 2, index + 3});
453 m.
triangles.push_back({index, index + 1, index + 2});
460 const Color &color) {
466 m.
colors.assign(12, color);
469 Vec3 centroid = (p0 + p1 + p2 + p3) * 0.25f;
471 std::vector<std::array<Vec3, 3>> faces = {
472 {p0, p1, p2}, {p0, p2, p3}, {p0, p3, p1}, {p1, p3, p2}
476 for (
const auto& face : faces) {
477 Vec3 A = face[0], B = face[1], C = face[2];
478 Vec3 normal = glm::normalize(glm::cross(B - A, C - A));
481 if (glm::dot(normal, A - centroid) < 0.0f) {
496 m.
triangles.push_back({index, index + 1, index + 2});
505 int seg_major,
int seg_minor,
const Color &color) {
509 int total_vertices = seg_major * seg_minor;
511 m.
normals.reserve(total_vertices);
512 m.
colors.reserve(total_vertices);
516 for (
int i = 0; i < seg_major; i++) {
517 float phi = glm::two_pi<float>() * float(i) / float(seg_major);
518 float cp = std::cos(phi), sp = std::sin(phi);
521 Vec3 minor_center = center +
Vec3(R * cp, 0.0f, R * sp);
523 for (
int j = 0; j < seg_minor; j++) {
524 float theta = glm::two_pi<float>() * float(j) / float(seg_minor);
525 float ct = std::cos(theta), st = std::sin(theta);
527 float px = (R + r * ct) * cp;
529 float pz = (R + r * ct) * sp;
531 Vec3 pos = center +
Vec3(px, py, pz);
533 m.
colors.push_back(color);
536 m.
normals.push_back(glm::normalize(pos - minor_center));
541 for (
int i = 0; i < seg_major; i++) {
542 int ni = (i + 1) % seg_major;
544 for (
int j = 0; j < seg_minor; j++) {
545 int nj = (j + 1) % seg_minor;
547 uint32_t a = i * seg_minor + j;
548 uint32_t b = ni * seg_minor + j;
549 uint32_t c = ni * seg_minor + nj;
550 uint32_t d = i * seg_minor + nj;
562 float hx,
float hy,
const Color &color) {
564 Vec3 n = glm::length(normal) > 1e-9f ? glm::normalize(normal) :
Vec3(0,0,1);
566 if (std::abs(n.x) < 0.9f) u = glm::normalize(glm::cross(n,
Vec3(1,0,0)));
567 else u = glm::normalize(glm::cross(n,
Vec3(0,1,0)));
568 v = glm::cross(n, u);
571 Vec3 p0 = center - u * hx - v * hy;
572 Vec3 p1 = center + u * hx - v * hy;
573 Vec3 p2 = center + u * hx + v * hy;
574 Vec3 p3 = center - u * hx + v * hy;
583 for (
int i = 0; i < 4; i++) {
585 m.
colors.push_back(color);
593 for (
int i = 0; i < 4; i++) {
595 m.
colors.push_back(color);
Low-level math utilities for the rendering pipeline.
Mesh generate_pyramid(const Vec3 &base_center, const Vec3 &apex, float hw, const Color &color)
Generate a square-based pyramid.
Mesh generate_tetrahedron(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Color &color)
Generate a tetrahedron (triangular pyramid) from four points.
Mesh generate_sphere(const Vec3 ¢er, float radius, int segments, const Color &color)
Generate a UV-sphere (latitude/longitude tessellation).
glm::vec3 Vec3
3-component floating-point vector (xyz).
Mesh generate_cuboid(const Vec3 ¢er, const Vec3 &half, const Color &color)
Generate an axis-aligned cuboid (rectangular box).
void merge_mesh(Mesh &dst, const Mesh &src)
Merge one mesh into another (append geometry).
Mesh generate_arrow(const Vec3 &from, const Vec3 &to, float shaft_radius, float head_radius, float head_length, int segments, const Color &color)
Generate a 3D arrow from from to to.
Mesh generate_cylinder(const Vec3 &start, const Vec3 &end, float radius, int segments, const Color &color)
Generate a cylinder between two endpoints.
Mesh generate_torus(const Vec3 ¢er, float R, float r, int seg_major, int seg_minor, const Color &color)
Generate a torus (donut shape).
Mesh generate_multi_spheres(const std::vector< Vec3 > ¢ers, const std::vector< float > &radii, const std::vector< Color > &colors, int segments)
Generate multiple spheres in a single mesh (efficient batching).
Mesh generate_plane(const Vec3 ¢er, const Vec3 &normal, float hx, float hy, const Color &color)
Generate a flat rectangular plane.
Mesh generate_cone(const Vec3 &base, const Vec3 &tip, float radius, int segments, const Color &color)
Generate a cone from a base circle to a tip point.
Mesh generate_multi_cylinders(const std::vector< Vec3 > &starts, const std::vector< Vec3 > &ends, const std::vector< float > &radii, const std::vector< Color > &colors, int segments)
Generate multiple cylinders in a single mesh (efficient batching).
Compute per-vertex surface normals for lighting.
Procedural geometry generators.
An RGBA color with floating-point components.
A 3D triangle mesh using an indexed face set representation.
std::vector< Color > colors
Per-vertex RGBA colors.
std::vector< Vec3 > normals
Per-vertex surface normals (unit-length direction vectors).
std::vector< Vec3 > vertices
3D vertex positions.
std::vector< Triangle > triangles
Triangle index triplets.