Blender V4.3
mesh_primitive_cuboid.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
23
25 : size(size),
29 edges_x(verts_x - 1),
30 edges_y(verts_y - 1),
31 edges_z(verts_z - 1)
32 {
33 BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0);
34 this->vertex_count = this->get_vertex_count();
35 this->face_count = this->get_face_count();
36 this->loop_count = this->face_count * 4;
37 }
38
39 private:
40 int get_vertex_count()
41 {
42 const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2);
43 return verts_x * verts_y * verts_z - inner_position_count;
44 }
45
46 int get_face_count()
47 {
48 return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x);
49 }
50};
51
52static void calculate_positions(const CuboidConfig &config, MutableSpan<float3> positions)
53{
54 const float z_bottom = -config.size.z / 2.0f;
55 const float z_delta = config.size.z / config.edges_z;
56
57 const float x_left = -config.size.x / 2.0f;
58 const float x_delta = config.size.x / config.edges_x;
59
60 const float y_front = -config.size.y / 2.0f;
61 const float y_delta = config.size.y / config.edges_y;
62
63 int vert_index = 0;
64
65 for (const int z : IndexRange(config.verts_z)) {
66 if (ELEM(z, 0, config.edges_z)) {
67 /* Fill bottom and top. */
68 const float z_pos = z_bottom + z_delta * z;
69 for (const int y : IndexRange(config.verts_y)) {
70 const float y_pos = y_front + y_delta * y;
71 for (const int x : IndexRange(config.verts_x)) {
72 const float x_pos = x_left + x_delta * x;
73 positions[vert_index++] = float3(x_pos, y_pos, z_pos);
74 }
75 }
76 }
77 else {
78 for (const int y : IndexRange(config.verts_y)) {
79 if (ELEM(y, 0, config.edges_y)) {
80 /* Fill y-sides. */
81 const float y_pos = y_front + y_delta * y;
82 const float z_pos = z_bottom + z_delta * z;
83 for (const int x : IndexRange(config.verts_x)) {
84 const float x_pos = x_left + x_delta * x;
85 positions[vert_index++] = float3(x_pos, y_pos, z_pos);
86 }
87 }
88 else {
89 /* Fill x-sides. */
90 const float x_pos = x_left;
91 const float y_pos = y_front + y_delta * y;
92 const float z_pos = z_bottom + z_delta * z;
93 positions[vert_index++] = float3(x_pos, y_pos, z_pos);
94 const float x_pos2 = x_left + x_delta * config.edges_x;
95 positions[vert_index++] = float3(x_pos2, y_pos, z_pos);
96 }
97 }
98 }
99 }
100}
101
102/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left.
103 * Hence they are passed as 1,4,3,2 when calculating faces clockwise, and 1,2,3,4 for
104 * anti-clockwise.
105 */
106static void define_quad(MutableSpan<int> corner_verts,
107 const int loop_index,
108 const int vert_1,
109 const int vert_2,
110 const int vert_3,
111 const int vert_4)
112{
113 corner_verts[loop_index] = vert_1;
114 corner_verts[loop_index + 1] = vert_2;
115 corner_verts[loop_index + 2] = vert_3;
116 corner_verts[loop_index + 3] = vert_4;
117}
118
119static void calculate_corner_verts(const CuboidConfig &config, MutableSpan<int> corner_verts)
120{
121 int loop_index = 0;
122
123 /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */
124 const int xy_cross_section_vert_count = config.verts_x * config.verts_y -
125 (config.verts_x - 2) * (config.verts_y - 2);
126
127 /* Calculate faces for Bottom faces. */
128 int vert_1_start = 0;
129
130 for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
131 for (const int x : IndexRange(config.edges_x)) {
132 const int vert_1 = vert_1_start + x;
133 const int vert_2 = vert_1_start + config.verts_x + x;
134 const int vert_3 = vert_2 + 1;
135 const int vert_4 = vert_1 + 1;
136
137 define_quad(corner_verts, loop_index, vert_1, vert_2, vert_3, vert_4);
138 loop_index += 4;
139 }
140 vert_1_start += config.verts_x;
141 }
142
143 /* Calculate faces for Front faces. */
144 vert_1_start = 0;
145 int vert_2_start = config.verts_x * config.verts_y;
146
147 for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) {
148 for (const int x : IndexRange(config.edges_x)) {
149 define_quad(corner_verts,
150 loop_index,
151 vert_1_start + x,
152 vert_1_start + x + 1,
153 vert_2_start + x + 1,
154 vert_2_start + x);
155 loop_index += 4;
156 }
157 vert_1_start = vert_2_start;
158 vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2);
159 }
160
161 /* Calculate faces for Top faces. */
162 vert_1_start = config.verts_x * config.verts_y +
163 (config.verts_z - 2) * (config.verts_x * config.verts_y -
164 (config.verts_x - 2) * (config.verts_y - 2));
165 vert_2_start = vert_1_start + config.verts_x;
166
167 for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
168 for (const int x : IndexRange(config.edges_x)) {
169 define_quad(corner_verts,
170 loop_index,
171 vert_1_start + x,
172 vert_1_start + x + 1,
173 vert_2_start + x + 1,
174 vert_2_start + x);
175 loop_index += 4;
176 }
177 vert_2_start += config.verts_x;
178 vert_1_start += config.verts_x;
179 }
180
181 /* Calculate faces for Back faces. */
182 vert_1_start = config.verts_x * config.edges_y;
183 vert_2_start = vert_1_start + xy_cross_section_vert_count;
184
185 for (const int z : IndexRange(config.edges_z)) {
186 if (z == (config.edges_z - 1)) {
187 vert_2_start += (config.verts_x - 2) * (config.verts_y - 2);
188 }
189 for (const int x : IndexRange(config.edges_x)) {
190 define_quad(corner_verts,
191 loop_index,
192 vert_1_start + x,
193 vert_2_start + x,
194 vert_2_start + x + 1,
195 vert_1_start + x + 1);
196 loop_index += 4;
197 }
198 vert_2_start += xy_cross_section_vert_count;
199 vert_1_start += xy_cross_section_vert_count;
200 }
201
202 /* Calculate faces for Left faces. */
203 vert_1_start = 0;
204 vert_2_start = config.verts_x * config.verts_y;
205
206 for (const int z : IndexRange(config.edges_z)) {
207 for (const int y : IndexRange(config.edges_y)) {
208 int vert_1;
209 int vert_2;
210 int vert_3;
211 int vert_4;
212
213 if (z == 0 || y == 0) {
214 vert_1 = vert_1_start + config.verts_x * y;
215 vert_4 = vert_1 + config.verts_x;
216 }
217 else {
218 vert_1 = vert_1_start + 2 * y;
219 vert_1 += config.verts_x - 2;
220 vert_4 = vert_1 + 2;
221 }
222
223 if (y == 0 || z == (config.edges_z - 1)) {
224 vert_2 = vert_2_start + config.verts_x * y;
225 vert_3 = vert_2 + config.verts_x;
226 }
227 else {
228 vert_2 = vert_2_start + 2 * y;
229 vert_2 += config.verts_x - 2;
230 vert_3 = vert_2 + 2;
231 }
232
233 define_quad(corner_verts, loop_index, vert_1, vert_2, vert_3, vert_4);
234 loop_index += 4;
235 }
236 if (z == 0) {
237 vert_1_start += config.verts_x * config.verts_y;
238 }
239 else {
240 vert_1_start += xy_cross_section_vert_count;
241 }
242 vert_2_start += xy_cross_section_vert_count;
243 }
244
245 /* Calculate faces for Right faces. */
246 vert_1_start = config.edges_x;
247 vert_2_start = vert_1_start + config.verts_x * config.verts_y;
248
249 for (const int z : IndexRange(config.edges_z)) {
250 for (const int y : IndexRange(config.edges_y)) {
251 int vert_1 = vert_1_start;
252 int vert_2 = vert_2_start;
253 int vert_3 = vert_2_start + 2;
254 int vert_4 = vert_1 + config.verts_x;
255
256 if (z == 0) {
257 vert_1 = vert_1_start + config.verts_x * y;
258 vert_4 = vert_1 + config.verts_x;
259 }
260 else {
261 vert_1 = vert_1_start + 2 * y;
262 vert_4 = vert_1 + 2;
263 }
264
265 if (z == (config.edges_z - 1)) {
266 vert_2 = vert_2_start + config.verts_x * y;
267 vert_3 = vert_2 + config.verts_x;
268 }
269 else {
270 vert_2 = vert_2_start + 2 * y;
271 vert_3 = vert_2 + 2;
272 }
273
274 if (y == (config.edges_y - 1)) {
275 vert_3 = vert_2 + config.verts_x;
276 vert_4 = vert_1 + config.verts_x;
277 }
278
279 define_quad(corner_verts, loop_index, vert_1, vert_4, vert_3, vert_2);
280 loop_index += 4;
281 }
282 if (z == 0) {
283 vert_1_start += config.verts_x * config.verts_y;
284 }
285 else {
286 vert_1_start += xy_cross_section_vert_count;
287 }
288 vert_2_start += xy_cross_section_vert_count;
289 }
290}
291
292static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const StringRef uv_id)
293{
294 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
295 bke::SpanAttributeWriter uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
297 MutableSpan<float2> uvs = uv_attribute.span;
298
299 int loop_index = 0;
300
301 const float x_delta = 0.25f / float(config.edges_x);
302 const float y_delta = 0.25f / float(config.edges_y);
303 const float z_delta = 0.25f / float(config.edges_z);
304
305 /* Calculate bottom face UVs. */
306 for (const int y : IndexRange(config.edges_y)) {
307 for (const int x : IndexRange(config.edges_x)) {
308 uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta);
309 uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta);
310 uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta);
311 uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta);
312 }
313 }
314
315 /* Calculate front face UVs. */
316 for (const int z : IndexRange(config.edges_z)) {
317 for (const int x : IndexRange(config.edges_x)) {
318 uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta);
319 uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta);
320 uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta);
321 uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta);
322 }
323 }
324
325 /* Calculate top face UVs. */
326 for (const int y : IndexRange(config.edges_y)) {
327 for (const int x : IndexRange(config.edges_x)) {
328 uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta);
329 uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta);
330 uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta);
331 uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta);
332 }
333 }
334
335 /* Calculate back face UVs. */
336 for (const int z : IndexRange(config.edges_z)) {
337 for (const int x : IndexRange(config.edges_x)) {
338 uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta);
339 uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta);
340 uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta);
341 uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta);
342 }
343 }
344
345 /* Calculate left face UVs. */
346 for (const int z : IndexRange(config.edges_z)) {
347 for (const int y : IndexRange(config.edges_y)) {
348 uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta);
349 uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta);
350 uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta);
351 uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta);
352 }
353 }
354
355 /* Calculate right face UVs. */
356 for (const int z : IndexRange(config.edges_z)) {
357 for (const int y : IndexRange(config.edges_y)) {
358 uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta);
359 uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta);
360 uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta);
361 uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta);
362 }
363 }
364
365 uv_attribute.finish();
366}
367
369 const int verts_x,
370 const int verts_y,
371 const int verts_z,
372 const std::optional<StringRef> &uv_id)
373{
374 const CuboidConfig config(size, verts_x, verts_y, verts_z);
375
376 Mesh *mesh = BKE_mesh_new_nomain(config.vertex_count, 0, config.face_count, config.loop_count);
377 MutableSpan<float3> positions = mesh->vert_positions_for_write();
378 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
379 bke::mesh_smooth_set(*mesh, false);
380
381 calculate_positions(config, positions);
382 offset_indices::fill_constant_group_size(4, 0, mesh->face_offsets_for_write());
383 calculate_corner_verts(config, corner_verts);
384 bke::mesh_calc_edges(*mesh, false, false);
385
386 if (uv_id) {
387 calculate_uvs(config, mesh, *uv_id);
388 }
389
390 const float3 bounds = size * 0.5f;
391 mesh->bounds_set_eager({-bounds, bounds});
392 mesh->tag_loose_verts_none();
393 mesh->tag_overlapping_none();
394
395 return mesh;
396}
397
399 const int verts_x,
400 const int verts_y,
401 const int verts_z)
402{
403 return create_cuboid_mesh(size, verts_x, verts_y, verts_z, {});
404}
405
406} // namespace blender::geometry
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ELEM(...)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
draw_view in_light_buf[] float
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
static void calculate_positions(const CuboidConfig &config, MutableSpan< float3 > positions)
static void define_quad(MutableSpan< int > corner_verts, const int loop_index, const int vert_1, const int vert_2, const int vert_3, const int vert_4)
Mesh * create_cuboid_mesh(const float3 &size, int verts_x, int verts_y, int verts_z, const std::optional< StringRef > &uv_id)
static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const StringRef uv_id)
static void calculate_corner_verts(const CuboidConfig &config, MutableSpan< int > corner_verts)
void fill_constant_group_size(int size, int start_offset, MutableSpan< int > offsets)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z)