Compare commits
9 Commits
temp-outli
...
temp-scale
Author | SHA1 | Date | |
---|---|---|---|
0375b51776 | |||
32e8381e0e | |||
c31a346ec5 | |||
00c5fa3bf1 | |||
2f6a84bee9 | |||
f9e03a5e16 | |||
abaed315d5 | |||
05a7f7c1fd | |||
7e3459ff78 |
@@ -149,6 +149,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")
|
||||
|
@@ -1710,6 +1710,7 @@ 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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -4970,6 +4970,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();
|
||||
|
@@ -30,6 +30,7 @@ class DisjointSet {
|
||||
private:
|
||||
Array<int64_t> parents_;
|
||||
Array<int64_t> ranks_;
|
||||
bool all_roots_ensured_ = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -49,6 +50,8 @@ class DisjointSet {
|
||||
*/
|
||||
void join(int64_t x, int64_t y)
|
||||
{
|
||||
BLI_assert_msg(!all_roots_ensured_, "Cannot join after `ensure_all_roots` has been called.");
|
||||
|
||||
int64_t root1 = this->find_root(x);
|
||||
int64_t root2 = this->find_root(y);
|
||||
|
||||
@@ -98,6 +101,16 @@ class DisjointSet {
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Span<int64_t> ensure_all_roots()
|
||||
{
|
||||
for (const int64_t i : parents_.index_range()) {
|
||||
/* Updates `parents_` when doing path compression. */
|
||||
this->find_root(i);
|
||||
}
|
||||
all_roots_ensured_ = true;
|
||||
return parents_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
@@ -2315,6 +2315,12 @@ 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,
|
||||
GEO_NODE_SCALE_ELEMENTS_MODE_CURVE = 2,
|
||||
} GeometryNodeScaleElementsMode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -11287,6 +11287,33 @@ 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"},
|
||||
{GEO_NODE_SCALE_ELEMENTS_MODE_CURVE, "CURVE", ICON_NONE, "Curve", "Scale individual curves"},
|
||||
{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");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
|
@@ -153,6 +153,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);
|
||||
|
@@ -402,6 +402,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", "")
|
||||
|
@@ -164,6 +164,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
|
||||
|
201
source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
Normal file
201
source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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_disjoint_set.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.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::Vector>(N_("Scale")).default_value({1.0f, 1.0f, 1.0f}).supports_field();
|
||||
b.add_input<decl::Vector>(N_("Pivot")).subtype(PROP_TRANSLATION).implicit_field();
|
||||
b.add_input<decl::Vector>(N_("X Axis")).default_value({1.0f, 0.0f, 0.0f}).supports_field();
|
||||
b.add_input<decl::Vector>(N_("Up")).default_value({0.0f, 0.0f, 1.0f}).supports_field();
|
||||
b.add_output<decl::Geometry>(N_("Geometry"));
|
||||
};
|
||||
|
||||
struct InputFields {
|
||||
Field<bool> selection;
|
||||
Field<float3> scale;
|
||||
Field<float3> pivot;
|
||||
Field<float3> x_axis;
|
||||
Field<float3> up;
|
||||
};
|
||||
|
||||
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};
|
||||
evaluator.set_selection(input_fields.selection);
|
||||
VArray<float3> scales;
|
||||
VArray<float3> pivots;
|
||||
VArray<float3> x_axis_vectors;
|
||||
VArray<float3> up_vectors;
|
||||
evaluator.add(input_fields.scale, &scales);
|
||||
evaluator.add(input_fields.pivot, &pivots);
|
||||
evaluator.add(input_fields.x_axis, &x_axis_vectors);
|
||||
evaluator.add(input_fields.up, &up_vectors);
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
||||
|
||||
DisjointSet disjoint_set(mesh->totvert);
|
||||
for (const int poly_index : 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);
|
||||
}
|
||||
|
||||
const Span<int64_t> group_by_vertex_index = disjoint_set.ensure_all_roots();
|
||||
|
||||
struct GroupData {
|
||||
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};
|
||||
int tot_faces = 0;
|
||||
};
|
||||
|
||||
const int group_amount = mesh->totvert;
|
||||
Array<GroupData> groups_data(group_amount);
|
||||
for (const int poly_index : selection) {
|
||||
const MPoly &poly = mesh->mpoly[poly_index];
|
||||
const int first_vertex = mesh->mloop[poly.loopstart].v;
|
||||
const int group_index = group_by_vertex_index[first_vertex];
|
||||
GroupData &group_info = groups_data[group_index];
|
||||
group_info.pivot += pivots[poly_index];
|
||||
group_info.scale += scales[poly_index];
|
||||
group_info.x_axis += x_axis_vectors[poly_index];
|
||||
group_info.up += up_vectors[poly_index];
|
||||
group_info.tot_faces++;
|
||||
}
|
||||
|
||||
Array<float4x4> transforms(group_amount);
|
||||
threading::parallel_for(IndexRange(mesh->totvert), 1024, [&](const IndexRange range) {
|
||||
for (const int vert_index : range) {
|
||||
GroupData &group_data = groups_data[vert_index];
|
||||
if (group_data.tot_faces == 0) {
|
||||
transforms[vert_index] = float4x4::identity();
|
||||
continue;
|
||||
}
|
||||
|
||||
const float f = 1.0f / group_data.tot_faces;
|
||||
group_data.scale *= f;
|
||||
group_data.pivot *= f;
|
||||
|
||||
const float3 x_axis = group_data.x_axis.normalized();
|
||||
const float3 y_axis = -float3::cross(x_axis, group_data.up).normalized();
|
||||
const float3 z_axis = float3::cross(x_axis, y_axis);
|
||||
|
||||
const float3 pivot = group_data.pivot;
|
||||
|
||||
float4x4 &transform = transforms[vert_index];
|
||||
unit_m4(transform.values);
|
||||
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);
|
||||
|
||||
float4x4 axis_transform_inv = axis_transform.transposed();
|
||||
|
||||
float4x4 scale_transform;
|
||||
unit_m4(scale_transform.values);
|
||||
scale_transform.values[0][0] = group_data.scale.x;
|
||||
scale_transform.values[1][1] = group_data.scale.y;
|
||||
scale_transform.values[2][2] = group_data.scale.z;
|
||||
|
||||
transform = axis_transform * scale_transform * axis_transform_inv * transform;
|
||||
add_v3_v3(transform.values[3], pivot);
|
||||
}
|
||||
});
|
||||
|
||||
threading::parallel_for(IndexRange(mesh->totvert), 1024, [&](const IndexRange range) {
|
||||
for (const int vert_index : range) {
|
||||
const int group_index = group_by_vertex_index[vert_index];
|
||||
MVert &vert = mesh->mvert[vert_index];
|
||||
const float3 old_position = vert.co;
|
||||
const float3 new_position = transforms[group_index] * old_position;
|
||||
copy_v3_v3(vert.co, new_position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>(
|
||||
params.node().custom1);
|
||||
|
||||
GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
|
||||
InputFields input_fields;
|
||||
input_fields.selection = params.get_input<Field<bool>>("Selection");
|
||||
input_fields.scale = params.get_input<Field<float3>>("Scale");
|
||||
input_fields.pivot = params.get_input<Field<float3>>("Pivot");
|
||||
input_fields.x_axis = params.get_input<Field<float3>>("X Axis");
|
||||
input_fields.up = params.get_input<Field<float3>>("Up");
|
||||
|
||||
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: {
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SCALE_ELEMENTS_MODE_CURVE: {
|
||||
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;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -59,7 +59,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const NodeGeometrySeparateGeometry &storage = node_storage(params.node());
|
||||
const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
|
||||
|
||||
auto separate_geometry_maybe_recursively = [&](bool invert) {
|
||||
auto separate_geometry_maybe_recursively = [&](GeometrySet &geometry_set, bool invert) {
|
||||
bool is_error;
|
||||
if (domain == ATTR_DOMAIN_INSTANCE) {
|
||||
/* Only delete top level instances. */
|
||||
@@ -84,11 +84,11 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
GeometrySet second_set(geometry_set);
|
||||
if (params.output_is_required("Selection")) {
|
||||
separate_geometry_maybe_recursively(false);
|
||||
separate_geometry_maybe_recursively(geometry_set, false);
|
||||
params.set_output("Selection", std::move(geometry_set));
|
||||
}
|
||||
if (params.output_is_required("Inverted")) {
|
||||
separate_geometry_maybe_recursively(true);
|
||||
separate_geometry_maybe_recursively(second_set, true);
|
||||
params.set_output("Inverted", std::move(second_set));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user