Blender V5.0
mesh_primitive_uv_sphere.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <cmath>
6
7#include "BLI_math_numbers.hh"
8
9#include "BKE_attribute.hh"
10#include "BKE_mesh.hh"
11
13
14namespace blender::geometry {
15
17 const float radius_bottom,
18 const int segments,
19 const float height)
20{
21 const float radius = std::max(radius_top, radius_bottom);
22 const float delta_phi = (2.0f * math::numbers::pi) / float(segments);
23
24 const float x_max = radius;
25 const float x_min = std::cos(std::round(0.5f * segments) * delta_phi) * radius;
26 const float y_max = std::sin(std::round(0.25f * segments) * delta_phi) * radius;
27 const float y_min = -y_max;
28
29 const float3 bounds_min(x_min, y_min, -height);
30 const float3 bounds_max(x_max, y_max, height);
31
32 return {bounds_min, bounds_max};
33}
34
35static int sphere_vert_total(const int segments, const int rings)
36{
37 return segments * (rings - 1) + 2;
38}
39
40static int sphere_edge_total(const int segments, const int rings)
41{
42 return segments * (rings * 2 - 1);
43}
44
45static int sphere_corner_total(const int segments, const int rings)
46{
47 const int quad_corners = 4 * segments * (rings - 2);
48 const int tri_corners = 3 * segments * 2;
49 return quad_corners + tri_corners;
50}
51
52static int sphere_face_total(const int segments, const int rings)
53{
54 const int quads = segments * (rings - 2);
55 const int triangles = segments * 2;
56 return quads + triangles;
57}
58
64 MutableSpan<float3> vert_normals,
65 const float radius,
66 const int segments,
67 const int rings)
68{
69 const float delta_theta = math::numbers::pi / rings;
70 const float delta_phi = (2.0f * math::numbers::pi) / segments;
71
72 Array<float, 64> segment_cosines(segments + 1);
73 for (const int segment : IndexRange(1, segments)) {
74 const float phi = segment * delta_phi;
75 segment_cosines[segment] = std::cos(phi);
76 }
77 Array<float, 64> segment_sines(segments + 1);
78 for (const int segment : IndexRange(1, segments)) {
79 const float phi = segment * delta_phi;
80 segment_sines[segment] = std::sin(phi);
81 }
82
83 positions[0] = float3(0.0f, 0.0f, radius);
84 vert_normals.first() = float3(0.0f, 0.0f, 1.0f);
85
86 int vert_index = 1;
87 for (const int ring : IndexRange(1, rings - 1)) {
88 const float theta = ring * delta_theta;
89 const float sin_theta = std::sin(theta);
90 const float z = std::cos(theta);
91 for (const int segment : IndexRange(1, segments)) {
92 const float x = sin_theta * segment_cosines[segment];
93 const float y = sin_theta * segment_sines[segment];
94 positions[vert_index] = float3(x, y, z) * radius;
95 vert_normals[vert_index] = float3(x, y, z);
96 vert_index++;
97 }
98 }
99
100 positions.last() = float3(0.0f, 0.0f, -radius);
101 vert_normals.last() = float3(0.0f, 0.0f, -1.0f);
102}
103
105 const int segments,
106 const int rings)
107{
108 int edge_index = 0;
109
110 /* Add the edges connecting the top vertex to the first ring. */
111 const int first_vert_ring_index_start = 1;
112 for (const int segment : IndexRange(segments)) {
113 int2 &edge = edges[edge_index++];
114 edge[0] = 0;
115 edge[1] = first_vert_ring_index_start + segment;
116 }
117
118 int ring_vert_index_start = 1;
119 for (const int ring : IndexRange(rings - 1)) {
120 const int next_ring_vert_index_start = ring_vert_index_start + segments;
121
122 /* Add the edges running along each ring. */
123 for (const int segment : IndexRange(segments)) {
124 int2 &edge = edges[edge_index++];
125 edge[0] = ring_vert_index_start + segment;
126 edge[1] = ring_vert_index_start + ((segment + 1) % segments);
127 }
128
129 /* Add the edges connecting to the next ring. */
130 if (ring < rings - 2) {
131 for (const int segment : IndexRange(segments)) {
132 int2 &edge = edges[edge_index++];
133 edge[0] = ring_vert_index_start + segment;
134 edge[1] = next_ring_vert_index_start + segment;
135 }
136 }
137 ring_vert_index_start += segments;
138 }
139
140 /* Add the edges connecting the last ring to the bottom vertex. */
141 const int last_vert_index = sphere_vert_total(segments, rings) - 1;
142 const int last_vert_ring_start = last_vert_index - segments;
143 for (const int segment : IndexRange(segments)) {
144 int2 &edge = edges[edge_index++];
145 edge[0] = last_vert_index;
146 edge[1] = last_vert_ring_start + segment;
147 }
148}
149
150BLI_NOINLINE static void calculate_sphere_faces(MutableSpan<int> face_offsets, const int segments)
151{
152 MutableSpan<int> face_sizes = face_offsets.drop_back(1);
153 /* Add the triangles connected to the top vertex. */
154 face_sizes.take_front(segments).fill(3);
155 /* Add the middle quads. */
156 face_sizes.drop_front(segments).drop_back(segments).fill(4);
157 /* Add the triangles connected to the bottom vertex. */
158 face_sizes.take_back(segments).fill(3);
159
161}
162
164 MutableSpan<int> corner_edges,
165 const int segments,
166 const int rings)
167{
168 auto segment_next_or_first = [&](const int segment) {
169 return segment == segments - 1 ? 0 : segment + 1;
170 };
171
172 /* Add the triangles connected to the top vertex. */
173 const int first_vert_ring_start = 1;
174 for (const int segment : IndexRange(segments)) {
175 const int loop_start = segment * 3;
176 const int segment_next = segment_next_or_first(segment);
177
178 corner_verts[loop_start + 0] = 0;
179 corner_edges[loop_start + 0] = segment;
180
181 corner_verts[loop_start + 1] = first_vert_ring_start + segment;
182 corner_edges[loop_start + 1] = segments + segment;
183
184 corner_verts[loop_start + 2] = first_vert_ring_start + segment_next;
185 corner_edges[loop_start + 2] = segment_next;
186 }
187
188 const int rings_vert_start = 1;
189 const int rings_edge_start = segments;
190 const int rings_loop_start = segments * 3;
191 for (const int ring : IndexRange(1, rings - 2)) {
192 const int ring_vert_start = rings_vert_start + (ring - 1) * segments;
193 const int ring_edge_start = rings_edge_start + (ring - 1) * segments * 2;
194 const int ring_loop_start = rings_loop_start + (ring - 1) * segments * 4;
195
196 const int next_ring_vert_start = ring_vert_start + segments;
197 const int next_ring_edge_start = ring_edge_start + segments * 2;
198 const int ring_vertical_edge_start = ring_edge_start + segments;
199
200 for (const int segment : IndexRange(segments)) {
201 const int loop_start = ring_loop_start + segment * 4;
202 const int segment_next = segment_next_or_first(segment);
203
204 corner_verts[loop_start + 0] = ring_vert_start + segment;
205 corner_edges[loop_start + 0] = ring_vertical_edge_start + segment;
206
207 corner_verts[loop_start + 1] = next_ring_vert_start + segment;
208 corner_edges[loop_start + 1] = next_ring_edge_start + segment;
209
210 corner_verts[loop_start + 2] = next_ring_vert_start + segment_next;
211 corner_edges[loop_start + 2] = ring_vertical_edge_start + segment_next;
212
213 corner_verts[loop_start + 3] = ring_vert_start + segment_next;
214 corner_edges[loop_start + 3] = ring_edge_start + segment;
215 }
216 }
217
218 /* Add the triangles connected to the bottom vertex. */
219 const int bottom_loop_start = rings_loop_start + segments * (rings - 2) * 4;
220 const int last_edge_ring_start = segments * (rings - 2) * 2 + segments;
221 const int bottom_edge_fan_start = last_edge_ring_start + segments;
222 const int last_vert_index = sphere_vert_total(segments, rings) - 1;
223 const int last_vert_ring_start = last_vert_index - segments;
224 for (const int segment : IndexRange(segments)) {
225 const int loop_start = bottom_loop_start + segment * 3;
226 const int segment_next = segment_next_or_first(segment);
227
228 corner_verts[loop_start + 0] = last_vert_index;
229 corner_edges[loop_start + 0] = bottom_edge_fan_start + segment_next;
230
231 corner_verts[loop_start + 1] = last_vert_ring_start + segment_next;
232 corner_edges[loop_start + 1] = last_edge_ring_start + segment;
233
234 corner_verts[loop_start + 2] = last_vert_ring_start + segment;
235 corner_edges[loop_start + 2] = bottom_edge_fan_start + segment;
236 }
237}
238
240 const float segments,
241 const float rings,
242 const StringRef uv_map_id)
243{
244 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
245
248 MutableSpan<float2> uvs = uv_attribute.span;
249
250 const float dy = 1.0f / rings;
251
252 const float segments_inv = 1.0f / segments;
253
254 for (const int i_segment : IndexRange(segments)) {
255 const int loop_start = i_segment * 3;
256 const float segment = float(i_segment);
257 uvs[loop_start + 0] = float2((segment + 0.5f) * segments_inv, 0.0f);
258 uvs[loop_start + 1] = float2(segment * segments_inv, dy);
259 uvs[loop_start + 2] = float2((segment + 1.0f) * segments_inv, dy);
260 }
261
262 const int rings_loop_start = segments * 3;
263 for (const int i_ring : IndexRange(1, rings - 2)) {
264 const int ring_loop_start = rings_loop_start + (i_ring - 1) * segments * 4;
265 const float ring = float(i_ring);
266 for (const int i_segment : IndexRange(segments)) {
267 const int loop_start = ring_loop_start + i_segment * 4;
268 const float segment = float(i_segment);
269 uvs[loop_start + 0] = float2(segment * segments_inv, ring / rings);
270 uvs[loop_start + 1] = float2(segment * segments_inv, (ring + 1.0f) / rings);
271 uvs[loop_start + 2] = float2((segment + 1.0f) * segments_inv, (ring + 1.0f) / rings);
272 uvs[loop_start + 3] = float2((segment + 1.0f) * segments_inv, ring / rings);
273 }
274 }
275
276 const int bottom_loop_start = rings_loop_start + segments * (rings - 2) * 4;
277 for (const int i_segment : IndexRange(segments)) {
278 const int loop_start = bottom_loop_start + i_segment * 3;
279 const float segment = float(i_segment);
280 uvs[loop_start + 0] = float2((segment + 0.5f) * segments_inv, 1.0f);
281 uvs[loop_start + 1] = float2((segment + 1.0f) * segments_inv, 1.0f - dy);
282 uvs[loop_start + 2] = float2(segment * segments_inv, 1.0f - dy);
283 }
284
285 uv_attribute.finish();
286}
287
289 const int segments,
290 const int rings)
291{
292 const float delta_theta = math::numbers::pi / float(rings);
293 const float sin_equator = std::sin(std::round(0.5f * rings) * delta_theta);
294
295 return calculate_bounds_radial_primitive(0.0f, radius * sin_equator, segments, radius);
296}
297
298Mesh *create_uv_sphere_mesh(const float radius,
299 const int segments,
300 const int rings,
301 const std::optional<StringRef> uv_map_id)
302{
303 Mesh *mesh = BKE_mesh_new_nomain(sphere_vert_total(segments, rings),
304 sphere_edge_total(segments, rings),
305 sphere_face_total(segments, rings),
306 sphere_corner_total(segments, rings));
307 MutableSpan<float3> positions = mesh->vert_positions_for_write();
308 MutableSpan<int2> edges = mesh->edges_for_write();
309 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
310 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
311 MutableSpan<int> corner_edges = mesh->corner_edges_for_write();
312 bke::mesh_smooth_set(*mesh, false);
313
315 1024 < segments * rings,
316 [&]() {
317 Vector<float3> vert_normals(mesh->verts_num);
318 calculate_sphere_vertex_data(positions, vert_normals, radius, segments, rings);
319 bke::mesh_vert_normals_assign(*mesh, std::move(vert_normals));
320 },
321 [&]() { calculate_sphere_edge_indices(edges, segments, rings); },
322 [&]() { calculate_sphere_faces(face_offsets, segments); },
323 [&]() { calculate_sphere_corners(corner_verts, corner_edges, segments, rings); },
324 [&]() {
325 if (uv_map_id) {
326 calculate_sphere_uvs(mesh, segments, rings, *uv_map_id);
327 }
328 });
329
330 mesh->tag_loose_verts_none();
331 mesh->tag_loose_edges_none();
332 mesh->tag_overlapping_none();
333 mesh->bounds_set_eager(calculate_bounds_uv_sphere(radius, segments, rings));
334
335 return mesh;
336}
337
338} // namespace blender::geometry
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define BLI_NOINLINE
ccl_device_inline float delta_phi(const int p, const float gamma_o, const float gamma_t)
ccl_device_inline float sin_theta(const float3 w)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
constexpr MutableSpan take_back(const int64_t n) const
Definition BLI_span.hh:640
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:607
constexpr T & first() const
Definition BLI_span.hh:679
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:629
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
nullptr float
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void mesh_vert_normals_assign(Mesh &mesh, Span< float3 > vert_normals)
static int sphere_corner_total(const int segments, const int rings)
static BLI_NOINLINE void calculate_sphere_faces(MutableSpan< int > face_offsets, const int segments)
static int sphere_face_total(const int segments, const int rings)
Mesh * create_uv_sphere_mesh(float radius, int segments, int rings, std::optional< StringRef > uv_map_id)
static int sphere_vert_total(const int segments, const int rings)
static BLI_NOINLINE void calculate_sphere_corners(MutableSpan< int > corner_verts, MutableSpan< int > corner_edges, const int segments, const int rings)
static BLI_NOINLINE void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings, const StringRef uv_map_id)
static BLI_NOINLINE void calculate_sphere_vertex_data(MutableSpan< float3 > positions, MutableSpan< float3 > vert_normals, const float radius, const int segments, const int rings)
Bounds< float3 > calculate_bounds_radial_primitive(float radius_top, float radius_bottom, int segments, float height)
static int sphere_edge_total(const int segments, const int rings)
static BLI_NOINLINE void calculate_sphere_edge_indices(MutableSpan< int2 > edges, const int segments, const int rings)
static Bounds< float3 > calculate_bounds_uv_sphere(const float radius, const int segments, const int rings)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:221
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
int verts_num