WIP: Geometry Nodes: new Bridge Curves node #114014

Draft
Jacques Lucke wants to merge 4 commits from JacquesLucke/blender:bridge-curves into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
5 changed files with 165 additions and 0 deletions

View File

@ -113,6 +113,7 @@ class NODE_MT_geometry_node_GEO_CURVE_OPERATIONS(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeBridgeCurves")
node_add_menu.add_node_type(layout, "GeometryNodeCurveToMesh")
node_add_menu.add_node_type(layout, "GeometryNodeCurveToPoints")
node_add_menu.add_node_type(layout, "GeometryNodeDeformCurvesOnSurface")

View File

@ -1315,6 +1315,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_POINTS_TO_CURVES 2114
#define GEO_NODE_INPUT_EDGE_SMOOTH 2115
#define GEO_NODE_SPLIT_TO_INSTANCES 2116
#define GEO_NODE_BRIDGE_CURVES 2117
/** \} */

View File

@ -368,6 +368,7 @@ DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", Inst
DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",InstancesToPoints, "Instances to Points", "Generate points at the origins of instances.\nNote: Nested instances are not affected by this node")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one")
DefNode(GeometryNode, GEO_NODE_BRIDGE_CURVES, 0, "BRIDGE_CURVES", BridgeCurves, "Bridge Curves", "Create a mesh between the provided curves")
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
DefNode(GeometryNode, GEO_NODE_MEAN_FILTER_SDF_VOLUME, 0, "MEAN_FILTER_SDF_VOLUME", MeanFilterSDFVolume, "Mean Filter SDF Volume", "Smooth the surface of an SDF volume by applying a mean filter")
DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, 0, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")

View File

