From cc7da09c1b820b91b8cfc7639893284e2bfc7ac2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 29 Sep 2023 21:44:36 +0200 Subject: [PATCH] Geometry: add utility to check for bad geometry element index dependence Sometimes .blend files have compatibility issues between Blender versions, because .blend files depended on the specific order of geometry elements generated by some nodes/modifiers (#112746, #113018). While we make guarantees about the order in some places, that is relatively rare, because it makes future improvements much harder. The functionality in this patch makes it easier for users to notice when they depend on things that are not expected to be stable between Blender builds. This is achieved by adding a new global flag which indicates whether some algorithms should randomize their output. The functionality can be toggled on or off by searching for `Set Geometry Randomization`. If there are no differences (or acceptable minor ones) when the flag is on or off, one can be reasonably sure that one does not on unspecified behavior (can't be 100% sure though, because randomization might be missing in some places). If there are big differences, one should consider fixing the file before it comes to an actual breakage in the next Blender version. Currently, the setting is only available when `Developer Extras` is turned on, because the setting is in no menu. With this patch, if we get bug reports with compatibility issues caused by depending on indices, one of the following three cases should always apply: * We actually accidentally broke something, which requires a fix commit. * Turning on geometry randomization shows that the .blend file depends on things it shouldn't depend on. In this case the user has to fix the file. * We are missing geometry randomization somewhere, which requires a fix commit. Pull Request: https://projects.blender.org/blender/blender/pulls/113030 --- source/blender/blenkernel/BKE_global.h | 9 + .../blender/editors/geometry/CMakeLists.txt | 1 + .../editors/geometry/geometry_intern.hh | 1 + .../blender/editors/geometry/geometry_ops.cc | 1 + .../geometry/geometry_randomization.cc | 58 +++++ source/blender/geometry/CMakeLists.txt | 2 + source/blender/geometry/GEO_randomize.hh | 24 ++ .../geometry/intern/mesh_merge_by_distance.cc | 3 + .../geometry/intern/mesh_split_edges.cc | 3 + .../geometry/intern/mesh_to_curve_convert.cc | 3 + .../intern/point_merge_by_distance.cc | 3 + source/blender/geometry/intern/randomize.cc | 231 ++++++++++++++++++ source/blender/modifiers/intern/MOD_bevel.cc | 4 + .../blender/modifiers/intern/MOD_boolean.cc | 6 + .../blender/modifiers/intern/MOD_decimate.cc | 4 + .../blender/modifiers/intern/MOD_edgesplit.cc | 4 + source/blender/modifiers/intern/MOD_remesh.cc | 5 + .../nodes/geometry/nodes/node_geo_boolean.cc | 4 + .../geometry/nodes/node_geo_convex_hull.cc | 5 + .../geometry/nodes/node_geo_curve_to_mesh.cc | 4 + .../node_geo_distribute_points_in_volume.cc | 4 + .../node_geo_distribute_points_on_faces.cc | 4 + .../geometry/nodes/node_geo_dual_mesh.cc | 3 + .../geometry/nodes/node_geo_extrude_mesh.cc | 4 + .../nodes/node_geo_interpolate_curves.cc | 4 + .../node_geo_mesh_primitive_ico_sphere.cc | 4 + .../geometry/nodes/node_geo_mesh_subdivide.cc | 3 + .../nodes/node_geo_points_to_curves.cc | 4 + .../nodes/node_geo_string_to_curves.cc | 4 + .../nodes/node_geo_subdivision_surface.cc | 4 + .../geometry/nodes/node_geo_triangulate.cc | 6 + .../geometry/nodes/node_geo_volume_to_mesh.cc | 4 + 32 files changed, 423 insertions(+) create mode 100644 source/blender/editors/geometry/geometry_randomization.cc create mode 100644 source/blender/geometry/GEO_randomize.hh create mode 100644 source/blender/geometry/intern/randomize.cc diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index ddd6cc7a052..ad11f72984f 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -129,6 +129,15 @@ typedef struct Global { */ int debug; + /** + * When true, various geometry processing algorithms randomize the order of elements (e.g. + * vertices or edges) in the output. In many cases, we don't make guarantees about the exact + * order of elements. So if users depend on the indices with e.g. geometry nodes, their file can + * break in a different Blender version. Explicitly turning on randomization can help protect + * oneself against such breakages. + */ + bool randomize_geometry_element_order; + /** * Control behavior of file reading/writing. * diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt index 3d339a5652d..f283c3bafe1 100644 --- a/source/blender/editors/geometry/CMakeLists.txt +++ b/source/blender/editors/geometry/CMakeLists.txt @@ -24,6 +24,7 @@ set(INC_SYS set(SRC geometry_attributes.cc geometry_ops.cc + geometry_randomization.cc node_group_operator.cc geometry_intern.hh diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh index e9ba987fa79..145396cfc35 100644 --- a/source/blender/editors/geometry/geometry_intern.hh +++ b/source/blender/editors/geometry/geometry_intern.hh @@ -21,6 +21,7 @@ void GEOMETRY_OT_color_attribute_render_set(wmOperatorType *ot); void GEOMETRY_OT_color_attribute_duplicate(wmOperatorType *ot); void GEOMETRY_OT_attribute_convert(wmOperatorType *ot); void GEOMETRY_OT_color_attribute_convert(wmOperatorType *ot); +void GEOMETRY_OT_geometry_randomization(wmOperatorType *ot); void GEOMETRY_OT_execute_node_group(wmOperatorType *ot); diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc index 8285d9c30ca..85b9acb995a 100644 --- a/source/blender/editors/geometry/geometry_ops.cc +++ b/source/blender/editors/geometry/geometry_ops.cc @@ -27,4 +27,5 @@ void ED_operatortypes_geometry() WM_operatortype_append(GEOMETRY_OT_attribute_convert); WM_operatortype_append(GEOMETRY_OT_color_attribute_convert); WM_operatortype_append(GEOMETRY_OT_execute_node_group); + WM_operatortype_append(GEOMETRY_OT_geometry_randomization); } diff --git a/source/blender/editors/geometry/geometry_randomization.cc b/source/blender/editors/geometry/geometry_randomization.cc new file mode 100644 index 00000000000..a5f683e7867 --- /dev/null +++ b/source/blender/editors/geometry/geometry_randomization.cc @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "WM_api.hh" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" + +#include "DEG_depsgraph.hh" + +#include "RNA_access.hh" +#include "RNA_define.hh" + +#include "geometry_intern.hh" + +namespace blender::ed::geometry { + +static int geometry_randomization_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_boolean_set(op->ptr, "value", G.randomize_geometry_element_order); + return WM_operator_props_popup(C, op, event); +} + +static int geometry_randomization_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + + G.randomize_geometry_element_order = RNA_boolean_get(op->ptr, "value"); + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + } + WM_event_add_notifier(C, NC_WINDOW, nullptr); + return OPERATOR_FINISHED; +} + +void GEOMETRY_OT_geometry_randomization(wmOperatorType *ot) +{ + ot->name = "Set Geometry Randomization"; + ot->idname = "GEOMETRY_OT_geometry_randomization"; + ot->description = "Toggle geometry randomization for debugging purposes"; + + ot->exec = geometry_randomization_exec; + ot->invoke = geometry_randomization_invoke; + ot->flag |= OPTYPE_UNDO | OPTYPE_REGISTER; + + RNA_def_boolean(ot->srna, + "value", + false, + "Value", + "Randomize the order of geometry elements (e.g. vertices or edges) after some " + "operations where there are no guarantees about the order. This avoids " + "accidentally depending on something that may change in the future"); +} + +} // namespace blender::ed::geometry diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 9a17246fc9a..eda7767b8ab 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -30,6 +30,7 @@ set(SRC intern/mesh_to_volume.cc intern/point_merge_by_distance.cc intern/points_to_volume.cc + intern/randomize.cc intern/realize_instances.cc intern/resample_curves.cc intern/reverse_uv_sampler.cc @@ -54,6 +55,7 @@ set(SRC GEO_mesh_to_volume.hh GEO_point_merge_by_distance.hh GEO_points_to_volume.hh + GEO_randomize.hh GEO_realize_instances.hh GEO_resample_curves.hh GEO_reverse_uv_sampler.hh diff --git a/source/blender/geometry/GEO_randomize.hh b/source/blender/geometry/GEO_randomize.hh new file mode 100644 index 00000000000..4727b43b3a8 --- /dev/null +++ b/source/blender/geometry/GEO_randomize.hh @@ -0,0 +1,24 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +struct Mesh; +struct PointCloud; +namespace blender::bke { +class CurvesGeometry; +} + +namespace blender::geometry { + +bool use_debug_randomization(); + +void debug_randomize_vertex_order(Mesh *mesh); +void debug_randomize_edge_order(Mesh *mesh); +void debug_randomize_face_order(Mesh *mesh); +void debug_randomize_mesh_order(Mesh *mesh); +void debug_randomize_point_order(PointCloud *pointcloud); +void debug_randomize_curve_order(bke::CurvesGeometry *curves); + +}; // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index 8098c466155..8d4dccf494a 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -21,6 +21,7 @@ #include "BKE_mesh.hh" #include "GEO_mesh_merge_by_distance.hh" +#include "GEO_randomize.hh" #ifdef USE_WELD_DEBUG_TIME # include "BLI_timeit.hh" @@ -1680,6 +1681,8 @@ static Mesh *create_merged_mesh(const Mesh &mesh, BLI_assert(int(r_i) == result_nfaces); BLI_assert(loop_cur == result_nloops); + debug_randomize_mesh_order(result); + return result; } diff --git a/source/blender/geometry/intern/mesh_split_edges.cc b/source/blender/geometry/intern/mesh_split_edges.cc index b78b7922890..608ceccf936 100644 --- a/source/blender/geometry/intern/mesh_split_edges.cc +++ b/source/blender/geometry/intern/mesh_split_edges.cc @@ -12,6 +12,7 @@ #include "BKE_mesh_mapping.hh" #include "GEO_mesh_split_edges.hh" +#include "GEO_randomize.hh" namespace blender::geometry { @@ -591,6 +592,8 @@ void split_edges(Mesh &mesh, propagate_vert_attributes(mesh, vert_map); BKE_mesh_tag_edges_split(&mesh); + + debug_randomize_mesh_order(&mesh); } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index c501310590a..056ad06731f 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -17,6 +17,7 @@ #include "BKE_mesh.hh" #include "GEO_mesh_to_curve.hh" +#include "GEO_randomize.hh" namespace blender::geometry { @@ -74,6 +75,8 @@ BLI_NOINLINE bke::CurvesGeometry create_curve_from_vert_indices( return true; }); + debug_randomize_curve_order(&curves); + return curves; } diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 3d317706b97..01441669495 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -13,6 +13,7 @@ #include "BKE_pointcloud.h" #include "GEO_point_merge_by_distance.hh" +#include "GEO_randomize.hh" namespace blender::geometry { @@ -154,6 +155,8 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points, }); } + debug_randomize_point_order(dst_pointcloud); + return dst_pointcloud; } diff --git a/source/blender/geometry/intern/randomize.cc b/source/blender/geometry/intern/randomize.cc new file mode 100644 index 00000000000..6f4bea740d2 --- /dev/null +++ b/source/blender/geometry/intern/randomize.cc @@ -0,0 +1,231 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +#include "GEO_randomize.hh" + +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute.hh" +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_mesh.hh" + +#include "BLI_array.hh" + +namespace blender::geometry { + +static Array get_permutation(const int length, const int seed) +{ + Array data(length); + for (const int i : IndexRange(length)) { + data[i] = i; + } + std::shuffle(data.begin(), data.end(), std::default_random_engine(seed)); + return data; +} + +static Array invert_permutation(const Span permutation) +{ + Array data(permutation.size()); + for (const int i : permutation.index_range()) { + data[permutation[i]] = i; + } + return data; +} + +/** + * We can't use a fully random seed, because then the randomization wouldn't be deterministic, + * which is important to avoid causing issues when determinism is expected. Using a single constant + * seed is not ideal either, because then two geometries might be randomized equally or very + * similar. Ideally, the seed would be a hash of everything that feeds into the geometry processing + * algorithm before the randomization, but that's too expensive. Just use something simple but + * correct for now. + */ +static int seed_from_mesh(const Mesh &mesh) +{ + return mesh.totvert; +} + +static int seed_from_pointcloud(const PointCloud &pointcloud) +{ + return pointcloud.totpoint; +} + +static int seed_from_curves(const bke::CurvesGeometry &curves) +{ + return curves.point_num; +} + +static void reorder_customdata(CustomData &data, const Span new_by_old_map) +{ + CustomData new_data; + CustomData_copy_layout(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, new_by_old_map.size()); + + for (const int old_i : new_by_old_map.index_range()) { + const int new_i = new_by_old_map[old_i]; + CustomData_copy_data(&data, &new_data, old_i, new_i, 1); + } + CustomData_free(&data, new_by_old_map.size()); + data = new_data; +} + +void debug_randomize_vertex_order(Mesh *mesh) +{ + if (mesh == nullptr || !use_debug_randomization()) { + return; + } + + const int seed = seed_from_mesh(*mesh); + const Array new_by_old_map = get_permutation(mesh->totvert, seed); + + reorder_customdata(mesh->vert_data, new_by_old_map); + + for (int &v : mesh->edges_for_write().cast()) { + v = new_by_old_map[v]; + } + for (int &v : mesh->corner_verts_for_write()) { + v = new_by_old_map[v]; + } + + BKE_mesh_tag_topology_changed(mesh); +} + +void debug_randomize_edge_order(Mesh *mesh) +{ + if (mesh == nullptr || !use_debug_randomization()) { + return; + } + + const int seed = seed_from_mesh(*mesh); + const Array new_by_old_map = get_permutation(mesh->totedge, seed); + + reorder_customdata(mesh->edge_data, new_by_old_map); + + for (int &e : mesh->corner_edges_for_write()) { + e = new_by_old_map[e]; + } + + BKE_mesh_tag_topology_changed(mesh); +} + +static Array make_new_offset_indices(const OffsetIndices old_offsets, + const Span old_by_new_map) +{ + Array new_offsets(old_offsets.data().size()); + new_offsets[0] = 0; + for (const int new_i : old_offsets.index_range()) { + const int old_i = old_by_new_map[new_i]; + new_offsets[new_i + 1] = new_offsets[new_i] + old_offsets[old_i].size(); + } + return new_offsets; +} + +static void reorder_customdata_groups(CustomData &data, + const OffsetIndices old_offsets, + const OffsetIndices new_offsets, + const Span new_by_old_map) +{ + const int elements_num = new_offsets.total_size(); + const int groups_num = new_by_old_map.size(); + CustomData new_data; + CustomData_copy_layout(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, elements_num); + for (const int old_i : IndexRange(groups_num)) { + const int new_i = new_by_old_map[old_i]; + const IndexRange old_range = old_offsets[old_i]; + const IndexRange new_range = new_offsets[new_i]; + BLI_assert(old_range.size() == new_range.size()); + CustomData_copy_data(&data, &new_data, old_range.start(), new_range.start(), old_range.size()); + } + CustomData_free(&data, elements_num); + data = new_data; +} + +void debug_randomize_face_order(Mesh *mesh) +{ + if (mesh == nullptr || !use_debug_randomization()) { + return; + } + + const int seed = seed_from_mesh(*mesh); + const Array new_by_old_map = get_permutation(mesh->faces_num, seed); + const Array old_by_new_map = invert_permutation(new_by_old_map); + + reorder_customdata(mesh->face_data, new_by_old_map); + + const OffsetIndices old_faces = mesh->faces(); + Array new_face_offsets = make_new_offset_indices(old_faces, old_by_new_map); + const OffsetIndices new_faces = new_face_offsets.as_span(); + + reorder_customdata_groups(mesh->loop_data, old_faces, new_faces, new_by_old_map); + + mesh->face_offsets_for_write().copy_from(new_face_offsets); + + BKE_mesh_tag_topology_changed(mesh); +} + +void debug_randomize_point_order(PointCloud *pointcloud) +{ + if (pointcloud == nullptr || !use_debug_randomization()) { + return; + } + + const int seed = seed_from_pointcloud(*pointcloud); + const Array new_by_old_map = get_permutation(pointcloud->totpoint, seed); + + reorder_customdata(pointcloud->pdata, new_by_old_map); + + pointcloud->tag_positions_changed(); + pointcloud->tag_radii_changed(); +} + +void debug_randomize_curve_order(bke::CurvesGeometry *curves) +{ + if (curves == nullptr || !use_debug_randomization()) { + return; + } + + const int seed = seed_from_curves(*curves); + const Array new_by_old_map = get_permutation(curves->curve_num, seed); + const Array old_by_new_map = invert_permutation(new_by_old_map); + + reorder_customdata(curves->curve_data, new_by_old_map); + + const OffsetIndices old_points_by_curve = curves->points_by_curve(); + Array new_curve_offsets = make_new_offset_indices(old_points_by_curve, old_by_new_map); + const OffsetIndices new_points_by_curve = new_curve_offsets.as_span(); + + reorder_customdata_groups( + curves->point_data, old_points_by_curve, new_points_by_curve, new_by_old_map); + + curves->offsets_for_write().copy_from(new_curve_offsets); + + curves->tag_topology_changed(); +} + +void debug_randomize_mesh_order(Mesh *mesh) +{ + if (mesh == nullptr || !use_debug_randomization()) { + return; + } + + debug_randomize_vertex_order(mesh); + debug_randomize_edge_order(mesh); + debug_randomize_face_order(mesh); +} + +bool use_debug_randomization() +{ + return G.randomize_geometry_element_order; +} + +} // namespace blender::geometry diff --git a/source/blender/modifiers/intern/MOD_bevel.cc b/source/blender/modifiers/intern/MOD_bevel.cc index 673505e7502..a777c10b44e 100644 --- a/source/blender/modifiers/intern/MOD_bevel.cc +++ b/source/blender/modifiers/intern/MOD_bevel.cc @@ -39,6 +39,8 @@ #include "BLO_read_write.hh" +#include "GEO_randomize.hh" + #include "bmesh.h" #include "bmesh_tools.h" @@ -230,6 +232,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh BM_mesh_free(bm); + blender::geometry::debug_randomize_mesh_order(result); + return result; } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index f4444c13a79..9a21d80fda5 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -51,6 +51,8 @@ #include "MEM_guardedalloc.h" +#include "GEO_randomize.hh" + #include "bmesh.h" #include "bmesh_tools.h" #include "tools/bmesh_boolean.h" @@ -501,6 +503,8 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd, MutableSpan(result->mat, result->totcol).copy_from(materials); } + blender::geometry::debug_randomize_mesh_order(result); + return result; } #endif @@ -598,6 +602,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } + blender::geometry::debug_randomize_mesh_order(result); + return result; } diff --git a/source/blender/modifiers/intern/MOD_decimate.cc b/source/blender/modifiers/intern/MOD_decimate.cc index bde363094bd..f6aa43e6717 100644 --- a/source/blender/modifiers/intern/MOD_decimate.cc +++ b/source/blender/modifiers/intern/MOD_decimate.cc @@ -32,6 +32,8 @@ #include "DEG_depsgraph_query.hh" +#include "GEO_randomize.hh" + #include "bmesh.h" #include "bmesh_tools.h" @@ -207,6 +209,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh BM_mesh_free(bm); + blender::geometry::debug_randomize_mesh_order(result); + #ifdef USE_TIMEIT TIMEIT_END(decim); #endif diff --git a/source/blender/modifiers/intern/MOD_edgesplit.cc b/source/blender/modifiers/intern/MOD_edgesplit.cc index 69035f8e713..ebc9cc91e68 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.cc +++ b/source/blender/modifiers/intern/MOD_edgesplit.cc @@ -38,6 +38,8 @@ #include "MOD_modifiertypes.hh" #include "MOD_ui_common.hh" +#include "GEO_randomize.hh" + /* For edge split modifier node. */ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd); @@ -103,6 +105,8 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); + blender::geometry::debug_randomize_mesh_order(result); + return result; } diff --git a/source/blender/modifiers/intern/MOD_remesh.cc b/source/blender/modifiers/intern/MOD_remesh.cc index 4f0b4ae4b91..bd42cad66dd 100644 --- a/source/blender/modifiers/intern/MOD_remesh.cc +++ b/source/blender/modifiers/intern/MOD_remesh.cc @@ -36,6 +36,8 @@ #include "MOD_modifiertypes.hh" #include "MOD_ui_common.hh" +#include "GEO_randomize.hh" + #include #include @@ -198,6 +200,9 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext * /*ctx*/, BKE_mesh_copy_parameters_for_eval(result, mesh); BKE_mesh_calc_edges(result, true, false); + + blender::geometry::debug_randomize_mesh_order(result); + return result; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index d67095395b3..441cd30466c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -13,6 +13,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_boolean_cc { @@ -145,6 +147,8 @@ static void node_geo_exec(GeoNodeExecParams params) selection.finish(); } + geometry::debug_randomize_mesh_order(result); + params.set_output("Mesh", GeometrySet::from_mesh(result)); #else params.error_message_add(NodeWarningType::Error, diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index d41e9d18655..d8dd36cfd6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -10,6 +10,8 @@ #include "BKE_material.h" #include "BKE_mesh.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" #ifdef WITH_BULLET @@ -215,6 +217,9 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { Mesh *mesh = compute_hull(geometry_set); + if (mesh) { + geometry::debug_randomize_mesh_order(mesh); + } geometry_set.replace_mesh(mesh); geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f5d82915e58..3711f1c5631 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -9,6 +9,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_to_mesh_cc { @@ -37,11 +39,13 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, if (profile_curves == nullptr) { Mesh *mesh = bke::curve_to_wire_mesh(curves.geometry.wrap(), propagation_info); + geometry::debug_randomize_mesh_order(mesh); geometry_set.replace_mesh(mesh); } else { Mesh *mesh = bke::curve_to_mesh_sweep( curves.geometry.wrap(), profile_curves->geometry.wrap(), fill_caps, propagation_info); + geometry::debug_randomize_mesh_order(mesh); geometry_set.replace_mesh(mesh); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc index a47b9f9c026..b228caa13a3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc @@ -22,6 +22,8 @@ #include "DEG_depsgraph_query.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_distribute_points_in_volume_cc { @@ -253,6 +255,8 @@ static void node_geo_exec(GeoNodeExecParams params) point_radii.span.fill(0.05f); point_radii.finish(); + geometry::debug_randomize_point_order(pointcloud); + geometry_set.replace_pointcloud(pointcloud); geometry_set.keep_only_during_modify({GeometryComponent::Type::PointCloud}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 2f3ed09dc9a..61a099a7afe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -24,6 +24,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_distribute_points_on_faces_cc { @@ -556,6 +558,8 @@ static void point_distribution_calculate(GeometrySet &geometry_set, const bool use_legacy_normal = params.node().custom2 != 0; compute_attribute_outputs( mesh, *pointcloud, bary_coords, looptri_indices, attribute_outputs, use_legacy_normal); + + geometry::debug_randomize_point_order(pointcloud); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index cf1ada8e1ab..1dc84d6713a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -12,6 +12,8 @@ #include "BKE_mesh.hh" #include "BKE_mesh_mapping.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_dual_mesh_cc { @@ -921,6 +923,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (const Mesh *mesh = geometry_set.get_mesh()) { Mesh *new_mesh = calc_dual_mesh( *mesh, keep_boundaries, params.get_output_propagation_info("Dual Mesh")); + geometry::debug_randomize_mesh_order(new_mesh); geometry_set.replace_mesh(new_mesh); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 7111b493ba4..ca861f09e4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -15,6 +15,8 @@ #include "BKE_mesh_mapping.hh" #include "BKE_mesh_runtime.hh" +#include "GEO_randomize.hh" + #include "NOD_rna_define.hh" #include "UI_interface.hh" @@ -1431,6 +1433,8 @@ static void node_geo_exec(GeoNodeExecParams params) break; } } + + geometry::debug_randomize_mesh_order(mesh); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc index 557d6b3660b..19e932f6e01 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc @@ -14,6 +14,8 @@ #include "BKE_curves.hh" #include "BKE_curves_utils.hh" +#include "GEO_randomize.hh" + #include "DNA_pointcloud_types.h" namespace blender::nodes::node_geo_interpolate_curves_cc { @@ -759,6 +761,8 @@ static GeometrySet generate_interpolated_curves( child_curves_id->totcol = guide_curves_id.totcol; } + geometry::debug_randomize_curve_order(&child_curves); + return GeometrySet::from_curves(child_curves_id); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 6ab30013c25..e1b1b974452 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -8,6 +8,8 @@ #include "BKE_material.h" #include "BKE_mesh.hh" +#include "GEO_randomize.hh" + #include "bmesh.h" #include "node_geometry_util.hh" @@ -107,6 +109,8 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, } attributes.remove("UVMap"); + geometry::debug_randomize_mesh_order(mesh); + mesh->bounds_set_eager(calculate_bounds_ico_sphere(radius, subdivisions)); return mesh; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 86becc348b1..1e3d0fc361c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -9,6 +9,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_subdivide_cc { @@ -48,6 +50,7 @@ static Mesh *simple_subdivide_mesh(const Mesh &mesh, const int level) BKE_subdiv_free(subdiv); + geometry::debug_randomize_mesh_order(result); return result; } #endif /* WITH_OPENSUBDIV */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_curves.cc index 25836af3d8e..d24ff921842 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_curves.cc @@ -15,6 +15,8 @@ #include "BLI_sort.hh" #include "BLI_task.hh" +#include "GEO_randomize.hh" + #include "BKE_geometry_set.hh" namespace blender::nodes::node_geo_points_to_curves_cc { @@ -151,6 +153,8 @@ static Curves *curves_from_points(const PointCloud &points, {}, indices, curves.attributes_for_write()); + + geometry::debug_randomize_curve_order(&curves); return curves_id; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 503164eeb35..a4e939290d8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -20,6 +20,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_string_to_curves_cc { @@ -296,6 +298,8 @@ static Map create_curve_instances(GeoNodeExecParams ¶ms, bke::CurvesGeometry &curves = curves_id->geometry.wrap(); BKE_nurbList_free(&cu.nurb); + geometry::debug_randomize_curve_order(&curves); + float4x4 size_matrix = math::from_scale(float3(layout.final_font_size)); curves.transform(size_matrix); diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index c0bd0a069a6..df05462c4ea 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -20,6 +20,8 @@ #include "NOD_rna_define.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_subdivision_surface_cc { @@ -159,6 +161,8 @@ static Mesh *mesh_subsurf_calc(const Mesh *mesh, BKE_id_free(nullptr, mesh_copy); } + geometry::debug_randomize_mesh_order(result); + return result; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index f5175d1b310..d876cdd445a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -15,6 +15,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_triangulate_cc { @@ -67,6 +69,10 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, /* Positions are not changed by the triangulation operation, so the bounds are the same. */ result->runtime->bounds_cache = mesh.runtime->bounds_cache; + /* Vertex order is not affected. */ + geometry::debug_randomize_edge_order(result); + geometry::debug_randomize_face_order(result); + return result; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 5aa751b949e..5adbaca360d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -26,6 +26,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "GEO_randomize.hh" + namespace blender::nodes::node_geo_volume_to_mesh_cc { NODE_STORAGE_FUNCS(NodeGeometryVolumeToMesh) @@ -151,6 +153,8 @@ static Mesh *create_mesh_from_volume_grids(Span gri BKE_mesh_calc_edges(mesh, false, false); BKE_mesh_smooth_flag_set(mesh, false); + geometry::debug_randomize_mesh_order(mesh); + return mesh; }