Geometry Nodes: Curve Set Spline Type
This node sets the selected (or all) splines in curve to a chosen target spline type. Poly, Bezier, and NURB splines can be converted to any of the other types. This is meant to be a building block node, useful in many procedural situations. In the future the node could be optimized with multi-threading, or by avoiding copying in many cases, either by retrieving the curve for write access or by passing the raw vectors to the new splines where possible. With edits from Hans Goudey (@HooglyBoogly) Differential Revision: https://developer.blender.org/D12013
This commit is contained in:
parent
26f1a5e2c8
commit
0f45576590
|
@ -510,6 +510,7 @@ geometry_node_categories = [
|
|||
NodeItem("GeometryNodeCurveTrim"),
|
||||
NodeItem("GeometryNodeCurveLength"),
|
||||
NodeItem("GeometryNodeCurveReverse"),
|
||||
NodeItem("GeometryNodeCurveSplineType"),
|
||||
NodeItem("GeometryNodeCurveSetHandles"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
|
||||
|
|
|
@ -1475,6 +1475,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||
#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070
|
||||
#define GEO_NODE_CURVE_TRIM 1071
|
||||
#define GEO_NODE_CURVE_SET_HANDLES 1072
|
||||
#define GEO_NODE_CURVE_SPLINE_TYPE 1073
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -5149,6 +5149,7 @@ static void registerGeometryNodes()
|
|||
register_node_type_geo_curve_resample();
|
||||
register_node_type_geo_curve_reverse();
|
||||
register_node_type_geo_curve_set_handles();
|
||||
register_node_type_geo_curve_spline_type();
|
||||
register_node_type_geo_curve_subdivide();
|
||||
register_node_type_geo_curve_to_mesh();
|
||||
register_node_type_geo_curve_to_points();
|
||||
|
|
|
@ -1355,6 +1355,11 @@ typedef struct NodeSwitch {
|
|||
uint8_t input_type;
|
||||
} NodeSwitch;
|
||||
|
||||
typedef struct NodeGeometryCurveSplineType {
|
||||
/* GeometryNodeSplineType. */
|
||||
uint8_t spline_type;
|
||||
} NodeGeometryCurveSplineType;
|
||||
|
||||
typedef struct NodeGeometryCurveSetHandles {
|
||||
/* GeometryNodeCurveHandleType. */
|
||||
uint8_t handle_type;
|
||||
|
@ -1828,6 +1833,12 @@ typedef enum GeometryNodeBooleanOperation {
|
|||
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
|
||||
} GeometryNodeBooleanOperation;
|
||||
|
||||
typedef enum GeometryNodeSplineType {
|
||||
GEO_NODE_SPLINE_TYPE_BEZIER = 0,
|
||||
GEO_NODE_SPLINE_TYPE_NURBS = 1,
|
||||
GEO_NODE_SPLINE_TYPE_POLY = 2,
|
||||
} GeometryNodeSplineType;
|
||||
|
||||
typedef enum GeometryNodeCurvePrimitiveCircleMode {
|
||||
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS = 0,
|
||||
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1
|
||||
|
|
|
@ -9441,6 +9441,23 @@ static void def_geo_attribute_vector_rotate(StructRNA *srna)
|
|||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_curve_spline_type(StructRNA *srna)
|
||||
{
|
||||
static const EnumPropertyItem type_items[] = {
|
||||
{GEO_NODE_SPLINE_TYPE_BEZIER, "BEZIER", ICON_NONE, "Bezier", "Set the splines to Bezier"},
|
||||
{GEO_NODE_SPLINE_TYPE_NURBS, "NURBS", ICON_NONE, "NURBS", "Set the splines to NURBS"},
|
||||
{GEO_NODE_SPLINE_TYPE_POLY, "POLY", ICON_NONE, "Poly", "Set the splines to Poly"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
PropertyRNA *prop;
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSplineType", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "spline_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "spline_type");
|
||||
RNA_def_property_enum_items(prop, type_items);
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_curve_set_handles(StructRNA *srna)
|
||||
{
|
||||
static const EnumPropertyItem type_items[] = {
|
||||
|
|
|
@ -176,6 +176,7 @@ set(SRC
|
|||
geometry/nodes/node_geo_curve_resample.cc
|
||||
geometry/nodes/node_geo_curve_reverse.cc
|
||||
geometry/nodes/node_geo_curve_set_handles.cc
|
||||
geometry/nodes/node_geo_curve_spline_type.cc
|
||||
geometry/nodes/node_geo_curve_subdivide.cc
|
||||
geometry/nodes/node_geo_curve_to_mesh.cc
|
||||
geometry/nodes/node_geo_curve_to_points.cc
|
||||
|
|
|
@ -63,6 +63,7 @@ void register_node_type_geo_curve_primitive_star(void);
|
|||
void register_node_type_geo_curve_resample(void);
|
||||
void register_node_type_geo_curve_reverse(void);
|
||||
void register_node_type_geo_curve_set_handles(void);
|
||||
void register_node_type_geo_curve_spline_type(void);
|
||||
void register_node_type_geo_curve_subdivide(void);
|
||||
void register_node_type_geo_curve_to_mesh(void);
|
||||
void register_node_type_geo_curve_to_points(void);
|
||||
|
|
|
@ -303,6 +303,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR",
|
|||
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
|
||||
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* 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 "BKE_spline.hh"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_curve_spline_type_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Curve")},
|
||||
{SOCK_STRING, N_("Selection")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_curve_spline_type_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Curve")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void geo_node_curve_spline_type_layout(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE);
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN(
|
||||
sizeof(NodeGeometryCurveSplineType), __func__);
|
||||
|
||||
data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void scale_input_assign(const Span<T> input,
|
||||
const int scale,
|
||||
const int offset,
|
||||
const MutableSpan<T> r_output)
|
||||
{
|
||||
for (const int i : IndexRange(r_output.size())) {
|
||||
r_output[i] = input[i * scale + offset];
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void scale_output_assign(const Span<T> input,
|
||||
const int scale,
|
||||
const int offset,
|
||||
const MutableSpan<T> &r_output)
|
||||
{
|
||||
for (const int i : IndexRange(input.size())) {
|
||||
r_output[i * scale + offset] = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CopyFn>
|
||||
static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
|
||||
{
|
||||
input_spline.attributes.foreach_attribute(
|
||||
[&](StringRefNull name, const AttributeMetaData &meta_data) {
|
||||
std::optional<GSpan> src = input_spline.attributes.get_for_read(name);
|
||||
BLI_assert(src);
|
||||
if (!output_spline.attributes.create(name, meta_data.data_type)) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(name);
|
||||
if (!dst) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
copy_fn(*src, *dst);
|
||||
|
||||
return true;
|
||||
},
|
||||
ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
static SplinePtr convert_to_poly_spline(const Spline &input)
|
||||
{
|
||||
std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
|
||||
output->resize(input.positions().size());
|
||||
output->positions().copy_from(input.positions());
|
||||
output->radii().copy_from(input.radii());
|
||||
output->tilts().copy_from(input.tilts());
|
||||
Spline::copy_base_settings(input, *output);
|
||||
output->attributes = input.attributes;
|
||||
return output;
|
||||
}
|
||||
|
||||
static SplinePtr poly_to_nurbs(const Spline &input)
|
||||
{
|
||||
std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
|
||||
output->resize(input.positions().size());
|
||||
output->positions().copy_from(input.positions());
|
||||
output->radii().copy_from(input.radii());
|
||||
output->tilts().copy_from(input.tilts());
|
||||
output->weights().fill(1.0f);
|
||||
output->set_resolution(12);
|
||||
output->set_order(4);
|
||||
Spline::copy_base_settings(input, *output);
|
||||
output->knots_mode = NURBSpline::KnotsMode::Bezier;
|
||||
output->attributes = input.attributes;
|
||||
return output;
|
||||
}
|
||||
|
||||
static SplinePtr bezier_to_nurbs(const Spline &input)
|
||||
{
|
||||
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
|
||||
std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
|
||||
output->resize(input.size() * 3);
|
||||
|
||||
scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
|
||||
scale_output_assign(input.radii(), 3, 0, output->radii());
|
||||
scale_output_assign(input.tilts(), 3, 0, output->tilts());
|
||||
|
||||
scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
|
||||
scale_output_assign(input.radii(), 3, 1, output->radii());
|
||||
scale_output_assign(input.tilts(), 3, 1, output->tilts());
|
||||
|
||||
scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
|
||||
scale_output_assign(input.radii(), 3, 2, output->radii());
|
||||
scale_output_assign(input.tilts(), 3, 2, output->tilts());
|
||||
|
||||
Spline::copy_base_settings(input, *output);
|
||||
output->weights().fill(1.0f);
|
||||
output->set_resolution(12);
|
||||
output->set_order(4);
|
||||
output->set_cyclic(input.is_cyclic());
|
||||
output->knots_mode = NURBSpline::KnotsMode::Bezier;
|
||||
output->attributes.reallocate(output->size());
|
||||
copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
|
||||
scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
|
||||
scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
|
||||
});
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
static SplinePtr poly_to_bezier(const Spline &input)
|
||||
{
|
||||
std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
|
||||
output->resize(input.size());
|
||||
output->positions().copy_from(input.positions());
|
||||
output->radii().copy_from(input.radii());
|
||||
output->tilts().copy_from(input.tilts());
|
||||
output->handle_types_left().fill(BezierSpline::HandleType::Vector);
|
||||
output->handle_types_right().fill(BezierSpline::HandleType::Vector);
|
||||
output->set_resolution(12);
|
||||
Spline::copy_base_settings(input, *output);
|
||||
output->attributes = input.attributes;
|
||||
return output;
|
||||
}
|
||||
|
||||
static SplinePtr nurbs_to_bezier(const Spline &input)
|
||||
{
|
||||
const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
|
||||
std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
|
||||
output->resize(input.size() / 3);
|
||||
scale_input_assign<float3>(input.positions(), 3, 1, output->positions());
|
||||
scale_input_assign<float3>(input.positions(), 3, 0, output->handle_positions_left());
|
||||
scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right());
|
||||
scale_input_assign<float>(input.radii(), 3, 2, output->radii());
|
||||
scale_input_assign<float>(input.tilts(), 3, 2, output->tilts());
|
||||
output->handle_types_left().fill(BezierSpline::HandleType::Align);
|
||||
output->handle_types_right().fill(BezierSpline::HandleType::Align);
|
||||
output->set_resolution(nurbs_spline.resolution());
|
||||
Spline::copy_base_settings(input, *output);
|
||||
output->attributes.reallocate(output->size());
|
||||
copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
scale_input_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
|
||||
});
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
|
||||
{
|
||||
switch (input.type()) {
|
||||
case Spline::Type::Bezier:
|
||||
return input.copy();
|
||||
case Spline::Type::Poly:
|
||||
return poly_to_bezier(input);
|
||||
case Spline::Type::NURBS:
|
||||
if (input.size() < 6) {
|
||||
params.error_message_add(
|
||||
NodeWarningType::Info,
|
||||
TIP_("NURBS must have minimum of 6 points for Bezier Conversion"));
|
||||
return input.copy();
|
||||
}
|
||||
else {
|
||||
if (input.size() % 3 != 0) {
|
||||
params.error_message_add(NodeWarningType::Info,
|
||||
TIP_("NURBS must have multiples of 3 points for full Bezier "
|
||||
"conversion, curve truncated"));
|
||||
}
|
||||
return nurbs_to_bezier(input);
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
|
||||
static SplinePtr convert_to_nurbs(const Spline &input)
|
||||
{
|
||||
switch (input.type()) {
|
||||
case Spline::Type::NURBS:
|
||||
return input.copy();
|
||||
case Spline::Type::Bezier:
|
||||
return bezier_to_nurbs(input);
|
||||
case Spline::Type::Poly:
|
||||
return poly_to_nurbs(input);
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
|
||||
static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const NodeGeometryCurveSplineType *storage =
|
||||
(const NodeGeometryCurveSplineType *)params.node().storage;
|
||||
const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_output("Curve", geometry_set);
|
||||
return;
|
||||
}
|
||||
|
||||
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
|
||||
const CurveEval &curve = *curve_component->get_for_read();
|
||||
|
||||
const std::string selection_name = params.extract_input<std::string>("Selection");
|
||||
GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
|
||||
selection_name, ATTR_DOMAIN_CURVE, true);
|
||||
|
||||
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
|
||||
for (const int i : curve.splines().index_range()) {
|
||||
if (selection[i]) {
|
||||
switch (output_type) {
|
||||
case GEO_NODE_SPLINE_TYPE_POLY:
|
||||
new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
|
||||
break;
|
||||
case GEO_NODE_SPLINE_TYPE_BEZIER:
|
||||
new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
|
||||
break;
|
||||
case GEO_NODE_SPLINE_TYPE_NURBS:
|
||||
new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_curve->add_spline(curve.splines()[i]->copy());
|
||||
}
|
||||
}
|
||||
|
||||
new_curve->attributes = curve.attributes;
|
||||
params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_curve_spline_type()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
geo_node_type_base(
|
||||
&ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
|
||||
node_type_socket_templates(
|
||||
&ntype, geo_node_curve_spline_type_in, geo_node_curve_spline_type_out);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec;
|
||||
node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init);
|
||||
node_type_storage(&ntype,
|
||||
"NodeGeometryCurveSplineType",
|
||||
node_free_standard_storage,
|
||||
node_copy_standard_storage);
|
||||
ntype.draw_buttons = geo_node_curve_spline_type_layout;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
Loading…
Reference in New Issue