WIP: Geometry Nodes: Curve to mesh "Angle Scale" option #120757
|
@ -28,6 +28,7 @@ class AnonymousAttributePropagationInfo;
|
|||
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
const CurvesGeometry &profile,
|
||||
bool fill_caps,
|
||||
bool angle_scale,
|
||||
const AnonymousAttributePropagationInfo &propagation_info);
|
||||
/**
|
||||
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_rotation.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
|
@ -173,37 +177,74 @@ static void mark_bezier_vector_edges_sharp(const int profile_point_num,
|
|||
}
|
||||
}
|
||||
|
||||
static void fill_mesh_positions(const int main_point_num,
|
||||
const int profile_point_num,
|
||||
const Span<float3> main_positions,
|
||||
static float4x4 calc_profile_matrix(const Span<float3> positions,
|
||||
const Span<float3> tangents,
|
||||
const Span<float3> normals,
|
||||
const Span<float> radii,
|
||||
const bool angle_scale,
|
||||
const int i)
|
||||
{
|
||||
float3x3 matrix = math::from_orthonormal_axes<float3x3>(normals[i], tangents[i]);
|
||||
|
||||
if (angle_scale && !ELEM(i, 0, positions.index_range().last())) {
|
||||
const float3 dir_a = math::normalize(positions[i] - positions[i - 1]);
|
||||
std::cout << "dir_a: " << dir_a << '\n';
|
||||
const float3 dir_b = math::normalize(positions[i] - positions[i + 1]);
|
||||
std::cout << "dir_b: " << dir_b << '\n';
|
||||
// const float factor = shell_v3v3_normalized_to_dist(dir_a, dir_b);
|
||||
// const float angle = angle_v3v3v3(positions[i - 1], positions[i], positions[i + 1]);
|
||||
// std::cout << "angle: " << angle << '\n';
|
||||
// const float factor = shell_angle_to_dist(angle);
|
||||
const float dot = math::dot(dir_a, dir_b);
|
||||
std::cout << "dot: " << dot << '\n';
|
||||
const float factor = 1.0f / math::sqrt((1.0f - dot) * 0.5f);
|
||||
std::cout << "factor: " << factor << '\n';
|
||||
// if (factor != 1.0f) {
|
||||
const float3 tri_normal = math::normal_tri(positions[i - 1], positions[i], positions[i + 1]);
|
||||
const float3 normal = math::is_zero(tri_normal) ? normals[i] : tri_normal;
|
||||
std::cout << "tri_normal: " << tri_normal << '\n';
|
||||
const float3x3 base = math::from_orthonormal_axes<float3x3>(tangents[i], normal);
|
||||
std::cout << "base: " << base << '\n';
|
||||
const float3x3 scale = math::scale(base, float3(1.0f, factor, 1.0f));
|
||||
std::cout << "scale: " << scale << '\n';
|
||||
matrix = scale * matrix;
|
||||
// }
|
||||
}
|
||||
|
||||
const float radius = radii.is_empty() ? 1.0f : radii[i];
|
||||
if (radius != 1.0f) {
|
||||
matrix = math::scale(matrix, float3(radius));
|
||||
}
|
||||
|
||||
float4x4 final(matrix);
|
||||
final.location() = positions[i];
|
||||
return final;
|
||||
}
|
||||
|
||||
static void fill_mesh_positions(const Span<float3> main_positions,
|
||||
const Span<float3> profile_positions,
|
||||
const Span<float3> tangents,
|
||||
const Span<float3> normals,
|
||||
const Span<float> radii,
|
||||
const bool angle_scale,
|
||||
MutableSpan<float3> mesh_positions)
|
||||
{
|
||||
if (profile_point_num == 1) {
|
||||
for (const int i_ring : IndexRange(main_point_num)) {
|
||||
float4x4 point_matrix = math::from_orthonormal_axes<float4x4>(
|
||||
main_positions[i_ring], normals[i_ring], tangents[i_ring]);
|
||||
if (!radii.is_empty()) {
|
||||
point_matrix = math::scale(point_matrix, float3(radii[i_ring]));
|
||||
}
|
||||
mesh_positions[i_ring] = math::transform_point(point_matrix, profile_positions.first());
|
||||
if (profile_positions.size() == 1) {
|
||||
for (const int i_ring : main_positions.index_range()) {
|
||||
const float4x4 matrix = calc_profile_matrix(
|
||||
main_positions, tangents, normals, radii, angle_scale, i_ring);
|
||||
mesh_positions[i_ring] = math::transform_point(matrix, profile_positions.first());
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i_ring : IndexRange(main_point_num)) {
|
||||
float4x4 point_matrix = math::from_orthonormal_axes<float4x4>(
|
||||
main_positions[i_ring], normals[i_ring], tangents[i_ring]);
|
||||
if (!radii.is_empty()) {
|
||||
point_matrix = math::scale(point_matrix, float3(radii[i_ring]));
|
||||
}
|
||||
for (const int i_ring : main_positions.index_range()) {
|
||||
const float4x4 matrix = calc_profile_matrix(
|
||||
main_positions, tangents, normals, radii, angle_scale, i_ring);
|
||||
|
||||
const int ring_vert_start = i_ring * profile_point_num;
|
||||
for (const int i_profile : IndexRange(profile_point_num)) {
|
||||
const int ring_vert_start = i_ring * profile_positions.size();
|
||||
for (const int i_profile : profile_positions.index_range()) {
|
||||
mesh_positions[ring_vert_start + i_profile] = math::transform_point(
|
||||
point_matrix, profile_positions[i_profile]);
|
||||
matrix, profile_positions[i_profile]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,6 +503,7 @@ static void foreach_curve_combination(const CurvesInfo &info,
|
|||
|
||||
static void build_mesh_positions(const CurvesInfo &curves_info,
|
||||
const ResultOffsets &offsets,
|
||||
const bool angle_scale,
|
||||
Vector<std::byte> &eval_buffer,
|
||||
Mesh &mesh)
|
||||
{
|
||||
|
@ -494,13 +536,12 @@ static void build_mesh_positions(const CurvesInfo &curves_info,
|
|||
radii_eval = evaluate_attribute(radii, curves_info.main, eval_buffer).typed<float>();
|
||||
}
|
||||
foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
|
||||
fill_mesh_positions(info.main_points.size(),
|
||||
info.profile_points.size(),
|
||||
main_positions.slice(info.main_points),
|
||||
fill_mesh_positions(main_positions.slice(info.main_points),
|
||||
profile_positions.slice(info.profile_points),
|
||||
tangents.slice(info.main_points),
|
||||
normals.slice(info.main_points),
|
||||
radii_eval.is_empty() ? radii_eval : radii_eval.slice(info.main_points),
|
||||
angle_scale,
|
||||
positions.slice(info.vert_range));
|
||||
});
|
||||
}
|
||||
|
@ -802,6 +843,7 @@ static void write_sharp_bezier_edges(const CurvesInfo &curves_info,
|
|||
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
const CurvesGeometry &profile,
|
||||
const bool fill_caps,
|
||||
const bool angle_scale,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const CurvesInfo curves_info = get_curves_info(main, profile);
|
||||
|
@ -861,7 +903,7 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
|||
/* Make sure curve attributes can be interpolated. */
|
||||
main.ensure_can_interpolate_to_evaluated();
|
||||
|
||||
build_mesh_positions(curves_info, offsets, eval_buffer, *mesh);
|
||||
build_mesh_positions(curves_info, offsets, angle_scale, eval_buffer, *mesh);
|
||||
|
||||
mesh->tag_overlapping_none();
|
||||
if (!offsets.any_single_point_main) {
|
||||
|
@ -984,7 +1026,7 @@ Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
|
|||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
static const CurvesGeometry vert_curve = get_curve_single_vert();
|
||||
return curve_to_mesh_sweep(curve, vert_curve, false, propagation_info);
|
||||
return curve_to_mesh_sweep(curve, vert_curve, false, false, propagation_info);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -26,19 +26,23 @@ static void node_declare(NodeDeclarationBuilder &b)
|
|||
b.add_input<decl::Bool>("Fill Caps")
|
||||
.description(
|
||||
"If the profile spline is cyclic, fill the ends of the generated mesh with N-gons");
|
||||
b.add_input<decl::Bool>("Angle Scale")
|
||||
.description(
|
||||
"Scale each profile based on the of the neighboring edges to give an even thickness");
|
||||
b.add_output<decl::Geometry>("Mesh").propagate_all();
|
||||
}
|
||||
|
||||
static Mesh *curve_to_mesh(const bke::CurvesGeometry &curves,
|
||||
const GeometrySet &profile_set,
|
||||
const bool fill_caps,
|
||||
const bool angle_scale,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
Mesh *mesh;
|
||||
if (profile_set.has_curves()) {
|
||||
const Curves *profile_curves = profile_set.get_curves();
|
||||
mesh = bke::curve_to_mesh_sweep(
|
||||
curves, profile_curves->geometry.wrap(), fill_caps, propagation_info);
|
||||
curves, profile_curves->geometry.wrap(), fill_caps, angle_scale, propagation_info);
|
||||
}
|
||||
else {
|
||||
mesh = bke::curve_to_wire_mesh(curves, propagation_info);
|
||||
|
@ -50,6 +54,7 @@ static Mesh *curve_to_mesh(const bke::CurvesGeometry &curves,
|
|||
static void grease_pencil_to_mesh(GeometrySet &geometry_set,
|
||||
const GeometrySet &profile_set,
|
||||
const bool fill_caps,
|
||||
const bool angle_scale,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
@ -63,7 +68,8 @@ static void grease_pencil_to_mesh(GeometrySet &geometry_set,
|
|||
continue;
|
||||
}
|
||||
const bke::CurvesGeometry &curves = drawing->strokes();
|
||||
mesh_by_layer[layer_index] = curve_to_mesh(curves, profile_set, fill_caps, propagation_info);
|
||||
mesh_by_layer[layer_index] = curve_to_mesh(
|
||||
curves, profile_set, fill_caps, angle_scale, propagation_info);
|
||||
}
|
||||
|
||||
if (mesh_by_layer.is_empty()) {
|
||||
|
@ -102,6 +108,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
|
||||
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
|
||||
const bool fill_caps = params.extract_input<bool>("Fill Caps");
|
||||
const bool angle_scale = params.extract_input<bool>("Angle Scale");
|
||||
|
||||
bke::GeometryComponentEditData::remember_deformed_positions_if_necessary(curve_set);
|
||||
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
|
||||
|
@ -110,11 +117,12 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
|
||||
if (geometry_set.has_curves()) {
|
||||
const Curves &curves = *geometry_set.get_curves();
|
||||
Mesh *mesh = curve_to_mesh(curves.geometry.wrap(), profile_set, fill_caps, propagation_info);
|
||||
Mesh *mesh = curve_to_mesh(
|
||||
curves.geometry.wrap(), profile_set, fill_caps, angle_scale, propagation_info);
|
||||
geometry_set.replace_mesh(mesh);
|
||||
}
|
||||
if (geometry_set.has_grease_pencil()) {
|
||||
grease_pencil_to_mesh(geometry_set, profile_set, fill_caps, propagation_info);
|
||||
grease_pencil_to_mesh(geometry_set, profile_set, fill_caps, angle_scale, propagation_info);
|
||||
}
|
||||
geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue