diff options
-rw-r--r-- | release/scripts/startup/nodeitems_builtins.py | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 47 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_static_types.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc | 485 |
9 files changed, 543 insertions, 0 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 772668cb3fb..b8099127a55 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -150,6 +150,7 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeSubdivideMesh") yield NodeItem("GeometryNodeSubdivisionSurface") yield NodeItem("GeometryNodeTriangulate") + yield NodeItem("GeometryNodeScaleElements") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputMeshEdgeAngle") yield NodeItem("GeometryNodeInputMeshEdgeNeighbors") diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ccea76fc24d..3b952204755 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1631,6 +1631,7 @@ 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_SCALE_ELEMENTS 1151 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 9ee54c9ebeb..1acd4248639 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4825,6 +4825,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 a23e45f8b74..10058204e88 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2341,6 +2341,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 adb6b1e4e34..c506c35e281 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -11422,6 +11422,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/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index ff4036308c6..9c9e3876662 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -156,6 +156,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 53d6d64ef84..06a1cf90fca 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -407,6 +407,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 37d7843020a..37b43c26a86 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -167,6 +167,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 ¢er, + 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 ¢er, + 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 ¶ms, + 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 ¶ms, + 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); +} |