Blender V5.0
mesh_runtime.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_array_utils.hh"
10#include "BLI_math_geom.h"
11
13#include "BKE_bvhutils.hh"
14#include "BKE_customdata.hh"
15#include "BKE_editmesh_cache.hh"
16#include "BKE_lib_id.hh"
17#include "BKE_mesh.hh"
18#include "BKE_mesh_mapping.hh"
19#include "BKE_mesh_runtime.hh"
20#include "BKE_shrinkwrap.hh"
21#include "BKE_subdiv_ccg.hh"
22
23using blender::float3;
25using blender::Span;
26
27/* -------------------------------------------------------------------- */
30
31namespace blender::bke {
32
33static void free_mesh_eval(MeshRuntime &mesh_runtime)
34{
35 if (mesh_runtime.mesh_eval != nullptr) {
36 BKE_id_free(nullptr, mesh_runtime.mesh_eval);
37 mesh_runtime.mesh_eval = nullptr;
38 }
39}
40
41static void free_batch_cache(MeshRuntime &mesh_runtime)
42{
43 if (mesh_runtime.batch_cache) {
45 mesh_runtime.batch_cache = nullptr;
46 }
47}
48
49static void free_bvh_caches(MeshRuntime &mesh_runtime)
50{
51 mesh_runtime.bvh_cache_verts.tag_dirty();
52 mesh_runtime.bvh_cache_edges.tag_dirty();
53 mesh_runtime.bvh_cache_faces.tag_dirty();
54 mesh_runtime.bvh_cache_corner_tris.tag_dirty();
56 mesh_runtime.bvh_cache_loose_verts.tag_dirty();
58 mesh_runtime.bvh_cache_loose_edges.tag_dirty();
60}
61
62MeshRuntime::MeshRuntime() = default;
63
69
70static int reset_bits_and_count(MutableBitSpan bits, const Span<int> indices_to_reset)
71{
72 int count = bits.size();
73 for (const int i : indices_to_reset) {
74 if (bits[i]) {
75 bits[i].reset();
76 count--;
77 }
78 }
79 return count;
80}
81
82static void bit_vector_with_reset_bits_or_empty(const Span<int> indices_to_reset,
83 const int indexed_elems_num,
84 BitVector<> &r_bits,
85 int &r_count)
86{
87 r_bits.resize(0);
88 r_bits.resize(indexed_elems_num, true);
89 r_count = reset_bits_and_count(r_bits, indices_to_reset);
90 if (r_count == 0) {
91 r_bits.clear_and_shrink();
92 }
93}
94
99{
100 if (!mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
101 return;
102 }
103 if (!mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
104 return;
105 }
106 mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
108 r_data.count = 0;
109 });
110}
111
112} // namespace blender::bke
113
114blender::Span<int> Mesh::corner_to_face_map() const
115{
116 using namespace blender;
117 this->runtime->corner_to_face_map_cache.ensure([&](Array<int> &r_data) {
118 const OffsetIndices faces = this->faces();
120 });
121 return this->runtime->corner_to_face_map_cache.data();
122}
123
124blender::OffsetIndices<int> Mesh::vert_to_face_map_offsets() const
125{
126 using namespace blender;
127 this->runtime->vert_to_face_offset_cache.ensure([&](Array<int> &r_data) {
128 r_data = Array<int>(this->verts_num + 1, 0);
129 offset_indices::build_reverse_offsets(this->corner_verts(), r_data);
130 });
131 return OffsetIndices<int>(this->runtime->vert_to_face_offset_cache.data());
132}
133
134blender::GroupedSpan<int> Mesh::vert_to_face_map() const
135{
136 using namespace blender;
137 const OffsetIndices offsets = this->vert_to_face_map_offsets();
138 this->runtime->vert_to_face_map_cache.ensure([&](Array<int> &r_data) {
139 r_data.reinitialize(this->corners_num);
140 if (this->runtime->vert_to_corner_map_cache.is_cached() &&
141 this->runtime->corner_to_face_map_cache.is_cached())
142 {
143 /* The vertex to face cache can be built from the vertex to face corner
144 * and face corner to face maps if they are both already cached. */
145 array_utils::gather(this->runtime->corner_to_face_map_cache.data().as_span(),
146 this->runtime->vert_to_corner_map_cache.data().as_span(),
147 r_data.as_mutable_span());
148 }
149 else {
150 bke::mesh::build_vert_to_face_indices(this->faces(), this->corner_verts(), offsets, r_data);
151 }
152 });
153 return {offsets, this->runtime->vert_to_face_map_cache.data()};
154}
155
156blender::GroupedSpan<int> Mesh::vert_to_corner_map() const
157{
158 using namespace blender;
159 const OffsetIndices offsets = this->vert_to_face_map_offsets();
160 this->runtime->vert_to_corner_map_cache.ensure([&](Array<int> &r_data) {
161 r_data = bke::mesh::build_vert_to_corner_indices(this->corner_verts(), offsets);
162 });
163 return {offsets, this->runtime->vert_to_corner_map_cache.data()};
164}
165
166const blender::bke::LooseVertCache &Mesh::loose_verts() const
167{
168 using namespace blender::bke;
169 this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
170 const Span<int> verts = this->edges().cast<int>();
172 verts, this->verts_num, r_data.is_loose_bits, r_data.count);
173 });
174 return this->runtime->loose_verts_cache.data();
175}
176
177const blender::bke::LooseVertCache &Mesh::verts_no_face() const
178{
179 using namespace blender::bke;
180 this->runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
181 const Span<int> verts = this->corner_verts();
183 verts, this->verts_num, r_data.is_loose_bits, r_data.count);
184 });
185 return this->runtime->verts_no_face_cache.data();
186}
187
188bool Mesh::no_overlapping_topology() const
189{
190 return this->flag & ME_NO_OVERLAPPING_TOPOLOGY;
191}
192
193const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
194{
195 using namespace blender::bke;
196 this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
197 const Span<int> edges = this->corner_edges();
199 edges, this->edges_num, r_data.is_loose_bits, r_data.count);
200 });
201 return this->runtime->loose_edges_cache.data();
202}
203
204void Mesh::tag_loose_verts_none() const
205{
206 using namespace blender::bke;
207 this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
209 r_data.count = 0;
210 });
212}
213
214void Mesh::tag_loose_edges_none() const
215{
216 using namespace blender::bke;
217 this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
219 r_data.count = 0;
220 });
222}
223
224void Mesh::tag_overlapping_none()
225{
226 using namespace blender::bke;
228}
229
230namespace blender::bke {
231
233{
234 this->frozen = true;
235 this->dirty_while_frozen = false;
236}
237
239{
240 this->frozen = false;
241 if (this->dirty_while_frozen) {
242 this->data.tag_dirty();
243 }
244 this->dirty_while_frozen = false;
245}
246
248{
249 if (this->frozen) {
250 this->dirty_while_frozen = true;
251 }
252 else {
253 this->data.tag_dirty();
254 }
255}
256
257} // namespace blender::bke
258
259blender::Span<blender::int3> Mesh::corner_tris() const
260{
261 this->runtime->corner_tris_cache.data.ensure([&](blender::Array<blender::int3> &r_data) {
262 const Span<float3> positions = this->vert_positions();
263 const blender::OffsetIndices faces = this->faces();
264 const Span<int> corner_verts = this->corner_verts();
265
266 r_data.reinitialize(poly_to_tri_count(faces.size(), corner_verts.size()));
267
269 blender::bke::mesh::corner_tris_calc(positions, faces, corner_verts, r_data);
270 }
271 else {
273 positions, faces, corner_verts, this->face_normals(), r_data);
274 }
275 });
276
277 return this->runtime->corner_tris_cache.data.data();
278}
279
280blender::Span<int> Mesh::corner_tri_faces() const
281{
282 using namespace blender;
283 this->runtime->corner_tri_faces_cache.ensure([&](blender::Array<int> &r_data) {
284 const OffsetIndices faces = this->faces();
285 r_data.reinitialize(poly_to_tri_count(faces.size(), this->corners_num));
287 });
288 return this->runtime->corner_tri_faces_cache.data();
289}
290
292{
293 /* Allow returning the size without calculating the cache. */
294 return poly_to_tri_count(mesh->faces_num, mesh->corners_num);
295}
296
298{
299 if (!mesh->runtime->edit_data) {
300 mesh->runtime->edit_data = std::make_unique<blender::bke::EditMeshData>();
301 }
302}
303
305{
306 using namespace blender::bke;
307 free_mesh_eval(*mesh->runtime);
308 free_batch_cache(*mesh->runtime);
309 mesh->runtime->edit_data.reset();
311}
312
314{
315 /* Tagging shared caches dirty will free the allocated data if there is only one user. */
316 free_bvh_caches(*mesh->runtime);
317 mesh->runtime->subdiv_ccg.reset();
318 mesh->runtime->bounds_cache.tag_dirty();
319 mesh->runtime->vert_to_face_offset_cache.tag_dirty();
320 mesh->runtime->vert_to_face_map_cache.tag_dirty();
321 mesh->runtime->vert_to_corner_map_cache.tag_dirty();
322 mesh->runtime->corner_to_face_map_cache.tag_dirty();
323 mesh->runtime->vert_normals_cache.tag_dirty();
324 mesh->runtime->vert_normals_true_cache.tag_dirty();
325 mesh->runtime->face_normals_cache.tag_dirty();
326 mesh->runtime->face_normals_true_cache.tag_dirty();
327 mesh->runtime->corner_normals_cache.tag_dirty();
328 mesh->runtime->loose_edges_cache.tag_dirty();
329 mesh->runtime->loose_verts_cache.tag_dirty();
330 mesh->runtime->verts_no_face_cache.tag_dirty();
331 mesh->runtime->corner_tris_cache.data.tag_dirty();
332 mesh->runtime->corner_tri_faces_cache.tag_dirty();
333 mesh->runtime->shrinkwrap_boundary_cache.tag_dirty();
334 mesh->runtime->max_material_index.tag_dirty();
335 mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
336 mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
337 mesh->runtime->spatial_groups.reset();
339}
340
341void Mesh::tag_edges_split()
342{
343 /* Triangulation didn't change because vertex positions and loop vertex indices didn't change. */
344 free_bvh_caches(*this->runtime);
345 this->runtime->vert_normals_cache.tag_dirty();
346 this->runtime->corner_normals_cache.tag_dirty();
347 this->runtime->subdiv_ccg.reset();
348 this->runtime->vert_to_face_offset_cache.tag_dirty();
349 this->runtime->vert_to_face_map_cache.tag_dirty();
350 this->runtime->vert_to_corner_map_cache.tag_dirty();
351 if (this->runtime->loose_edges_cache.is_cached() &&
352 this->runtime->loose_edges_cache.data().count != 0)
353 {
354 this->runtime->loose_edges_cache.tag_dirty();
355 }
356 if (this->runtime->loose_verts_cache.is_cached() &&
357 this->runtime->loose_verts_cache.data().count != 0)
358 {
359 this->runtime->loose_verts_cache.tag_dirty();
360 }
361 if (this->runtime->verts_no_face_cache.is_cached() &&
362 this->runtime->verts_no_face_cache.data().count != 0)
363 {
364 this->runtime->verts_no_face_cache.tag_dirty();
365 }
366 this->runtime->subsurf_face_dot_tags.clear_and_shrink();
367 this->runtime->subsurf_optimal_display_edges.clear_and_shrink();
368 this->runtime->shrinkwrap_boundary_cache.tag_dirty();
369}
370
371void Mesh::tag_sharpness_changed()
372{
373 this->runtime->vert_normals_cache.tag_dirty();
374 this->runtime->face_normals_cache.tag_dirty();
375 this->runtime->corner_normals_cache.tag_dirty();
376}
377
378void Mesh::tag_custom_normals_changed()
379{
380 this->runtime->vert_normals_cache.tag_dirty();
381 this->runtime->face_normals_cache.tag_dirty();
382 this->runtime->corner_normals_cache.tag_dirty();
383}
384
385void Mesh::tag_face_winding_changed()
386{
387 this->runtime->vert_normals_cache.tag_dirty();
388 this->runtime->face_normals_cache.tag_dirty();
389 this->runtime->vert_normals_true_cache.tag_dirty();
390 this->runtime->face_normals_true_cache.tag_dirty();
391 this->runtime->corner_normals_cache.tag_dirty();
392 this->runtime->vert_to_corner_map_cache.tag_dirty();
393 this->runtime->shrinkwrap_boundary_cache.tag_dirty();
394}
395
396void Mesh::tag_positions_changed()
397{
398 this->runtime->vert_normals_cache.tag_dirty();
399 this->runtime->face_normals_cache.tag_dirty();
400 this->runtime->vert_normals_true_cache.tag_dirty();
401 this->runtime->face_normals_true_cache.tag_dirty();
402 this->runtime->corner_normals_cache.tag_dirty();
403 this->runtime->shrinkwrap_boundary_cache.tag_dirty();
404 this->tag_positions_changed_no_normals();
405}
406
407void Mesh::tag_positions_changed_no_normals()
408{
409 free_bvh_caches(*this->runtime);
410 this->runtime->corner_tris_cache.tag_dirty();
411 this->runtime->bounds_cache.tag_dirty();
412 this->runtime->shrinkwrap_boundary_cache.tag_dirty();
413}
414
415void Mesh::tag_positions_changed_uniformly()
416{
417 /* The normals and triangulation didn't change, since all verts moved by the same amount. */
418 free_bvh_caches(*this->runtime);
419 this->runtime->bounds_cache.tag_dirty();
420}
421
422void Mesh::tag_topology_changed()
423{
425}
426
427void Mesh::tag_visibility_changed()
428{
429 this->runtime->bvh_cache_corner_tris_no_hidden.tag_dirty();
430 this->runtime->bvh_cache_loose_verts_no_hidden.tag_dirty();
431 this->runtime->bvh_cache_loose_edges_no_hidden.tag_dirty();
432}
433
434void Mesh::tag_material_index_changed()
435{
436 this->runtime->max_material_index.tag_dirty();
437}
438
440
441/* -------------------------------------------------------------------- */
444
445/* Draw Engine */
446
448void (*BKE_mesh_batch_cache_free_cb)(void *batch_cache) = nullptr;
449
451{
452 if (mesh->runtime->batch_cache) {
454 }
455
456 /* Also tag batch cache for subdivided mesh, if it exists this will be
457 * the mesh that is actually being drawn. */
458 Mesh *mesh_eval = mesh->runtime->mesh_eval;
459 if (mesh_eval && mesh_eval->runtime->batch_cache) {
460 BKE_mesh_batch_cache_dirty_tag_cb(mesh_eval, mode);
461 }
462}
463void BKE_mesh_batch_cache_free(void *batch_cache)
464{
465 BKE_mesh_batch_cache_free_cb(batch_cache);
466}
467
469
470/* -------------------------------------------------------------------- */
473
474#ifndef NDEBUG
475
477{
478 const bool do_verbose = true;
479 const bool do_fixes = false;
480
481 bool is_valid = true;
482 bool changed = true;
483
484 if (do_verbose) {
485 printf("MESH: %s\n", mesh_eval->id.name + 2);
486 }
487
488 MutableSpan<float3> positions = mesh_eval->vert_positions_for_write();
489 MutableSpan<blender::int2> edges = mesh_eval->edges_for_write();
490 Span<int> face_offsets = mesh_eval->face_offsets();
491 Span<int> corner_verts = mesh_eval->corner_verts();
492 MutableSpan<int> corner_edges = mesh_eval->corner_edges_for_write();
493
495 &mesh_eval->vert_data,
496 mesh_eval->verts_num,
497 &mesh_eval->edge_data,
498 mesh_eval->edges_num,
499 &mesh_eval->corner_data,
500 mesh_eval->corners_num,
501 &mesh_eval->face_data,
502 mesh_eval->faces_num,
503 false, /* setting mask here isn't useful, gives false positives */
504 do_verbose,
505 do_fixes,
506 &changed);
507
508 MDeformVert *dverts = static_cast<MDeformVert *>(
510 is_valid &= BKE_mesh_validate_arrays(
511 mesh_eval,
512 reinterpret_cast<float (*)[3]>(positions.data()),
513 positions.size(),
514 edges.data(),
515 edges.size(),
517 &mesh_eval->fdata_legacy, CD_MFACE, mesh_eval->totface_legacy)),
518 mesh_eval->totface_legacy,
519 corner_verts.data(),
520 corner_edges.data(),
521 corner_verts.size(),
522 face_offsets.data(),
523 mesh_eval->faces_num,
524 dverts,
525 do_verbose,
526 do_fixes,
527 &changed);
528
529 BLI_assert(changed == false);
530
531 return is_valid;
532}
533
534#endif /* !NDEBUG */
535
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
void BKE_id_free(Main *bmain, void *idv)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
void(* BKE_mesh_batch_cache_free_cb)(void *batch_cache)
bool BKE_mesh_validate_all_customdata(CustomData *vert_data, uint verts_num, CustomData *edge_data, uint edges_num, CustomData *corner_data, uint corners_num, CustomData *face_data, uint faces_num, bool check_meshmask, bool do_verbose, bool do_fixes, bool *r_change)
eMeshBatchDirtyMode
Definition BKE_mesh.h:37
void(* BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *mesh, eMeshBatchDirtyMode mode)
void BKE_mesh_batch_cache_free(void *batch_cache)
bool BKE_mesh_face_normals_are_dirty(const Mesh *mesh)
bool BKE_mesh_validate_arrays(Mesh *mesh, float(*vert_positions)[3], unsigned int verts_num, blender::int2 *edges, unsigned int edges_num, MFace *legacy_faces, unsigned int legacy_faces_num, const int *corner_verts, int *corner_edges, unsigned int corners_num, const int *face_offsets, unsigned int faces_num, MDeformVert *dverts, bool do_verbose, bool do_fixes, bool *r_change)
bool BKE_mesh_runtime_is_valid(Mesh *mesh_eval)
void BKE_mesh_runtime_ensure_edit_data(Mesh *mesh)
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
int BKE_mesh_runtime_corner_tris_len(const Mesh *mesh)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
@ CD_MDEFORMVERT
@ ME_FLAG_UV_SELECT_SYNC_VALID
@ ME_NO_OVERLAPPING_TOPOLOGY
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr T * data() const
Definition BLI_span.hh:539
Span< NewT > constexpr cast() const
Definition BLI_span.hh:418
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
void resize(const int64_t new_size_in_bits, const bool value=false)
static float verts[][3]
#define printf(...)
int count
static char faces[256]
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void build_vert_to_face_indices(OffsetIndices< int > faces, Span< int > corner_verts, OffsetIndices< int > offsets, MutableSpan< int > face_indices)
void corner_tris_calc(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, MutableSpan< int3 > corner_tris)
Array< int > build_corner_to_face_map(OffsetIndices< int > faces)
Array< int > build_vert_to_corner_indices(Span< int > corner_verts, OffsetIndices< int > offsets)
void corner_tris_calc_with_normals(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< float3 > face_normals, MutableSpan< int3 > corner_tris)
void corner_tris_calc_face_indices(OffsetIndices< int > faces, MutableSpan< int > tri_faces)
static void try_tag_verts_no_face_none(const Mesh &mesh)
static void bit_vector_with_reset_bits_or_empty(const Span< int > indices_to_reset, const int indexed_elems_num, BitVector<> &r_bits, int &r_count)
static int reset_bits_and_count(MutableBitSpan bits, const Span< int > indices_to_reset)
static void free_bvh_caches(MeshRuntime &mesh_runtime)
static void free_batch_cache(MeshRuntime &mesh_runtime)
static void free_mesh_eval(MeshRuntime &mesh_runtime)
void build_reverse_offsets(Span< int > indices, MutableSpan< int > offsets)
VecBase< float, 3 > float3
char name[258]
Definition DNA_ID.h:432
int corners_num
CustomData edge_data
int edges_num
MeshRuntimeHandle * runtime
CustomData corner_data
CustomData face_data
CustomData vert_data
CustomData fdata_legacy
int totface_legacy
int faces_num
int verts_num
blender::BitVector is_loose_bits
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_loose_verts
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_loose_edges_no_hidden
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_corner_tris
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_edges
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_verts
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_loose_verts_no_hidden
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_loose_edges
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_faces
SharedCache< std::unique_ptr< BVHTree, BVHTreeDeleter > > bvh_cache_corner_tris_no_hidden
SharedCache< Array< int3 > > data
i
Definition text_draw.cc:230
uint8_t flag
Definition wm_window.cc:145