Fix: Apply tilt in curves data-block normals calculation
The ported normal calculation from ceed37fc5c
neglected to
use the tilt attribute to rotate the normals around the tangents.
This commit adds that behavior back, adding a new math header file
to avoid duplicating the rotation function for normalized axes.
Differential Revision: https://developer.blender.org/D14655
This commit is contained in:
parent
e96a809a68
commit
47d961a4b1
|
@ -182,6 +182,13 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
/** Mutable access to curve resolution. Call #tag_topology_changed after changes. */
|
||||
MutableSpan<int> resolution_for_write();
|
||||
|
||||
/**
|
||||
* The angle used to rotate evaluated normals around the tangents after their calculation.
|
||||
* Call #tag_normals_changed after changes.
|
||||
*/
|
||||
VArray<float> tilt() const;
|
||||
MutableSpan<float> tilt_for_write();
|
||||
|
||||
/**
|
||||
* Which method to use for calculating the normals of evaluated points (#NormalMode).
|
||||
* Call #tag_normals_changed after changes.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_rotation.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
|
@ -54,20 +54,6 @@ void calculate_tangents(const Span<float3> positions,
|
|||
}
|
||||
}
|
||||
|
||||
static float3 rotate_direction_around_axis(const float3 &direction,
|
||||
const float3 &axis,
|
||||
const float angle)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(direction);
|
||||
BLI_ASSERT_UNIT_V3(axis);
|
||||
|
||||
const float3 axis_scaled = axis * math::dot(direction, axis);
|
||||
const float3 diff = direction - axis_scaled;
|
||||
const float3 cross = math::cross(axis, diff);
|
||||
|
||||
return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
|
||||
}
|
||||
|
||||
void calculate_normals_z_up(const Span<float3> tangents, MutableSpan<float3> normals)
|
||||
{
|
||||
BLI_assert(normals.size() == tangents.size());
|
||||
|
@ -98,7 +84,7 @@ static float3 calculate_next_normal(const float3 &last_normal,
|
|||
const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
|
||||
if (angle != 0.0) {
|
||||
const float3 axis = math::normalize(math::cross(last_tangent, current_tangent));
|
||||
return rotate_direction_around_axis(last_normal, axis, angle);
|
||||
return math::rotate_direction_around_axis(last_normal, axis, angle);
|
||||
}
|
||||
return last_normal;
|
||||
}
|
||||
|
@ -147,7 +133,7 @@ void calculate_normals_minimum(const Span<float3> tangents,
|
|||
const float angle_step = correction_angle / normals.size();
|
||||
for (const int i : normals.index_range()) {
|
||||
const float angle = angle_step * i;
|
||||
normals[i] = rotate_direction_around_axis(normals[i], tangents[i], angle);
|
||||
normals[i] = math::rotate_direction_around_axis(normals[i], tangents[i], angle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "BLI_bounds.hh"
|
||||
#include "BLI_index_mask_ops.hh"
|
||||
#include "BLI_length_parameterize.hh"
|
||||
#include "BLI_math_rotation.hh"
|
||||
|
||||
#include "DNA_curves_types.h"
|
||||
|
||||
|
@ -22,6 +23,7 @@ namespace blender::bke {
|
|||
|
||||
static const std::string ATTR_POSITION = "position";
|
||||
static const std::string ATTR_RADIUS = "radius";
|
||||
static const std::string ATTR_TILT = "tilt";
|
||||
static const std::string ATTR_CURVE_TYPE = "curve_type";
|
||||
static const std::string ATTR_CYCLIC = "cyclic";
|
||||
static const std::string ATTR_RESOLUTION = "resolution";
|
||||
|
@ -330,6 +332,15 @@ MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
|
|||
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE);
|
||||
}
|
||||
|
||||
VArray<float> CurvesGeometry::tilt() const
|
||||
{
|
||||
return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT, 0.0f);
|
||||
}
|
||||
MutableSpan<float> CurvesGeometry::tilt_for_write()
|
||||
{
|
||||
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT);
|
||||
}
|
||||
|
||||
VArray<int8_t> CurvesGeometry::handle_types_left() const
|
||||
{
|
||||
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0);
|
||||
|
@ -717,6 +728,15 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
|
|||
return this->runtime->evaluated_tangent_cache;
|
||||
}
|
||||
|
||||
static void rotate_directions_around_axes(MutableSpan<float3> directions,
|
||||
const Span<float3> axes,
|
||||
const Span<float> angles)
|
||||
{
|
||||
for (const int i : directions.index_range()) {
|
||||
directions[i] = math::rotate_direction_around_axis(directions[i], axes[i], angles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Span<float3> CurvesGeometry::evaluated_normals() const
|
||||
{
|
||||
if (!this->runtime->normal_cache_dirty) {
|
||||
|
@ -733,11 +753,16 @@ Span<float3> CurvesGeometry::evaluated_normals() const
|
|||
const Span<float3> evaluated_tangents = this->evaluated_tangents();
|
||||
const VArray<bool> cyclic = this->cyclic();
|
||||
const VArray<int8_t> normal_mode = this->normal_mode();
|
||||
const VArray<int8_t> types = this->curve_types();
|
||||
const VArray<float> tilt = this->tilt();
|
||||
|
||||
this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num());
|
||||
MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache;
|
||||
|
||||
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
|
||||
/* Reuse a buffer for the evaluated tilts. */
|
||||
Vector<float> evaluated_tilts;
|
||||
|
||||
for (const int curve_index : curves_range) {
|
||||
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
||||
if (UNLIKELY(evaluated_points.is_empty())) {
|
||||
|
@ -754,6 +779,27 @@ Span<float3> CurvesGeometry::evaluated_normals() const
|
|||
evaluated_normals.slice(evaluated_points));
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the "tilt" attribute exists, rotate the normals around the tangents by the
|
||||
* evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
|
||||
if (!(tilt.is_single() && tilt.get_internal_single() == 0.0f)) {
|
||||
const IndexRange points = this->points_for_curve(curve_index);
|
||||
Span<float> curve_tilt = tilt.get_internal_span().slice(points);
|
||||
if (types[curve_index] == CURVE_TYPE_POLY) {
|
||||
rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
|
||||
evaluated_tangents.slice(evaluated_points),
|
||||
curve_tilt);
|
||||
}
|
||||
else {
|
||||
evaluated_tilts.clear();
|
||||
evaluated_tilts.resize(evaluated_points.size());
|
||||
this->interpolate_to_evaluated(
|
||||
curve_index, curve_tilt, evaluated_tilts.as_mutable_span());
|
||||
rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
|
||||
evaluated_tangents.slice(evaluated_points),
|
||||
evaluated_tilts.as_span());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
namespace blender::math {
|
||||
|
||||
/**
|
||||
* Rotate the unit-length \a direction around the unit-length \a axis by the \a angle.
|
||||
*/
|
||||
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle);
|
||||
|
||||
} // namespace blender::math
|
|
@ -94,6 +94,7 @@ set(SRC
|
|||
intern/math_interp.c
|
||||
intern/math_matrix.c
|
||||
intern/math_rotation.c
|
||||
intern/math_rotation.cc
|
||||
intern/math_solvers.c
|
||||
intern/math_statistics.c
|
||||
intern/math_time.c
|
||||
|
@ -251,6 +252,7 @@ set(SRC
|
|||
BLI_math_matrix.h
|
||||
BLI_math_mpq.hh
|
||||
BLI_math_rotation.h
|
||||
BLI_math_rotation.hh
|
||||
BLI_math_solvers.h
|
||||
BLI_math_statistics.h
|
||||
BLI_math_time.h
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_rotation.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
namespace blender::math {
|
||||
|
||||
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, const float angle)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(direction);
|
||||
BLI_ASSERT_UNIT_V3(axis);
|
||||
|
||||
const float3 axis_scaled = axis * math::dot(direction, axis);
|
||||
const float3 diff = direction - axis_scaled;
|
||||
const float3 cross = math::cross(axis, diff);
|
||||
|
||||
return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
|
||||
}
|
||||
|
||||
} // namespace blender::math
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_rotation.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
@ -147,3 +149,23 @@ TEST(math_rotation, quat_split_swing_and_twist_negative)
|
|||
EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON);
|
||||
EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON);
|
||||
}
|
||||
|
||||
namespace blender::math::tests {
|
||||
|
||||
TEST(math_rotation, RotateDirectionAroundAxis)
|
||||
{
|
||||
const float3 a = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI_2);
|
||||
EXPECT_NEAR(a.x, 0.0f, FLT_EPSILON);
|
||||
EXPECT_NEAR(a.y, 1.0f, FLT_EPSILON);
|
||||
EXPECT_NEAR(a.z, 0.0f, FLT_EPSILON);
|
||||
const float3 b = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI);
|
||||
EXPECT_NEAR(b.x, -1.0f, FLT_EPSILON);
|
||||
EXPECT_NEAR(b.y, 0.0f, FLT_EPSILON);
|
||||
EXPECT_NEAR(b.z, 0.0f, FLT_EPSILON);
|
||||
const float3 c = rotate_direction_around_axis({0, 0, 1}, {0, 0, 1}, 0.0f);
|
||||
EXPECT_NEAR(c.x, 0.0f, FLT_EPSILON);
|
||||
EXPECT_NEAR(c.y, 0.0f, FLT_EPSILON);
|
||||
EXPECT_NEAR(c.z, 1.0f, FLT_EPSILON);
|
||||
}
|
||||
|
||||
} // namespace blender::math::tests
|
||||
|
|
Loading…
Reference in New Issue