Geometry Nodes: Initial basic curve data support

This patch adds initial curve support to geometry nodes. Currently
there is only one node available, the "Curve to Mesh" node, T87428.

However, the aim of the changes here is larger than just supporting
curve data in nodes-- it also uses the opportunity to add better spline
data structures, intended to replace the existing curve evaluation code.
The curve code in Blender is quite old, and it's generally regarded as
some of the messiest, hardest-to-understand code as well. The classes
in `BKE_spline.hh` aim to be faster, more extensible, and much more
easily understandable. Further explanation can be found in comments in
that file.

Initial builtin spline attributes are supported-- reading and writing
from the `cyclic` and `resolution` attributes works with any of the
attribute nodes. Also, only Z-up normal calculation is implemented
at the moment, and tilts do not apply yet.

**Limitations**
 - For now, you must bring curves into the node tree with an "Object
   Info" node. Changes to the curve modifier stack will come later.
 - Converting to a mesh is necessary to visualize the curve data.

Further progress can be tracked in: T87245
Higher level design document: https://wiki.blender.org/wiki/Modules/Physics_Nodes/Projects/EverythingNodes/CurveNodes

Differential Revision: https://developer.blender.org/D11091
This commit is contained in:
2021-05-03 12:29:17 -05:00
parent c9d81678d7
commit 8216b759e9
47 changed files with 2872 additions and 7 deletions

View File

@@ -160,6 +160,7 @@ set(SRC
geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_curve_to_mesh.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc

View File

@@ -48,6 +48,7 @@ void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);

View File

@@ -312,6 +312,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIB
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
/* undefine macros */
#undef DefNode

View File

@@ -188,6 +188,9 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
if (geometry_set.has<CurveComponent>()) {
align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -255,6 +255,9 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -108,6 +108,9 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
if (geometry_set.has<CurveComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
}
params.set_output("Geometry", std::move(geometry_set));
}

View File

@@ -127,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -334,6 +334,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -167,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
if (geometry_set.has<CurveComponent>()) {
attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
params,
source_name,
result_name,
data_type,
domain);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -143,6 +143,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -410,6 +410,9 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -279,6 +279,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -209,6 +209,9 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -253,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_calc_proximity(
geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -315,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
if (geometry_set.has<CurveComponent>()) {
randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
params,
attribute_name,
data_type,
operation,
seed);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
if (geometry_set.has<CurveComponent>()) {
remove_attribute(
geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -122,6 +122,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -148,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -525,6 +525,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
if (geometry_set.has<CurveComponent>()) {
attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}

View File

@@ -0,0 +1,312 @@
/*
* 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_float4x4.hh"
#include "BLI_timeit.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
{SOCK_GEOMETRY, N_("Profile Curve")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
{SOCK_GEOMETRY, N_("Mesh")},
{-1, ""},
};
namespace blender::nodes {
static void vert_extrude_to_mesh_data(const Spline &spline,
const float3 profile_vert,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
int &vert_offset,
int &edge_offset)
{
Span<float3> positions = spline.evaluated_positions();
for (const int i : IndexRange(positions.size() - 1)) {
MEdge &edge = r_edges[edge_offset++];
edge.v1 = vert_offset + i;
edge.v2 = vert_offset + i + 1;
edge.flag = ME_LOOSEEDGE;
}
if (spline.is_cyclic()) {
MEdge &edge = r_edges[edge_offset++];
edge.v1 = vert_offset;
edge.v2 = vert_offset + positions.size() - 1;
edge.flag = ME_LOOSEEDGE;
}
for (const int i : positions.index_range()) {
MVert &vert = r_verts[vert_offset++];
copy_v3_v3(vert.co, positions[i] + profile_vert);
}
}
static void mark_edges_sharp(MutableSpan<MEdge> edges)
{
for (MEdge &edge : edges) {
edge.flag |= ME_SHARP;
}
}
static void spline_extrude_to_mesh_data(const Spline &spline,
const Spline &profile_spline,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
MutableSpan<MLoop> r_loops,
MutableSpan<MPoly> r_polys,
int &vert_offset,
int &edge_offset,
int &loop_offset,
int &poly_offset)
{
const int spline_vert_len = spline.evaluated_points_size();
const int spline_edge_len = spline.evaluated_edges_size();
const int profile_vert_len = profile_spline.evaluated_points_size();
const int profile_edge_len = profile_spline.evaluated_edges_size();
if (spline_vert_len == 0) {
return;
}
if (profile_vert_len == 1) {
vert_extrude_to_mesh_data(spline,
profile_spline.evaluated_positions()[0],
r_verts,
r_edges,
vert_offset,
edge_offset);
return;
}
/* Add the edges running along the length of the curve, starting at each profile vertex. */
const int spline_edges_start = edge_offset;
for (const int i_profile : IndexRange(profile_vert_len)) {
for (const int i_ring : IndexRange(spline_edge_len)) {
const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
MEdge &edge = r_edges[edge_offset++];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = next_ring_vert_offset + i_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
/* Add the edges running along each profile ring. */
const int profile_edges_start = edge_offset;
for (const int i_ring : IndexRange(spline_vert_len)) {
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
for (const int i_profile : IndexRange(profile_edge_len)) {
const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
MEdge &edge = r_edges[edge_offset++];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = ring_vert_offset + i_next_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
/* Calculate poly and corner indices. */
for (const int i_ring : IndexRange(spline_edge_len)) {
const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
for (const int i_profile : IndexRange(profile_edge_len)) {
const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
MPoly &poly = r_polys[poly_offset++];
poly.loopstart = loop_offset;
poly.totloop = 4;
poly.flag = ME_SMOOTH;
MLoop &loop_a = r_loops[loop_offset++];
loop_a.v = ring_vert_offset + i_profile;
loop_a.e = ring_edge_start + i_profile;
MLoop &loop_b = r_loops[loop_offset++];
loop_b.v = ring_vert_offset + i_next_profile;
loop_b.e = next_spline_edge_start + i_ring;
MLoop &loop_c = r_loops[loop_offset++];
loop_c.v = next_ring_vert_offset + i_next_profile;
loop_c.e = next_ring_edge_offset + i_profile;
MLoop &loop_d = r_loops[loop_offset++];
loop_d.v = next_ring_vert_offset + i_profile;
loop_d.e = spline_edge_start + i_ring;
}
}
/* Calculate the positions of each profile ring profile along the spline. */
Span<float3> positions = spline.evaluated_positions();
Span<float3> tangents = spline.evaluated_tangents();
Span<float3> normals = spline.evaluated_normals();
Span<float3> profile_positions = profile_spline.evaluated_positions();
GVArray_Typed<float> radii{
spline.interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(spline.radii()))};
for (const int i_ring : IndexRange(spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
point_matrix.apply_scale(radii[i_ring]);
for (const int i_profile : IndexRange(profile_vert_len)) {
MVert &vert = r_verts[vert_offset++];
copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
}
}
/* Mark edge loops from sharp vector control points sharp. */
if (profile_spline.type() == Spline::Type::Bezier) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
Span<int> control_point_offsets = bezier_spline.control_point_offsets();
for (const int i : control_point_offsets.index_range()) {
if (bezier_spline.point_is_sharp(i)) {
mark_edges_sharp(r_edges.slice(
spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
}
}
}
}
static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
{
int profile_vert_total = 0;
int profile_edge_total = 0;
for (const SplinePtr &profile_spline : profile_curve.splines) {
profile_vert_total += profile_spline->evaluated_points_size();
profile_edge_total += profile_spline->evaluated_edges_size();
}
int vert_total = 0;
int edge_total = 0;
int poly_total = 0;
for (const SplinePtr &spline : curve.splines) {
const int spline_vert_len = spline->evaluated_points_size();
const int spline_edge_len = spline->evaluated_edges_size();
vert_total += spline_vert_len * profile_vert_total;
poly_total += spline_edge_len * profile_edge_total;
/* Add the ring edges, with one ring for every curve vertex, and the edge loops
* that run along the length of the curve, starting on the first profile. */
edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
}
const int corner_total = poly_total * 4;
if (vert_total == 0) {
return nullptr;
}
Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
mesh->flag |= ME_AUTOSMOOTH;
mesh->smoothresh = DEG2RADF(180.0f);
int vert_offset = 0;
int edge_offset = 0;
int loop_offset = 0;
int poly_offset = 0;
for (const SplinePtr &spline : curve.splines) {
for (const SplinePtr &profile_spline : profile_curve.splines) {
spline_extrude_to_mesh_data(*spline,
*profile_spline,
verts,
edges,
loops,
polys,
vert_offset,
edge_offset,
loop_offset,
poly_offset);
}
}
BKE_mesh_calc_normals(mesh);
return mesh;
}
static CurveEval get_curve_single_vert()
{
CurveEval curve;
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
spline->add_point(float3(0), 0, 0.0f);
curve.splines.append(std::move(spline));
return curve;
}
static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
curve_set = bke::geometry_set_realize_instances(curve_set);
profile_set = bke::geometry_set_realize_instances(profile_set);
if (!curve_set.has_curve()) {
params.set_output("Mesh", GeometrySet());
return;
}
const CurveEval *profile_curve = profile_set.get_curve_for_read();
static const CurveEval vert_curve = get_curve_single_vert();
Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
(profile_curve == nullptr) ? vert_curve : *profile_curve);
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
void register_node_type_geo_curve_to_mesh()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
nodeRegisterType(&ntype);
}

View File

@@ -17,6 +17,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -261,6 +262,40 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
{
Vector<CurveComponent *> src_components;
for (GeometrySet &geometry_set : src_geometry_sets) {
if (geometry_set.has_curve()) {
/* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
* in the case where the input spline has no other users, because the splines can be
* moved from the source curve rather than copied from a read-only source. Retrieving
* the curve for write will make a copy only when it has a user elsewhere. */
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
src_components.append(&component);
}
}
if (src_components.size() == 0) {
return;
}
if (src_components.size() == 1) {
result.add(*src_components[0]);
return;
}
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
CurveEval *dst_curve = new CurveEval();
for (CurveComponent *component : src_components) {
CurveEval *src_curve = component->get_for_write();
for (SplinePtr &spline : src_curve->splines) {
dst_curve->splines.append(std::move(spline));
}
}
dst_component.replace(dst_curve);
}
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -291,6 +326,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}

View File

@@ -209,6 +209,11 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
add_instances_from_geometry_component(
instances, *geometry_set.get_component_for_read<CurveComponent>(), params);
}
params.set_output("Geometry", std::move(geometry_set_out));
}

View File

@@ -186,6 +186,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
}
if (geometry_set_in.has<CurveComponent>()) {
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii);
}
const float max_radius = *std::max_element(radii.begin(), radii.end());
const float voxel_size = compute_voxel_size(params, positions, max_radius);

View File

@@ -24,6 +24,7 @@
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -152,6 +153,21 @@ static void transform_volume(Volume *volume,
#endif
}
static void transform_curve(CurveEval &curve,
const float3 translation,
const float3 rotation,
const float3 scale)
{
if (use_translate(rotation, scale)) {
curve.translate(translation);
}
else {
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
curve.transform(matrix);
}
}
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -179,6 +195,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
transform_volume(volume, translation, rotation, scale, params);
}
if (geometry_set.has_curve()) {
CurveEval *curve = geometry_set.get_curve_for_write();
transform_curve(*curve, translation, rotation, scale);
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes