Compare commits

...

23 Commits

Author SHA1 Message Date
efcaac7ea6 add extrude node (by Hans) 2022-01-11 18:04:37 +01:00
c9df2017dc cleanup 2022-01-11 17:55:49 +01:00
101a298775 cleanup 2022-01-11 17:35:27 +01:00
d86becbf67 Merge branch 'master' into temp-scale-elements-node-test 2022-01-11 17:04:01 +01:00
b39b552ded add descriptions 2022-01-10 20:54:21 +01:00
7bd29bc0ea cleanup 2022-01-10 20:28:12 +01:00
409bcdc102 cleanup 2022-01-10 20:12:06 +01:00
1438398488 improve naming 2022-01-10 20:01:32 +01:00
1385f3af9e deduplicate code 2022-01-10 19:52:38 +01:00
901237f8de cleanup 2022-01-10 19:43:49 +01:00
becd9d7f99 cleanup 2022-01-10 19:24:47 +01:00
e15c20fa3e remove curve mode 2022-01-10 18:49:32 +01:00
0a9a9190a1 add uniform option 2022-01-10 18:46:46 +01:00
3cd76cc27b add edge scaling 2022-01-10 18:03:14 +01:00
38f5fcc94d Merge branch 'master' into temp-scale-elements-node-test 2022-01-10 17:27:48 +01:00
32e8381e0e Geometry Nodes: Experimental Scale Elements node (WIP).
This node is best used in combination with e.g. the Extrude node.

Differential Revision: https://developer.blender.org/D13757
2022-01-06 18:19:06 +01:00
c31a346ec5 progress 2022-01-06 17:01:17 +01:00
00c5fa3bf1 progress 2022-01-06 16:13:56 +01:00
2f6a84bee9 progress 2022-01-06 15:04:53 +01:00
f9e03a5e16 Merge branch 'master' into temp-scale-elements-node-test 2022-01-06 14:32:40 +01:00
abaed315d5 initial scale faces mode 2022-01-04 20:13:35 +01:00
05a7f7c1fd Merge branch 'master' into scale-elements-node-test 2022-01-04 17:41:45 +01:00
7e3459ff78 initial commit 2022-01-04 13:59:47 +01:00
15 changed files with 1823 additions and 2 deletions

View File

@@ -142,6 +142,7 @@ def mesh_node_items(context):
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeDualMesh")
yield NodeItem("GeometryNodeExtrudeMesh")
yield NodeItem("GeometryNodeMeshBoolean")
yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
@@ -149,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")

View File

@@ -25,6 +25,12 @@
#include "BKE_mesh_types.h"
#include "BLI_utildefines.h"
#ifdef __cplusplus
# include "BLI_span.hh"
# include "DNA_mesh_types.h"
# include "DNA_meshdata_types.h"
#endif
struct BLI_Stack;
struct BMesh;
struct BMeshCreateParams;
@@ -979,3 +985,47 @@ BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly,
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
namespace blender::bke {
inline Span<MVert> mesh_verts(const Mesh &mesh)
{
return {mesh.mvert, mesh.totvert};
}
inline MutableSpan<MVert> mesh_verts(Mesh &mesh)
{
return {mesh.mvert, mesh.totvert};
}
inline Span<MEdge> mesh_edges(const Mesh &mesh)
{
return {mesh.medge, mesh.totedge};
}
inline MutableSpan<MEdge> mesh_edges(Mesh &mesh)
{
return {mesh.medge, mesh.totedge};
}
inline Span<MPoly> mesh_polys(const Mesh &mesh)
{
return {mesh.mpoly, mesh.totpoly};
}
inline MutableSpan<MPoly> mesh_polys(Mesh &mesh)
{
return {mesh.mpoly, mesh.totpoly};
}
inline Span<MLoop> mesh_loops(const Mesh &mesh)
{
return {mesh.mloop, mesh.totloop};
}
inline MutableSpan<MLoop> mesh_loops(Mesh &mesh)
{
return {mesh.mloop, mesh.totloop};
}
} // namespace blender::bke
#endif

View File

@@ -1633,6 +1633,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_INPUT_SCENE_TIME 1145
#define GEO_NODE_ACCUMULATE_FIELD 1146
#define GEO_NODE_INPUT_MESH_EDGE_ANGLE 1147
#define GEO_NODE_SCALE_ELEMENTS 1148
#define GEO_NODE_EXTRUDE_MESH 1149
/** \} */

View File

@@ -2227,7 +2227,7 @@ void CustomData_realloc(CustomData *data, int totelem)
continue;
}
typeInfo = layerType_getInfo(layer->type);
layer->data = MEM_reallocN(layer->data, (size_t)totelem * typeInfo->size);
layer->data = MEM_recallocN(layer->data, (size_t)totelem * typeInfo->size);
}
}