@ -35,6 +35,7 @@ set(SRC
nodes/node_geo_blur_attribute.cc
nodes/node_geo_boolean.cc
nodes/node_geo_bounding_box.cc
nodes/node_geo_bridge_curves.cc
nodes/node_geo_collection_info.cc
nodes/node_geo_common.cc
nodes/node_geo_convex_hull.cc

View File

@ -0,0 +1,161 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_curves.hh"
#include "BKE_mesh.hh"
#include "BLI_array_utils.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_bridge_curves_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curves");
b.add_input<decl::Bool>("Selection").default_value(true).field_on_all().hide_value();
b.add_input<decl::Bool>("Cyclic").field_on_all();
b.add_input<decl::Int>("Group ID").hide_value().field_on_all();
b.add_input<decl::Float>("Weight").field_on_all().hide_value();
b.add_output<decl::Geometry>("Mesh").propagate_all();
}
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet curves_geometry = params.extract_input<GeometrySet>("Curves");
GeometrySet mesh_geometry;
if (const Curves *curves_id = curves_geometry.get_curves()) {
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const Span<float3> evaluated_curve_positions = curves.evaluated_positions();
const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
const int curves_num = curves.curves_num();
Array<int> curve_indices_to_bridge(curves_num);
array_utils::fill_index_range<int>(curve_indices_to_bridge);
const bool bridge_cyclic = params.get_input<bool>("Cyclic");
const int bridges_num = bke::curves::segments_num(curve_indices_to_bridge.size(),
bridge_cyclic);
Array<int> bridge_face_offsets(curves_num);
Array<int> bridge_corner_offsets(curves_num);
int face_counter = 0;
int corner_counter = 0;
for (const int i : IndexRange(bridges_num)) {
const int next_i = (i + 1) % curve_indices_to_bridge.size();
const int curve_i = curve_indices_to_bridge[i];
const int next_curve_i = curve_indices_to_bridge[next_i];
const bool is_cyclic = cyclic[curve_i];
const bool next_is_cyclic = cyclic[next_curve_i];
const IndexRange points = evaluated_points_by_curve[curve_i];
const IndexRange next_points = evaluated_points_by_curve[next_curve_i];
bridge_face_offsets[curve_i] = face_counter;
bridge_corner_offsets[curve_i] = corner_counter;
if (points.is_empty() || next_points.is_empty()) {
continue;
}
const int segments_num = bke::curves::segments_num(points.size(), is_cyclic);
const int next_segments_num = bke::curves::segments_num(next_points.size(), next_is_cyclic);
const int bridge_face_num = std::max(segments_num, next_segments_num);
const int bridge_quad_num = std::min(segments_num, next_segments_num);
const int bridge_tri_num = bridge_face_num - bridge_quad_num;
const int bridge_corner_num = 4 * bridge_quad_num + 3 * bridge_tri_num;
face_counter += bridge_face_num;
corner_counter += bridge_corner_num;
}
Mesh *mesh = BKE_mesh_new_nomain(
evaluated_curve_positions.size(), 0, face_counter, corner_counter);
MutableSpan<int> dst_face_offsets = mesh->face_offsets_for_write();
MutableSpan<int> dst_corner_verts = mesh->corner_verts_for_write();
MutableSpan<float3> mesh_positions = mesh->vert_positions_for_write();
mesh_positions.copy_from(evaluated_curve_positions);
BKE_mesh_tag_positions_changed(mesh);
if (face_counter > 0) {
dst_face_offsets[0] = 0;
}
for (const int i : IndexRange(bridges_num)) {
const int next_i = (i + 1) % curve_indices_to_bridge.size();
const int curve_i = curve_indices_to_bridge[i];
const int next_curve_i = curve_indices_to_bridge[next_i];
const bool is_cyclic = cyclic[curve_i];
const bool next_is_cyclic = cyclic[next_curve_i];
const int face_offset = bridge_face_offsets[curve_i];
const int corner_offset = bridge_corner_offsets[curve_i];
const IndexRange points = evaluated_points_by_curve[curve_i];
const IndexRange next_points = evaluated_points_by_curve[next_curve_i];
if (points.is_empty() || next_points.is_empty()) {
continue;
}
const int segments_num = bke::curves::segments_num(points.size(), is_cyclic);
const int next_segments_num = bke::curves::segments_num(next_points.size(), next_is_cyclic);
const int bridge_face_num = std::max(segments_num, next_segments_num);
const int bridge_quad_num = std::min(segments_num, next_segments_num);
const int bridge_tri_num = bridge_face_num - bridge_quad_num;
const int bridge_corner_num = 4 * bridge_quad_num + 3 * bridge_tri_num;
int corner_index = corner_offset;
for (const int face_i : IndexRange(bridge_face_num)) {
const bool from_single_point = face_i >= segments_num;
const bool to_single_point = face_i >= next_segments_num;
if (!from_single_point && !to_single_point) {
dst_corner_verts[corner_index + 0] = points[face_i];
dst_corner_verts[corner_index + 1] = next_points[face_i];
dst_corner_verts[corner_index + 2] = next_points[(face_i + 1) % next_points.size()];
dst_corner_verts[corner_index + 3] = points[(face_i + 1) % points.size()];
corner_index += 4;
}
else if (from_single_point) {
dst_corner_verts[corner_index + 0] =
points[std::min(face_i, segments_num) % points.size()];
dst_corner_verts[corner_index + 1] = next_points[face_i];
dst_corner_verts[corner_index + 2] = next_points[(face_i + 1) % next_points.size()];
corner_index += 3;
}
else {
BLI_assert(to_single_point);
dst_corner_verts[corner_index + 0] = points[face_i];
dst_corner_verts[corner_index + 1] =
next_points[std::min(face_i, next_segments_num) % next_points.size()];
dst_corner_verts[corner_index + 2] = points[(face_i + 1) % points.size()];
corner_index += 3;
}
dst_face_offsets[face_offset + face_i + 1] = corner_index;
}
}
BKE_mesh_calc_edges(mesh, false, false);
mesh_geometry.replace_mesh(mesh);
}
params.set_output("Mesh", mesh_geometry);
}
static void node_register()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_BRIDGE_CURVES, "Bridge Curves", NODE_CLASS_GEOMETRY);
ntype.geometry_node_execute = node_geo_exec;
ntype.declare = node_declare;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_geo_bridge_curves_cc