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
This commit is contained in:
parent
1c4c76ccbf
commit
cc7da09c1b
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#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<int> get_permutation(const int length, const int seed)
|
||||
{
|
||||
Array<int> 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<int> invert_permutation(const Span<int> permutation)
|
||||
{
|
||||
Array<int> 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<int> 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<int> 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<int>()) {
|
||||
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<int> 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<int> make_new_offset_indices(const OffsetIndices<int> old_offsets,
|
||||
const Span<int> old_by_new_map)
|
||||
{
|
||||
Array<int> 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<int> old_offsets,
|
||||
const OffsetIndices<int> new_offsets,
|
||||
const Span<int> 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<int> new_by_old_map = get_permutation(mesh->faces_num, seed);
|
||||
const Array<int> 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<int> new_face_offsets = make_new_offset_indices(old_faces, old_by_new_map);
|
||||
const OffsetIndices<int> 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<int> 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<int> new_by_old_map = get_permutation(curves->curve_num, seed);
|
||||
const Array<int> 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<int> new_curve_offsets = make_new_offset_indices(old_points_by_curve, old_by_new_map);
|
||||
const OffsetIndices<int> 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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "MOD_modifiertypes.hh"
|
||||
#include "MOD_ui_common.hh"
|
||||
|
||||
#include "GEO_randomize.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<int, int> 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<float4x4>(float3(layout.final_font_size));
|
||||
curves.transform(size_matrix);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<openvdb::GridBase::ConstPtr> gri
|
|||
BKE_mesh_calc_edges(mesh, false, false);
|
||||
BKE_mesh_smooth_flag_set(mesh, false);
|
||||
|
||||
geometry::debug_randomize_mesh_order(mesh);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue