Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-01-21 22:02:13 +0300
committerHans Goudey <h.goudey@me.com>2022-01-21 22:02:13 +0300
commit9258671bc331c62f84791ac93ded34b6033efb36 (patch)
treed67750edeb841c09a7104102dc4ade865c65072e /source/blender
parentebc069c45f2807b55c9dc3f3d7a1f3881f364568 (diff)
parent9d274a609ca507fbd16f8daff95129281d4a24d8 (diff)
Merge branch 'master' into temp-geometry-nodes-extrude-mesh
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenfont/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/BKE_node.h3
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/makesdna/DNA_node_types.h5
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c47
-rw-r--r--source/blender/makesrna/intern/rna_scene.c6
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc485
11 files changed, 549 insertions, 5 deletions
diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt
index cc6e298b322..b1453b243c0 100644
--- a/source/blender/blenfont/CMakeLists.txt
+++ b/source/blender/blenfont/CMakeLists.txt
@@ -54,7 +54,7 @@ set(LIB
bf_gpu
bf_intern_guardedalloc
- ${FREETYPE_LIBRARY}
+ ${FREETYPE_LIBRARIES}
)
if(WIN32)
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index bb7d647e713..2f9034f6438 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1631,7 +1631,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_FIELD_AT_INDEX 1148
#define GEO_NODE_CURVE_PRIMITIVE_ARC 1149
#define GEO_NODE_FLIP_FACES 1150
-#define GEO_NODE_EXTRUDE_MESH 1151
+#define GEO_NODE_SCALE_ELEMENTS 1151
+#define GEO_NODE_EXTRUDE_MESH 1152
/** \} */
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index cb32bc29463..358ae934088 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -522,7 +522,7 @@ set(LIB
bf_simulation
# For `vfontdata_freetype.c`.
- ${FREETYPE_LIBRARY}
+ ${FREETYPE_LIBRARIES}
)
if(WITH_BINRELOC)
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 56841368893..8baffff9ecf 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -4826,6 +4826,7 @@ static void registerGeometryNodes()
register_node_type_geo_realize_instances();
register_node_type_geo_rotate_instances();
register_node_type_geo_sample_texture();
+ register_node_type_geo_scale_elements();
register_node_type_geo_scale_instances();
register_node_type_geo_separate_components();
register_node_type_geo_separate_geometry();
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 1167e6d698b..cc2f7ce615a 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -2352,6 +2352,11 @@ typedef enum GeometryNodeRealizeInstancesFlag {
GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR = (1 << 0),
} GeometryNodeRealizeInstancesFlag;
+typedef enum GeometryNodeScaleElementsMode {
+ GEO_NODE_SCALE_ELEMENTS_UNIFORM = 0,
+ GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1,
+} GeometryNodeScaleElementsMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index cad212dcbc0..4192a9975be 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -11443,6 +11443,53 @@ static void def_geo_field_at_index(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
+static void def_geo_scale_elements(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem domain_items[] = {
+ {ATTR_DOMAIN_FACE,
+ "FACE",
+ ICON_NONE,
+ "Face",
+ "Scale individual faces or neighboring face islands"},
+ {ATTR_DOMAIN_EDGE,
+ "EDGE",
+ ICON_NONE,
+ "Edge",
+ "Scale individual edges or neighboring edge islands"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem scale_mode_items[] = {
+ {GEO_NODE_SCALE_ELEMENTS_UNIFORM,
+ "UNIFORM",
+ ICON_NONE,
+ "Uniform",
+ "Scale elements by the same factor in every direction"},
+ {GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS,
+ "SINGLE_AXIS",
+ ICON_NONE,
+ "Single Axis",
+ "Scale elements in a single direction"},
+ {0, NULL, 0, NULL, NULL},
+
+ };
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, domain_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_FACE);
+ RNA_def_property_ui_text(prop, "Domain", "Element type to transform");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+
+ prop = RNA_def_property(srna, "scale_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, scale_mode_items);
+ RNA_def_property_ui_text(prop, "Scale Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 5bfbd42866b..31b2e2f7023 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -7911,8 +7911,10 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "clip");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "MovieClip");
- RNA_def_property_ui_text(
- prop, "Active Movie Clip", "Active movie clip used for constraints and viewport drawing");
+ RNA_def_property_ui_text(prop,
+ "Active Movie Clip",
+ "Active Movie Clip that can be used by motion tracking constraints "
+ "or as a camera's background image");
RNA_def_property_update(prop, NC_SCENE | ND_DRAW_RENDER_VIEWPORT, NULL);
/* color management */
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 0d0e9462891..e5c005f8c95 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -157,6 +157,7 @@ void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
void register_node_type_geo_rotate_instances(void);
void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_scale_elements(void);
void register_node_type_geo_scale_instances(void);
void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_separate_components(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index eae9e5e63ee..66a2d3720ca 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -408,6 +408,7 @@ DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
+DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index ab1fefb5ad9..0e5f90b58bf 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -168,6 +168,7 @@ set(SRC
nodes/node_geo_raycast.cc
nodes/node_geo_realize_instances.cc
nodes/node_geo_rotate_instances.cc
+ nodes/node_geo_scale_elements.cc
nodes/node_geo_scale_instances.cc
nodes/node_geo_separate_components.cc
nodes/node_geo_separate_geometry.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
new file mode 100644
index 00000000000..fd599b7ca61
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
@@ -0,0 +1,485 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_disjoint_set.hh"
+#include "BLI_task.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_scale_elements_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Scale"), "Scale").default_value(1.0f).min(0.0f).supports_field();
+ b.add_input<decl::Vector>(N_("Center"))
+ .subtype(PROP_TRANSLATION)
+ .implicit_field()
+ .description(N_("Origin of the scaling for each element. If multiple elements are "
+ "connected, their center is averaged"));
+ b.add_input<decl::Vector>(N_("Axis"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .supports_field()
+ .description(N_("Direction in which to scale the element"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
+};
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "scale_mode", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = ATTR_DOMAIN_FACE;
+ node->custom2 = GEO_NODE_SCALE_ELEMENTS_UNIFORM;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first);
+ bNodeSocket *selection_socket = geometry_socket->next;
+ bNodeSocket *scale_float_socket = selection_socket->next;
+ bNodeSocket *center_socket = scale_float_socket->next;
+ bNodeSocket *axis_socket = center_socket->next;
+
+ const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>(
+ node->custom2);
+ const bool use_single_axis = mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS;
+
+ nodeSetSocketAvailability(ntree, axis_socket, use_single_axis);
+}
+
+struct UniformScaleFields {
+ Field<bool> selection;
+ Field<float> scale;
+ Field<float3> center;
+};
+
+struct UniformScaleParams {
+ IndexMask selection;
+ VArray<float> scales;
+ VArray<float3> centers;
+};
+
+struct AxisScaleFields {
+ Field<bool> selection;
+ Field<float> scale;
+ Field<float3> center;
+ Field<float3> axis;
+};
+
+struct AxisScaleParams {
+ IndexMask selection;
+ VArray<float> scales;
+ VArray<float3> centers;
+ VArray<float3> axis_vectors;
+};
+
+/**
+ * When multiple elements share the same vertices, they are scaled together.
+ */
+struct ElementIsland {
+ /* Either face or edge indices. */
+ Vector<int> element_indices;
+};
+
+static float3 transform_with_uniform_scale(const float3 &position,
+ const float3 &center,
+ const float scale)
+{
+ const float3 diff = position - center;
+ const float3 scaled_diff = scale * diff;
+ const float3 new_position = center + scaled_diff;
+ return new_position;
+}
+
+static float4x4 create_single_axis_transform(const float3 &center,
+ const float3 &axis,
+ const float scale)
+{
+ /* Scale along x axis. The other axis need to be orthogonal, but their specific value does not
+ * matter. */
+ const float3 x_axis = math::normalize(axis);
+ float3 y_axis = math::cross(x_axis, float3(0.0f, 0.0f, 1.0f));
+ if (math::is_zero(y_axis)) {
+ y_axis = math::cross(x_axis, float3(0.0f, 1.0f, 0.0f));
+ }
+ y_axis = math::normalize(y_axis);
+ const float3 z_axis = math::cross(x_axis, y_axis);
+
+ float4x4 transform = float4x4::identity();
+
+ /* Move scaling center to the origin. */
+ sub_v3_v3(transform.values[3], center);
+
+ /* `base_change` and `base_change_inv` are used to rotate space so that scaling along the
+ * provided axis is the same as scaling along the x axis. */
+ float4x4 base_change = float4x4::identity();
+ copy_v3_v3(base_change.values[0], x_axis);
+ copy_v3_v3(base_change.values[1], y_axis);
+ copy_v3_v3(base_change.values[2], z_axis);
+
+ /* Can invert by transposing, because the matrix is orthonormal. */
+ float4x4 base_change_inv = base_change.transposed();
+
+ float4x4 scale_transform = float4x4::identity();
+ scale_transform.values[0][0] = scale;
+
+ transform = base_change * scale_transform * base_change_inv * transform;
+
+ /* Move scaling center back to where it was. */
+ add_v3_v3(transform.values[3], center);
+
+ return transform;
+}
+
+using GetVertexIndicesFn =
+ FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>;
+
+static void scale_vertex_islands_uniformly(Mesh &mesh,
+ const Span<ElementIsland> islands,
+ const UniformScaleParams &params,
+ const GetVertexIndicesFn get_vertex_indices)
+{
+ threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
+ for (const int island_index : range) {
+ const ElementIsland &island = islands[island_index];
+
+ float scale = 0.0f;
+ float3 center = {0.0f, 0.0f, 0.0f};
+
+ VectorSet<int> vertex_indices;
+ for (const int poly_index : island.element_indices) {
+ get_vertex_indices(mesh, poly_index, vertex_indices);
+ center += params.centers[poly_index];
+ scale += params.scales[poly_index];
+ }
+
+ /* Divide by number of elements to get the average. */
+ const float f = 1.0f / island.element_indices.size();
+ scale *= f;
+ center *= f;
+
+ for (const int vert_index : vertex_indices) {
+ MVert &vert = mesh.mvert[vert_index];
+ const float3 old_position = vert.co;
+ const float3 new_position = transform_with_uniform_scale(old_position, center, scale);
+ copy_v3_v3(vert.co, new_position);
+ }
+ }
+ });
+
+ /* Positions have changed, so the normals will have to be recomputed. */
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static void scale_vertex_islands_on_axis(Mesh &mesh,
+ const Span<ElementIsland> islands,
+ const AxisScaleParams &params,
+ const GetVertexIndicesFn get_vertex_indices)
+{
+ threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
+ for (const int island_index : range) {
+ const ElementIsland &island = islands[island_index];
+
+ float scale = 0.0f;
+ float3 center = {0.0f, 0.0f, 0.0f};
+ float3 axis = {0.0f, 0.0f, 0.0f};
+
+ VectorSet<int> vertex_indices;
+ for (const int poly_index : island.element_indices) {
+ get_vertex_indices(mesh, poly_index, vertex_indices);
+ center += params.centers[poly_index];
+ scale += params.scales[poly_index];
+ axis += params.axis_vectors[poly_index];
+ }
+
+ /* Divide by number of elements to get the average. */
+ const float f = 1.0f / island.element_indices.size();
+ scale *= f;
+ center *= f;
+ axis *= f;
+
+ if (math::is_zero(axis)) {
+ axis = float3(1.0f, 0.0f, 0.0f);
+ }
+
+ const float4x4 transform = create_single_axis_transform(center, axis, scale);
+ for (const int vert_index : vertex_indices) {
+ MVert &vert = mesh.mvert[vert_index];
+ const float3 old_position = vert.co;
+ const float3 new_position = transform * old_position;
+ copy_v3_v3(vert.co, new_position);
+ }
+ }
+ });
+
+ /* Positions have changed, so the normals will have to be recomputed. */
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
+{
+ /* Use the disjoint set data structure to determine which vertices have to be scaled together. */
+ DisjointSet disjoint_set(mesh.totvert);
+ for (const int poly_index : face_selection) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ for (const int loop_index : IndexRange(poly.totloop - 1)) {
+ const int v1 = poly_loops[loop_index].v;
+ const int v2 = poly_loops[loop_index + 1].v;
+ disjoint_set.join(v1, v2);
+ }
+ disjoint_set.join(poly_loops.first().v, poly_loops.last().v);
+ }
+
+ VectorSet<int> island_ids;
+ Vector<ElementIsland> islands;
+ /* There are at most as many islands as there are selected faces. */
+ islands.reserve(face_selection.size());
+
+ /* Gather all of the face indices in each island into separate vectors. */
+ for (const int poly_index : face_selection) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const int island_id = disjoint_set.find_root(poly_loops[0].v);
+ const int island_index = island_ids.index_of_or_add(island_id);
+ if (island_index == islands.size()) {
+ islands.append_as();
+ }
+ ElementIsland &island = islands[island_index];
+ island.element_indices.append(poly_index);
+ }
+
+ return islands;
+}
+
+static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices)
+{
+ const MPoly &poly = mesh.mpoly[face_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ for (const MLoop &loop : poly_loops) {
+ r_vertex_indices.add(loop.v);
+ }
+}
+
+static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator,
+ const AxisScaleFields &fields)
+{
+ AxisScaleParams out;
+ evaluator.set_selection(fields.selection);
+ evaluator.add(fields.scale, &out.scales);
+ evaluator.add(fields.center, &out.centers);
+ evaluator.add(fields.axis, &out.axis_vectors);
+ evaluator.evaluate();
+ out.selection = evaluator.get_evaluated_selection_as_mask();
+ return out;
+}
+
+static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{field_context, mesh.totpoly};
+ AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
+ scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices);
+}
+
+static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator,
+ const UniformScaleFields &fields)
+{
+ UniformScaleParams out;
+ evaluator.set_selection(fields.selection);
+ evaluator.add(fields.scale, &out.scales);
+ evaluator.add(fields.center, &out.centers);
+ evaluator.evaluate();
+ out.selection = evaluator.get_evaluated_selection_as_mask();
+ return out;
+}
+
+static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{field_context, mesh.totpoly};
+ UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
+ scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices);
+}
+
+static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
+{
+ /* Use the disjoing set data structure to determine which vertices have to be scaled together. */
+ DisjointSet disjoint_set(mesh.totvert);
+ for (const int edge_index : edge_selection) {
+ const MEdge &edge = mesh.medge[edge_index];
+ disjoint_set.join(edge.v1, edge.v2);
+ }
+
+ VectorSet<int> island_ids;
+ Vector<ElementIsland> islands;
+ /* There are at most as many islands as there are selected edges. */
+ islands.reserve(edge_selection.size());
+
+ /* Gather all of the edge indices in each island into separate vectors. */
+ for (const int edge_index : edge_selection) {
+ const MEdge &edge = mesh.medge[edge_index];
+ const int island_id = disjoint_set.find_root(edge.v1);
+ const int island_index = island_ids.index_of_or_add(island_id);
+ if (island_index == islands.size()) {
+ islands.append_as();
+ }
+ ElementIsland &island = islands[island_index];
+ island.element_indices.append(edge_index);
+ }
+
+ return islands;
+}
+
+static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices)
+{
+ const MEdge &edge = mesh.medge[edge_index];
+ r_vertex_indices.add(edge.v1);
+ r_vertex_indices.add(edge.v2);
+}
+
+static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, mesh.totedge};
+ UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
+ scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices);
+}
+
+static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, mesh.totedge};
+ AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
+ scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const bNode &node = params.node();
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom1);
+ const GeometryNodeScaleElementsMode scale_mode = static_cast<GeometryNodeScaleElementsMode>(
+ node.custom2);
+
+ GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
+
+ Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
+ Field<float> scale_field = params.get_input<Field<float>>("Scale");
+ Field<float3> center_field = params.get_input<Field<float3>>("Center");
+ Field<float3> axis_field;
+ if (scale_mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS) {
+ axis_field = params.get_input<Field<float3>>("Axis");
+ }
+
+ geometry.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (!geometry.has_mesh()) {
+ return;
+ }
+ MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_faces_on_axis(mesh_component,
+ {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_edges_on_axis(mesh_component,
+ {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+
+ params.set_output("Geometry", std::move(geometry));
+}
+
+} // namespace blender::nodes::node_geo_scale_elements_cc
+
+void register_node_type_geo_scale_elements()
+{
+ namespace file_ns = blender::nodes::node_geo_scale_elements_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SCALE_ELEMENTS, "Scale Elements", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.initfunc = file_ns::node_init;
+ ntype.updatefunc = file_ns::node_update;
+ nodeRegisterType(&ntype);
+}