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