Fix #130945: Grease Pencil: Crazyspace support in sculpt mode
Grease Pencil v3 did not have crazyspace support yet. Without this sculpting on deformed geometry (e.g. on top of an armature modifier) will yield incorrect offsets because the tool writes to original data based on deformed positions. This patch adds computation of local deformation matrices which are stored in the `GeometryComponentEditData`. Those matrices are then used to convert local deformation of the evaluated geometry back to a deformation of the original geometry. All the relevant sculpt tools support the crazyspace correction now, using the `compute_orig_delta` helper function. Computing the deformation matrices should happen alongside modifier evaluation for any deforming modifier. This has been implemented for the armature modifier, others can be added. A fallback implementation for curves could also be added for modifiers that don't have an easy way to calculate local transformation. A "natural" orientation for both the original and deformed positions is calculated, then the difference yields deform matrices. For meshes the approach is to use the surface normal and a stable tangent space. For curves the common local coordinate frame based on parallel transport might be used. Currently crazyspace correction for the _Clone_ tool does not work because of #131496. Pull Request: https://projects.blender.org/blender/blender/pulls/131499
This commit is contained in:
@@ -644,7 +644,7 @@ void BKE_armature_deform_coords_with_curves(
|
||||
const Object &ob_target,
|
||||
const ListBase *defbase,
|
||||
blender::MutableSpan<blender::float3> vert_coords,
|
||||
std::optional<blender::MutableSpan<blender::float3>> vert_coords_prev,
|
||||
std::optional<blender::Span<blender::float3>> vert_coords_prev,
|
||||
std::optional<blender::MutableSpan<blender::float3x3>> vert_deform_mats,
|
||||
blender::Span<MDeformVert> dverts,
|
||||
int deformflag,
|
||||
|
||||
@@ -362,6 +362,10 @@ struct GeometrySet {
|
||||
* Returns read-only curve edit hints or null.
|
||||
*/
|
||||
const CurvesEditHints *get_curve_edit_hints() const;
|
||||
/**
|
||||
* Returns read-only Grease Pencil edit hints or null.
|
||||
*/
|
||||
const GreasePencilEditHints *get_grease_pencil_edit_hints() const;
|
||||
/**
|
||||
* Returns read-only gizmo edit hints or null.
|
||||
*/
|
||||
@@ -395,6 +399,10 @@ struct GeometrySet {
|
||||
* Returns mutable curve edit hints or null.
|
||||
*/
|
||||
CurvesEditHints *get_curve_edit_hints_for_write();
|
||||
/**
|
||||
* Returns mutable Grease Pencil edit hints or null.
|
||||
*/
|
||||
GreasePencilEditHints *get_grease_pencil_edit_hints_for_write();
|
||||
/**
|
||||
* Returns mutable gizmo edit hints or null.
|
||||
*/
|
||||
|
||||
@@ -929,6 +929,12 @@ class GreasePencilDrawingEditHints {
|
||||
const greasepencil::Drawing *drawing_orig;
|
||||
ImplicitSharingPtrAndData positions_data;
|
||||
|
||||
/**
|
||||
* Matrices which transform point movement vectors from original data to corresponding movements
|
||||
* of evaluated data.
|
||||
*/
|
||||
std::optional<Array<float3x3>> deform_mats;
|
||||
|
||||
std::optional<Span<float3>> positions() const;
|
||||
std::optional<MutableSpan<float3>> positions_for_write();
|
||||
};
|
||||
|
||||
@@ -637,7 +637,7 @@ void BKE_armature_deform_coords_with_curves(
|
||||
const Object &ob_target,
|
||||
const ListBase *defbase,
|
||||
blender::MutableSpan<blender::float3> vert_coords,
|
||||
std::optional<blender::MutableSpan<blender::float3>> vert_coords_prev,
|
||||
std::optional<blender::Span<blender::float3>> vert_coords_prev,
|
||||
std::optional<blender::MutableSpan<blender::float3x3>> vert_deform_mats,
|
||||
blender::Span<MDeformVert> dverts,
|
||||
int deformflag,
|
||||
@@ -647,6 +647,9 @@ void BKE_armature_deform_coords_with_curves(
|
||||
* used for Grease Pencil layers as well. */
|
||||
BLI_assert(dverts.size() == vert_coords.size());
|
||||
|
||||
/* const_cast for old positions for the C API, these are not actually written. */
|
||||
blender::float3 *vert_coords_prev_data = const_cast<blender::float3 *>(vert_coords_prev->data());
|
||||
|
||||
armature_deform_coords_impl(
|
||||
&ob_arm,
|
||||
&ob_target,
|
||||
@@ -655,7 +658,7 @@ void BKE_armature_deform_coords_with_curves(
|
||||
vert_deform_mats ? reinterpret_cast<float(*)[3][3]>(vert_deform_mats->data()) : nullptr,
|
||||
vert_coords.size(),
|
||||
deformflag,
|
||||
vert_coords_prev ? reinterpret_cast<float(*)[3]>(vert_coords_prev->data()) : nullptr,
|
||||
vert_coords_prev ? reinterpret_cast<float(*)[3]>(vert_coords_prev_data) : nullptr,
|
||||
defgrp_name.c_str(),
|
||||
dverts,
|
||||
nullptr,
|
||||
|
||||
@@ -652,6 +652,8 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
|
||||
return deformation;
|
||||
}
|
||||
|
||||
bool has_deformed_positions = false;
|
||||
|
||||
/* If there are edit hints, use the positions of those. */
|
||||
if (geometry_eval->has<GeometryComponentEditData>()) {
|
||||
const GeometryComponentEditData &edit_component_eval =
|
||||
@@ -663,12 +665,18 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
|
||||
BLI_assert(edit_hints->drawing_hints->size() == layers_orig.size());
|
||||
const GreasePencilDrawingEditHints &drawing_hints =
|
||||
edit_hints->drawing_hints.value()[layer_index];
|
||||
if (const std::optional<Span<float3>> positions = drawing_hints.positions()) {
|
||||
deformation.positions = *positions;
|
||||
return deformation;
|
||||
if (drawing_hints.positions()) {
|
||||
deformation.positions = *drawing_hints.positions();
|
||||
has_deformed_positions = true;
|
||||
}
|
||||
if (drawing_hints.deform_mats.has_value()) {
|
||||
deformation.deform_mats = *drawing_hints.deform_mats;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_deformed_positions) {
|
||||
return deformation;
|
||||
}
|
||||
|
||||
/* Otherwise use the positions of the evaluated drawing if the number of points match. */
|
||||
if (const GreasePencilComponent *grease_pencil_component_eval =
|
||||
@@ -682,7 +690,7 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
|
||||
layer_eval, frame))
|
||||
if (drawing_eval->strokes().points_num() == drawing_orig->strokes().points_num()) {
|
||||
deformation.positions = drawing_eval->strokes().positions();
|
||||
return deformation;
|
||||
has_deformed_positions = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,12 @@ const CurvesEditHints *GeometrySet::get_curve_edit_hints() const
|
||||
return (component == nullptr) ? nullptr : component->curves_edit_hints_.get();
|
||||
}
|
||||
|
||||
const GreasePencilEditHints *GeometrySet::get_grease_pencil_edit_hints() const
|
||||
{
|
||||
const GeometryComponentEditData *component = this->get_component<GeometryComponentEditData>();
|
||||
return (component == nullptr) ? nullptr : component->grease_pencil_edit_hints_.get();
|
||||
}
|
||||
|
||||
const GizmoEditHints *GeometrySet::get_gizmo_edit_hints() const
|
||||
{
|
||||
const GeometryComponentEditData *component = this->get_component<GeometryComponentEditData>();
|
||||
@@ -576,6 +582,16 @@ CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write()
|
||||
return component.curves_edit_hints_.get();
|
||||
}
|
||||
|
||||
GreasePencilEditHints *GeometrySet::get_grease_pencil_edit_hints_for_write()
|
||||
{
|
||||
if (!this->has<GeometryComponentEditData>()) {
|
||||
return nullptr;
|
||||
}
|
||||
GeometryComponentEditData &component =
|
||||
this->get_component_for_write<GeometryComponentEditData>();
|
||||
return component.grease_pencil_edit_hints_.get();
|
||||
}
|
||||
|
||||
GizmoEditHints *GeometrySet::get_gizmo_edit_hints_for_write()
|
||||
{
|
||||
if (!this->has<GeometryComponentEditData>()) {
|
||||
|
||||
@@ -2192,9 +2192,9 @@ void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
|
||||
if (layer_attributes.contains("tint_color") || layer_attributes.contains("radius_offset")) {
|
||||
grease_pencil_do_layer_adjustments(*geometry_set.get_grease_pencil_for_write());
|
||||
}
|
||||
/* Only add the edit hint component in edit mode for now so users can properly select deformed
|
||||
* drawings. */
|
||||
if (object->mode == OB_MODE_EDIT) {
|
||||
/* Only add the edit hint component in edit mode or sculpt mode for now so users can properly
|
||||
* select deformed drawings. */
|
||||
if (ELEM(object->mode, OB_MODE_EDIT, OB_MODE_SCULPT_GREASE_PENCIL)) {
|
||||
GeometryComponentEditData &edit_component =
|
||||
geometry_set.get_component_for_write<GeometryComponentEditData>();
|
||||
edit_component.grease_pencil_edit_hints_ = std::make_unique<GreasePencilEditHints>(
|
||||
|
||||
@@ -150,6 +150,17 @@ DeltaProjectionFunc get_screen_projection_fn(const GreasePencilStrokeParams &par
|
||||
const Object &object,
|
||||
const bke::greasepencil::Layer &layer);
|
||||
|
||||
/**
|
||||
* Compute position offset for a point in the original geometry
|
||||
* from a screen offset and crazyspace deformation info.
|
||||
* \param projection_fn Projection from screen space to the evaluated object.
|
||||
* \param deformation Converts evaluated position delta to original geometry.
|
||||
*/
|
||||
float3 compute_orig_delta(const DeltaProjectionFunc &projection_fn,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
int index,
|
||||
const float2 &screen_delta);
|
||||
|
||||
bool do_vertex_color_points(const Brush &brush);
|
||||
bool do_vertex_color_fill(const Brush &brush);
|
||||
|
||||
|
||||
@@ -272,6 +272,19 @@ DeltaProjectionFunc get_screen_projection_fn(const GreasePencilStrokeParams &par
|
||||
return [](const float3 &, const float2 &) { return float3(); };
|
||||
}
|
||||
|
||||
float3 compute_orig_delta(const DeltaProjectionFunc &projection_fn,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const int index,
|
||||
const float2 &screen_delta)
|
||||
{
|
||||
const float3 old_position_eval = deformation.positions[index];
|
||||
const float3 new_position_eval = projection_fn(old_position_eval, screen_delta);
|
||||
const float3 translation_eval = new_position_eval - old_position_eval;
|
||||
const float3 translation_orig = deformation.translation_from_deformed_to_original(
|
||||
index, translation_eval);
|
||||
return translation_orig;
|
||||
}
|
||||
|
||||
GreasePencilStrokeParams GreasePencilStrokeParams::from_context(
|
||||
const Scene &scene,
|
||||
Depsgraph &depsgraph,
|
||||
|
||||
@@ -81,7 +81,8 @@ void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
threading::parallel_for(pasted_points, 4096, [&](const IndexRange range) {
|
||||
for (const int point_i : range) {
|
||||
positions[point_i] = projection_fn(deformation.positions[point_i], mouse_delta);
|
||||
positions[point_i] = compute_orig_delta(
|
||||
projection_fn, deformation, point_i, mouse_delta);
|
||||
}
|
||||
});
|
||||
params.drawing.tag_positions_changed();
|
||||
|
||||
@@ -204,8 +204,8 @@ void GrabOperation::on_stroke_extended(const bContext &C, const InputSample &ext
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
mask.foreach_index(GrainSize(4096), [&](const int point_i, const int index) {
|
||||
/* Translate the point with the influence factor. */
|
||||
positions[point_i] = projection_fn(deformation.positions[point_i],
|
||||
mouse_delta_win * weights[index]);
|
||||
positions[point_i] += compute_orig_delta(
|
||||
projection_fn, deformation, point_i, mouse_delta_win * weights[index]);
|
||||
});
|
||||
|
||||
params.drawing.tag_positions_changed();
|
||||
|
||||
@@ -70,8 +70,8 @@ void PinchOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
const float influence_squared = influence * influence / 25.0f;
|
||||
const float influence_final = invert ? 1.0 + influence_squared :
|
||||
1.0f - influence_squared;
|
||||
positions[point_i] = projection_fn(deformation.positions[point_i],
|
||||
(target - co) * (1.0f - influence_final));
|
||||
positions[point_i] += compute_orig_delta(
|
||||
projection_fn, deformation, point_i, (target - co) * (1.0f - influence_final));
|
||||
});
|
||||
|
||||
params.drawing.tag_positions_changed();
|
||||
|
||||
@@ -67,8 +67,8 @@ void PushOperation::on_stroke_extended(const bContext &C, const InputSample &ext
|
||||
return;
|
||||
}
|
||||
|
||||
positions[point_i] = projection_fn(deformation.positions[point_i],
|
||||
mouse_delta * influence);
|
||||
positions[point_i] += compute_orig_delta(
|
||||
projection_fn, deformation, point_i, mouse_delta * influence);
|
||||
});
|
||||
|
||||
params.drawing.tag_positions_changed();
|
||||
|
||||
@@ -96,8 +96,8 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
return;
|
||||
}
|
||||
const float noise = 2.0f * hash_rng(seed, 5678, point_i) - 1.0f;
|
||||
positions[point_i] = projection_fn(deformation.positions[point_i],
|
||||
sideways * influence * noise);
|
||||
positions[point_i] += compute_orig_delta(
|
||||
projection_fn, deformation, point_i, sideways * influence * noise);
|
||||
});
|
||||
|
||||
params.drawing.tag_positions_changed();
|
||||
|
||||
@@ -79,9 +79,11 @@ void TwistOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
|
||||
const float angle = DEG2RADF(invert ? -1.0f : 1.0f) * influence;
|
||||
const float2 radial_offset = co - mouse_pos;
|
||||
positions[point_i] = projection_fn(deformation.positions[point_i],
|
||||
rotate_by_angle(radial_offset, angle) -
|
||||
radial_offset);
|
||||
positions[point_i] += compute_orig_delta(projection_fn,
|
||||
deformation,
|
||||
point_i,
|
||||
rotate_by_angle(radial_offset, angle) -
|
||||
radial_offset);
|
||||
});
|
||||
|
||||
params.drawing.tag_positions_changed();
|
||||
|
||||
@@ -194,6 +194,33 @@ static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const f
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_grease_pencil_edit_hints(bke::GreasePencilEditHints &edit_hints,
|
||||
const float4x4 &transform)
|
||||
{
|
||||
if (!edit_hints.drawing_hints) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (bke::GreasePencilDrawingEditHints &drawing_hints : *edit_hints.drawing_hints) {
|
||||
if (const std::optional<MutableSpan<float3>> positions = drawing_hints.positions_for_write()) {
|
||||
transform_positions(*positions, transform);
|
||||
}
|
||||
float3x3 deform_mat = transform.view<3, 3>();
|
||||
if (drawing_hints.deform_mats.has_value()) {
|
||||
MutableSpan<float3x3> deform_mats = *drawing_hints.deform_mats;
|
||||
threading::parallel_for(deform_mats.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
deform_mats[i] = deform_mat * deform_mats[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
drawing_hints.deform_mats.emplace(drawing_hints.drawing_orig->strokes().points_num(),
|
||||
deform_mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_gizmo_edit_hints(bke::GizmoEditHints &edit_hints, const float4x4 &transform)
|
||||
{
|
||||
for (float4x4 &m : edit_hints.gizmo_transforms.values()) {
|
||||
@@ -268,6 +295,11 @@ std::optional<TransformGeometryErrors> transform_geometry(bke::GeometrySet &geom
|
||||
if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
|
||||
transform_curve_edit_hints(*curve_edit_hints, transform);
|
||||
}
|
||||
if (bke::GreasePencilEditHints *grease_pencil_edit_hints =
|
||||
geometry.get_grease_pencil_edit_hints_for_write())
|
||||
{
|
||||
transform_grease_pencil_edit_hints(*grease_pencil_edit_hints, transform);
|
||||
}
|
||||
if (bke::GizmoEditHints *gizmo_edit_hints = geometry.get_gizmo_edit_hints_for_write()) {
|
||||
transform_gizmo_edit_hints(*gizmo_edit_hints, transform);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
@@ -104,7 +105,22 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont
|
||||
DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier");
|
||||
}
|
||||
|
||||
static void modify_curves(ModifierData &md, const ModifierEvalContext &ctx, Drawing &drawing)
|
||||
static ImplicitSharingPtrAndData save_shared_attribute(const bke::GAttributeReader &attribute)
|
||||
{
|
||||
if (attribute.sharing_info && attribute.varray.is_span()) {
|
||||
const void *data = attribute.varray.get_internal_span().data();
|
||||
attribute.sharing_info->add_user();
|
||||
return {ImplicitSharingPtr(attribute.sharing_info), data};
|
||||
}
|
||||
auto *data = new ImplicitSharedValue<GArray<>>(attribute.varray.type(), attribute.varray.size());
|
||||
attribute.varray.materialize(data->data.data());
|
||||
return {ImplicitSharingPtr<>(data), data->data.data()};
|
||||
}
|
||||
|
||||
static void modify_curves(ModifierData &md,
|
||||
const ModifierEvalContext &ctx,
|
||||
Drawing &drawing,
|
||||
bke::GreasePencilDrawingEditHints *edit_hints)
|
||||
{
|
||||
auto &amd = reinterpret_cast<GreasePencilArmatureModifierData &>(md);
|
||||
modifier::greasepencil::ensure_no_bezier_curves(drawing);
|
||||
@@ -130,15 +146,35 @@ static void modify_curves(ModifierData &md, const ModifierEvalContext &ctx, Draw
|
||||
return;
|
||||
}
|
||||
|
||||
ImplicitSharingPtrAndData old_positions_data = save_shared_attribute(
|
||||
curves.attributes().lookup("position", CD_PROP_FLOAT3));
|
||||
Span<float3> old_positions = {static_cast<const float3 *>(old_positions_data.data),
|
||||
curves.points_num()};
|
||||
|
||||
std::optional<MutableSpan<float3x3>> deform_mats;
|
||||
if (edit_hints) {
|
||||
if (!edit_hints->deform_mats.has_value()) {
|
||||
edit_hints->deform_mats.emplace(drawing.strokes().points_num(), float3x3::identity());
|
||||
}
|
||||
deform_mats = edit_hints->deform_mats->as_mutable_span();
|
||||
}
|
||||
|
||||
curves_mask.foreach_index(blender::GrainSize(128), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
|
||||
std::optional<Span<float3>> old_positions_for_curve;
|
||||
std::optional<MutableSpan<float3x3>> deform_mats_for_curve;
|
||||
if (deform_mats) {
|
||||
old_positions_for_curve = old_positions.slice(points);
|
||||
deform_mats_for_curve = deform_mats->slice(points);
|
||||
}
|
||||
|
||||
BKE_armature_deform_coords_with_curves(*amd.object,
|
||||
*ctx.object,
|
||||
&curves.vertex_group_names,
|
||||
positions.slice(points),
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
old_positions_for_curve,
|
||||
deform_mats_for_curve,
|
||||
dverts.slice(points),
|
||||
deformflag,
|
||||
amd.influence.vertex_group_name);
|
||||
@@ -157,15 +193,37 @@ static void modify_geometry_set(ModifierData *md,
|
||||
return;
|
||||
}
|
||||
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
|
||||
const GreasePencil &grease_pencil_orig = *reinterpret_cast<GreasePencil *>(
|
||||
DEG_get_original_id(&grease_pencil.id));
|
||||
const int frame = grease_pencil.runtime->eval_frame;
|
||||
|
||||
MutableSpan<bke::GreasePencilDrawingEditHints> edit_hints = {};
|
||||
if (geometry_set->has_component<bke::GeometryComponentEditData>()) {
|
||||
bke::GeometryComponentEditData &edit_component =
|
||||
geometry_set->get_component_for_write<bke::GeometryComponentEditData>();
|
||||
if (edit_component.grease_pencil_edit_hints_) {
|
||||
if (!edit_component.grease_pencil_edit_hints_->drawing_hints) {
|
||||
edit_component.grease_pencil_edit_hints_->drawing_hints.emplace(
|
||||
grease_pencil_orig.layers().size());
|
||||
}
|
||||
edit_hints = *edit_component.grease_pencil_edit_hints_->drawing_hints;
|
||||
}
|
||||
}
|
||||
|
||||
IndexMaskMemory mask_memory;
|
||||
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
|
||||
grease_pencil, amd->influence, mask_memory);
|
||||
const Vector<Drawing *> drawings = modifier::greasepencil::get_drawings_for_write(
|
||||
grease_pencil, layer_mask, frame);
|
||||
threading::parallel_for_each(drawings,
|
||||
[&](Drawing *drawing) { modify_curves(*md, *ctx, *drawing); });
|
||||
threading::parallel_for_each(drawings.index_range(), [&](const int index) {
|
||||
Drawing *drawing = drawings[index];
|
||||
if (edit_hints.is_empty()) {
|
||||
modify_curves(*md, *ctx, *drawing, nullptr);
|
||||
}
|
||||
else {
|
||||
modify_curves(*md, *ctx, *drawing, &edit_hints[index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void panel_draw(const bContext *C, Panel *panel)
|
||||
|
||||
Reference in New Issue
Block a user