GPv3: Texture coordinate shear attribute. #120123

Open
casey-bianco-davis wants to merge 1 commits from casey-bianco-davis/blender:GPv3-texture-shear into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.

View File

@ -464,7 +464,8 @@ static float4x2 get_local_to_stroke_matrix(const Span<float3> positions, const f
*/
static float3x2 get_stroke_to_texture_matrix(const float uv_rotation,
const float2 uv_translation,
const float2 uv_scale)
const float2 uv_scale,
const float uv_shear)
{
using namespace blender::math;
@ -480,8 +481,11 @@ static float3x2 get_stroke_to_texture_matrix(const float uv_rotation,
*
* The translation is applied last so that the origin goes to `uv_translation`
* The rotation is applied after the scale so that the `u` direction's angle is `uv_rotation`
* Scale is the only transform that changes the length of the basis vectors and if it is applied
* first it's independent of the other transforms.
* Scale is the only transform that changes the length of the `u` basis vector and if it is
* applied first it's independent of the other transforms.
* The shear can be applied in any order
* as long as it is after scale so that the shear factor is in units of scale. The shear only
* changes the `v` basis vector.
*
* These properties are not true with a different order.
*/
@ -492,6 +496,9 @@ static float3x2 get_stroke_to_texture_matrix(const float uv_rotation,
/* Apply rotation. */
texture_matrix = rot * texture_matrix;
/* Apply shear. */
texture_matrix[1] += texture_matrix[0] * uv_shear;
/* Apply translation. */
texture_matrix[2] += uv_translation;
@ -530,6 +537,8 @@ Span<float4x2> Drawing::texture_matrices() const
"uv_translation", AttrDomain::Curve, float2(0.0f, 0.0f));
const VArray<float2> uv_scales = *attributes.lookup_or_default<float2>(
"uv_scale", AttrDomain::Curve, float2(1.0f, 1.0f));
const VArray<float> uv_shears = *attributes.lookup_or_default<float>(
"uv_shear", AttrDomain::Curve, 0.0f);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
@ -541,8 +550,10 @@ Span<float4x2> Drawing::texture_matrices() const
const IndexRange points = points_by_curve[curve_i];
const float3 normal = normals[curve_i];
const float4x2 strokemat = get_local_to_stroke_matrix(positions.slice(points), normal);
const float3x2 texture_matrix = get_stroke_to_texture_matrix(
uv_rotations[curve_i], uv_translations[curve_i], uv_scales[curve_i]);
const float3x2 texture_matrix = get_stroke_to_texture_matrix(uv_rotations[curve_i],
uv_translations[curve_i],
uv_scales[curve_i],
uv_shears[curve_i]);
const float4x2 texspace = texture_matrix * expand_4x2_mat(strokemat);
@ -558,6 +569,7 @@ void Drawing::set_texture_matrices(Span<float4x2> matrices, const IndexMask &sel
using namespace blender::math;
CurvesGeometry &curves = this->strokes_for_write();
MutableAttributeAccessor attributes = curves.attributes_for_write();
const bool did_shear_exist = attributes.contains("uv_shear");
SpanAttributeWriter<float> uv_rotations = attributes.lookup_or_add_for_write_span<float>(
"uv_rotation", AttrDomain::Curve);
SpanAttributeWriter<float2> uv_translations = attributes.lookup_or_add_for_write_span<float2>(
@ -566,11 +578,15 @@ void Drawing::set_texture_matrices(Span<float4x2> matrices, const IndexMask &sel
"uv_scale",
AttrDomain::Curve,
AttributeInitVArray(VArray<float2>::ForSingle(float2(1.0f, 1.0f), curves.curves_num())));
SpanAttributeWriter<float> uv_shears = attributes.lookup_or_add_for_write_span<float>(
"uv_shear", AttrDomain::Curve);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const Span<float3> normals = this->curve_plane_normals();
std::atomic<bool> is_shear_used = false;
selection.foreach_index(GrainSize(256), [&](const int64_t curve_i, const int64_t pos) {
const IndexRange points = points_by_curve[curve_i];
const float3 normal = normals[curve_i];
@ -612,18 +628,37 @@ void Drawing::set_texture_matrices(Span<float4x2> matrices, const IndexMask &sel
/* Calculate the determinant to check if the `v` scale is negative. */
const float det = determinant(float2x2(texture_matrix));
/* Solve scale, scaling is the only transformation that changes the length, so scale factor
* is simply the length. And flip the sign of `v` if the determinant is negative. */
/* Solve shear, without shear the `u` and `v` basis vectors are orthogonal, so the shear is
* just their dot product with scaling. */
const float uv_shear = dot(texture_matrix[0], texture_matrix[1]) /
dot(texture_matrix[0], texture_matrix[0]);
/* Calculate the what the `v` basis would be without the shear. */
const float2 V_basis_without_shear = texture_matrix[1] - texture_matrix[0] * uv_shear;
/* Solve scale, scaling is the only transformation that changes the length of `u` basis, so
* scale factor is simply the length. And flip the sign of `v` if the determinant is negative.
*/
const float2 uv_scale = safe_rcp(
float2(length(texture_matrix[0]), sign(det) * length(texture_matrix[1])));
float2(length(texture_matrix[0]), sign(det) * length(V_basis_without_shear)));
uv_rotations.span[curve_i] = uv_rotation;
uv_translations.span[curve_i] = uv_translation;
uv_scales.span[curve_i] = uv_scale;
uv_shears.span[curve_i] = uv_shear;
if (abs(uv_shear) > std::numeric_limits<float>::epsilon() * 10.0f) {
is_shear_used.store(true, std::memory_order_relaxed);
}
});
uv_rotations.finish();
uv_translations.finish();
uv_scales.finish();
uv_shears.finish();
/* Remove the attribute if it is only storing zeros. */
if (!is_shear_used && (!did_shear_exist || selection.size() == curves.curves_num())) {
attributes.remove("uv_shear");
}
this->tag_texture_matrices_changed();
}