View File

@@ -30,6 +30,8 @@
#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "BLT_translation.h"
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
@@ -611,6 +613,8 @@ bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const
/** \} */
/** \} */
/* -------------------------------------------------------------------- */
/** \name C API
* \{ */

View File

@@ -4766,6 +4766,7 @@ static void registerGeometryNodes()
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_dual_mesh();
register_node_type_geo_edge_split();
register_node_type_geo_extrude_mesh();
register_node_type_geo_geometry_to_instance();
register_node_type_geo_image_texture();
register_node_type_geo_input_curve_handles();
@@ -4821,6 +4822,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();

View File

@@ -96,7 +96,7 @@ std::ostream &operator<<(std::ostream &stream, const eSpace &space);
/** Template class to store RGBA values with different precision, space and alpha association. */
template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
public:
ChannelStorageType r, g, b, a;
ChannelStorageType r = 0, g = 0, b = 0, a = 1;
constexpr ColorRGBA() = default;
constexpr ColorRGBA(const ChannelStorageType rgba[4])

View File

@@ -1385,6 +1385,11 @@ typedef struct NodeGeometryPointTranslate {
uint8_t input_type;
} NodeGeometryPointTranslate;
typedef struct NodeGeometryExtrudeMesh {
/* GeometryNodeExtrudeMeshMode */
uint8_t mode;
} NodeGeometryExtrudeMesh;
typedef struct NodeGeometryObjectInfo {
/* GeometryNodeTransformSpace. */
uint8_t transform_space;
@@ -2149,6 +2154,12 @@ typedef enum GeometryNodeDistributePointsOnFacesMode {
GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1,
} GeometryNodeDistributePointsOnFacesMode;
typedef enum GeometryNodeExtrudeMeshMode {
GEO_NODE_EXTRUDE_MESH_VERTICES = 0,
GEO_NODE_EXTRUDE_MESH_EDGES = 1,
GEO_NODE_EXTRUDE_MESH_FACES = 2,
} GeometryNodeExtrudeMeshMode;
typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_EULER = 0,
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
@@ -2330,6 +2341,13 @@ typedef enum GeometryNodeRealizeInstancesFlag {
GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR = (1 << 0),
} GeometryNodeRealizeInstancesFlag;
typedef enum GeometryNodeScaleElementsMode {
GEO_NODE_SCALE_ELEMENTS_MODE_FACE = 0,
GEO_NODE_SCALE_ELEMENTS_MODE_EDGE = 1,
} GeometryNodeScaleElementsMode;
#define GEO_NODE_SCALE_ELEMENTS_UNIFORM 1
#ifdef __cplusplus
}
#endif

View File

@@ -9889,6 +9889,27 @@ static void def_geo_point_distribute(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_extrude_mesh(StructRNA *srna)
{
PropertyRNA *prop;
static const EnumPropertyItem mode_items[] = {
{GEO_NODE_EXTRUDE_MESH_VERTICES, "VERTICES", 0, "Vertices", ""},
{GEO_NODE_EXTRUDE_MESH_EDGES, "EDGES", 0, "Edges", ""},
{GEO_NODE_EXTRUDE_MESH_FACES, "FACES", 0, "Faces", ""},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryExtrudeMesh", "storage");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_enum_default(prop, GEO_NODE_EXTRUDE_MESH_FACES);
RNA_def_property_ui_text(prop, "Mode", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_distribute_points_on_faces(StructRNA *srna)
{
PropertyRNA *prop;
@@ -11372,6 +11393,37 @@ static void def_geo_realize_instances(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 scale_elements_mode_items[] = {
{GEO_NODE_SCALE_ELEMENTS_MODE_FACE,
"FACE",
ICON_NONE,
"Face",
"Scale individual faces or neighbouring face islands"},
{GEO_NODE_SCALE_ELEMENTS_MODE_EDGE,
"EDGE",
ICON_NONE,
"Edge",
"Scale individual edges or neighbouring edge islands"},
{0, NULL, 0, NULL, NULL},
};
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, scale_elements_mode_items);
RNA_def_property_enum_default(prop, GEO_NODE_SCALE_ELEMENTS_MODE_FACE);
RNA_def_property_ui_text(prop, "Mode", "Element type to transform");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
prop = RNA_def_property(srna, "uniform", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "custom2", GEO_NODE_SCALE_ELEMENTS_UNIFORM);
RNA_def_property_ui_text(prop, "Uniform", "Scale element by the same factor in every direction");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@@ -16,6 +16,9 @@
#include "MOD_nodes_evaluator.hh"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "BKE_type_conversions.hh"
#include "NOD_geometry_exec.hh"
@@ -381,6 +384,11 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
return true;
}
if (bnode.type == GEO_NODE_EXTRUDE_MESH) {
new (r_value)
ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>()));
return true;
}
new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
return true;
}

View File

@@ -98,6 +98,7 @@ void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_dual_mesh(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_extrude_mesh(void);
void register_node_type_geo_geometry_to_instance(void);
void register_node_type_geo_image_texture(void);
void register_node_type_geo_input_curve_handles(void);
@@ -153,6 +154,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);

View File

@@ -350,6 +350,7 @@ DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE
DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "")
DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
@@ -403,6 +404,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", "")

View File

@@ -116,6 +116,7 @@ set(SRC
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
nodes/node_geo_edge_split.cc
nodes/node_geo_extrude_mesh.cc
nodes/node_geo_geometry_to_instance.cc
nodes/node_geo_image_texture.cc
nodes/node_geo_input_curve_handles.cc
@@ -164,6 +165,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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,396 @@
/*
* 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_Float").default_value(1.0f).supports_field();
b.add_input<decl::Vector>(N_("Scale"), "Scale_Vector")
.default_value({1.0f, 1.0f, 1.0f})
.supports_field();
b.add_input<decl::Vector>(N_("Pivot"))
.subtype(PROP_TRANSLATION)
.implicit_field()
.description(
N_("Origin of the scaling for each element. If multiple elements are scaled together, "
"their pivot is averaged"));
b.add_input<decl::Vector>(N_("X Axis"))
.default_value({1.0f, 0.0f, 0.0f})
.supports_field()
.description(N_("Direction which the x component of the scale input changes the element"));
b.add_input<decl::Vector>(N_("Up"))
.default_value({0.0f, 0.0f, 1.0f})
.supports_field()
.description(N_("Direction which the z component of the scale input changes the element (or "
"approximate direction when not orthogonal to x axis)"));
b.add_output<decl::Geometry>(N_("Geometry"));
};
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "uniform", 0, nullptr, ICON_NONE);
}
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = GEO_NODE_SCALE_ELEMENTS_MODE_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 *scale_vector_socket = scale_float_socket->next;
bNodeSocket *pivot_socket = scale_vector_socket->next;
bNodeSocket *x_axis_socket = pivot_socket->next;
bNodeSocket *up_socket = x_axis_socket->next;
const bool use_uniform_scale = node->custom2 & GEO_NODE_SCALE_ELEMENTS_UNIFORM;
nodeSetSocketAvailability(ntree, scale_float_socket, use_uniform_scale);
nodeSetSocketAvailability(ntree, scale_vector_socket, !use_uniform_scale);
nodeSetSocketAvailability(ntree, x_axis_socket, !use_uniform_scale);
nodeSetSocketAvailability(ntree, up_socket, !use_uniform_scale);
}
struct InputFields {
bool use_uniform_scale;
Field<bool> selection;
Field<float> uniform_scale;
Field<float3> vector_scale;
Field<float3> pivot;
Field<float3> x_axis;
Field<float3> up;
};
struct EvaluatedFields {
IndexMask selection;
VArray<float> uniform_scales;
VArray<float3> vector_scales;
VArray<float3> pivots;
VArray<float3> x_axis_vectors;
VArray<float3> up_vectors;
};
/**
* When multiple elements share the same vertices, they are scaled together.
*/
struct ElementIsland {
/* Either face or edge indices. */
Vector<int> element_indices;
};
/**
* Prepares a matrix that can be used to transform every point in an island.
*/
static float4x4 create_transform(const float3 &pivot,
float3 x_axis,
const float3 &up,
const float3 &scale)
{
/* Compute main scale axis. */
x_axis = x_axis.normalized();
const float3 y_axis = -float3::cross(x_axis, up).normalized();
const float3 z_axis = float3::cross(x_axis, y_axis);
float4x4 transform;
unit_m4(transform.values);
/* Move pivot to origin. */
sub_v3_v3(transform.values[3], pivot);
float4x4 axis_transform;
unit_m4(axis_transform.values);
copy_v3_v3(axis_transform.values[0], x_axis);
copy_v3_v3(axis_transform.values[1], y_axis);
copy_v3_v3(axis_transform.values[2], z_axis);
/* Can invert by transposing, because the matrix is orthonormal. */
float4x4 axis_transform_inv = axis_transform.transposed();
/* Prepare scale matrix. */
float4x4 scale_transform;
unit_m4(scale_transform.values);
scale_transform.values[0][0] = scale.x;
scale_transform.values[1][1] = scale.y;
scale_transform.values[2][2] = scale.z;
/* Do the scaling in the right space. */
transform = axis_transform * scale_transform * axis_transform_inv * transform;
/* Move pivot back to where it was. */
add_v3_v3(transform.values[3], pivot);
return transform;
}
static EvaluatedFields evaluate_fields(FieldEvaluator &evaluator, const InputFields &input_fields)
{
EvaluatedFields evaluated;
evaluator.set_selection(input_fields.selection);
if (input_fields.use_uniform_scale) {
evaluator.add(input_fields.uniform_scale, &evaluated.uniform_scales);
}
else {
evaluator.add(input_fields.vector_scale, &evaluated.vector_scales);
evaluator.add(input_fields.x_axis, &evaluated.x_axis_vectors);
evaluator.add(input_fields.up, &evaluated.up_vectors);
}
evaluator.add(input_fields.pivot, &evaluated.pivots);
evaluator.evaluate();
evaluated.selection = evaluator.get_evaluated_selection_as_mask();
return evaluated;
}
static void scale_vertex_islands(
Mesh &mesh,
const Span<ElementIsland> islands,
const EvaluatedFields &evaluated,
const FunctionRef<void(int element_index, Vector<int> &r_vertex_indices)> get_vertex_indices)
{
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
Set<int> handled_vertices;
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
float3 scale = {0.0f, 0.0f, 0.0f};
float3 pivot = {0.0f, 0.0f, 0.0f};
float3 x_axis = {0.0f, 0.0f, 0.0f};
float3 up = {0.0f, 0.0f, 0.0f};
Vector<int> vertex_indices;
for (const int poly_index : island.element_indices) {
get_vertex_indices(poly_index, vertex_indices);
pivot += evaluated.pivots[poly_index];
if (evaluated.uniform_scales) {
scale += float3(evaluated.uniform_scales[poly_index]);
x_axis += float3(1, 0, 0);
up += float3(0, 0, 1);
}
else {
scale += evaluated.vector_scales[poly_index];
x_axis += evaluated.x_axis_vectors[poly_index];
up += evaluated.up_vectors[poly_index];
}
}
/* Divide by number of elements to get the average. */
const float f = 1.0f / island.element_indices.size();
scale *= f;
pivot *= f;
x_axis *= f;
up *= f;
const float4x4 transform = create_transform(pivot, x_axis, up, scale);
handled_vertices.clear();
for (const int vert_index : vertex_indices) {
if (!handled_vertices.add(vert_index)) {
/* This vertex has been transformed already. */
continue;
}
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 disjoing 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());
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 scale_faces(MeshComponent &mesh_component, const InputFields &input_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};
EvaluatedFields evaluated = evaluate_fields(evaluator, input_fields);
Vector<ElementIsland> island = prepare_face_islands(mesh, evaluated.selection);
scale_vertex_islands(
mesh, island, evaluated, [&](int face_index, Vector<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.append(loop.v);
}
});
}
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());
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 scale_edges(MeshComponent &mesh_component, const InputFields &input_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};
EvaluatedFields evaluated = evaluate_fields(evaluator, input_fields);
Vector<ElementIsland> island = prepare_edge_islands(mesh, evaluated.selection);
scale_vertex_islands(
mesh, island, evaluated, [&](const int edge_index, Vector<int> &r_vertex_indices) {
const MEdge &edge = mesh.medge[edge_index];
r_vertex_indices.append(edge.v1);
r_vertex_indices.append(edge.v2);
});
}
static void node_geo_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>(
node.custom1);
GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
InputFields input_fields;
input_fields.use_uniform_scale = node.custom2 & GEO_NODE_SCALE_ELEMENTS_UNIFORM;
input_fields.selection = params.get_input<Field<bool>>("Selection");
if (input_fields.use_uniform_scale) {
input_fields.uniform_scale = params.get_input<Field<float>>("Scale_Float");
}
else {
input_fields.vector_scale = params.get_input<Field<float3>>("Scale_Vector");
input_fields.x_axis = params.get_input<Field<float3>>("X Axis");
input_fields.up = params.get_input<Field<float3>>("Up");
}
input_fields.pivot = params.get_input<Field<float3>>("Pivot");
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
switch (mode) {
case GEO_NODE_SCALE_ELEMENTS_MODE_FACE: {
if (geometry.has_mesh()) {
MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
scale_faces(mesh_component, input_fields);
}
break;
}
case GEO_NODE_SCALE_ELEMENTS_MODE_EDGE: {
if (geometry.has_mesh()) {
MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
scale_edges(mesh_component, input_fields);
}
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);
}