Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2022-07-22 16:39:41 +0300
committerJacques Lucke <jacques@blender.org>2022-07-22 16:39:41 +0300
commit1f94b56d774440d08eb92f2a7a47b9a6a7aa7b84 (patch)
tree06928c94d524852c7031aa310d578691735dc0a8 /source/blender/blenkernel
parent98bf714b37c1f1b05a162b6ffdaca367b312de1f (diff)
Curves: support sculpting on deformed curves
Previously, curves sculpt tools only worked on original data. This was very limiting, because one could effectively only sculpt the curves when all procedural effects were turned off. This patch adds support for curves sculpting while looking the result of procedural effects (like deformation based on the surface mesh). This functionality is also known as "crazy space" support in Blender. For more details see D15407. Differential Revision: https://developer.blender.org/D15407
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_crazyspace.hh53
-rw-r--r--source/blender/blenkernel/BKE_curves.hh33
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h3
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh51
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh11
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/crazyspace.cc64
-rw-r--r--source/blender/blenkernel/intern/curves.cc24
-rw-r--r--source/blender/blenkernel/intern/geometry_component_edit_data.cc58
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc35
10 files changed, 332 insertions, 2 deletions
diff --git a/source/blender/blenkernel/BKE_crazyspace.hh b/source/blender/blenkernel/BKE_crazyspace.hh
new file mode 100644
index 00000000000..adebf0b7884
--- /dev/null
+++ b/source/blender/blenkernel/BKE_crazyspace.hh
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_span.hh"
+
+struct Depsgraph;
+struct Object;
+
+namespace blender::bke::crazyspace {
+
+/**
+ * Contains information about how points have been deformed during evaluation.
+ * This allows mapping edits on evaluated data back to original data in some cases.
+ */
+struct GeometryDeformation {
+ /**
+ * Positions of the deformed points. This may also point to the original position if no
+ * deformation data is available.
+ */
+ Span<float3> positions;
+ /**
+ * Matrices that transform point translations on original data into corresponding translations in
+ * evaluated data. This may be empty if not available.
+ */
+ Span<float3x3> deform_mats;
+
+ float3 translation_from_deformed_to_original(const int position_i,
+ const float3 &translation) const
+ {
+ if (this->deform_mats.is_empty()) {
+ return translation;
+ }
+ const float3x3 &deform_mat = this->deform_mats[position_i];
+ return deform_mat.inverted() * translation;
+ }
+};
+
+/**
+ * During evaluation of the object, deformation data may have been generated for this object. This
+ * function either retrieves the deformation data from the evaluated object, or falls back to
+ * returning the original data.
+ */
+GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
+ const Object &ob_orig);
+
+} // namespace blender::bke::crazyspace
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 68c90a45031..568899721a9 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -11,6 +11,7 @@
#include <mutex>
+#include "BLI_float3x3.hh"
#include "BLI_float4x4.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_index_mask.hh"
@@ -414,6 +415,38 @@ class CurvesGeometry : public ::CurvesGeometry {
}
};
+/**
+ * Used to propagate deformation data through modifier evaluation so that sculpt tools can work on
+ * evaluated data.
+ */
+class CurvesEditHints {
+ public:
+ /**
+ * Original data that the edit hints below are meant to be used for.
+ */
+ const Curves &curves_id_orig;
+ /**
+ * Evaluated positions for the points in #curves_orig. If this is empty, the positions from the
+ * evaluated #Curves should be used if possible.
+ */
+ std::optional<Array<float3>> positions;
+ /**
+ * Matrices which transform point movement vectors from original data to corresponding movements
+ * of evaluated data.
+ */
+ std::optional<Array<float3x3>> deform_mats;
+
+ CurvesEditHints(const Curves &curves_id_orig) : curves_id_orig(curves_id_orig)
+ {
+ }
+
+ /**
+ * The edit hints have to correspond to the original curves, i.e. the number of deformed points
+ * is the same as the number of original points.
+ */
+ bool is_valid() const;
+};
+
namespace curves {
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index a28e9e6bdf6..97e69f3fe1f 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -23,9 +23,10 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
GEO_COMPONENT_TYPE_CURVE = 4,
+ GEO_COMPONENT_TYPE_EDIT = 5,
} GeometryComponentType;
-#define GEO_COMPONENT_TYPE_ENUM_SIZE 5
+#define GEO_COMPONENT_TYPE_ENUM_SIZE 6
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 4108e2f7e2e..be2ec3e3dca 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -43,7 +43,8 @@ enum class GeometryOwnershipType {
namespace blender::bke {
class ComponentAttributeProviders;
-}
+class CurvesEditHints;
+} // namespace blender::bke
class GeometryComponent;
@@ -168,6 +169,12 @@ struct GeometrySet {
* Remove all geometry components with types that are not in the provided list.
*/
void keep_only(const blender::Span<GeometryComponentType> component_types);
+ /**
+ * Keeps the provided geometry types, but also instances and edit data.
+ * Instances must not be removed while using #modify_geometry_sets.
+ */
+ void keep_only_during_modify(const blender::Span<GeometryComponentType> component_types);
+ void remove_geometry_during_modify();
void add(const GeometryComponent &component);
@@ -287,6 +294,10 @@ struct GeometrySet {
* Returns a read-only curves data-block or null.
*/
const Curves *get_curves_for_read() const;
+ /**
+ * Returns read-only curve edit hints or null.
+ */
+ const blender::bke::CurvesEditHints *get_curve_edit_hints_for_read() const;
/**
* Returns a mutable mesh or null. No ownership is transferred.
@@ -304,6 +315,10 @@ struct GeometrySet {
* Returns a mutable curves data-block or null. No ownership is transferred.
*/
Curves *get_curves_for_write();
+ /**
+ * Returns mutable curve edit hints or null.
+ */
+ blender::bke::CurvesEditHints *get_curve_edit_hints_for_write();
/* Utility methods for replacement. */
/**
@@ -825,3 +840,37 @@ class VolumeComponent : public GeometryComponent {
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};
+
+/**
+ * When the original data is in some edit mode, we want to propagate some additional information
+ * through object evaluation. This information can be used by edit modes to support working on
+ * evaluated data.
+ *
+ * This component is added at the beginning of modifier evaluation.
+ */
+class GeometryComponentEditData final : public GeometryComponent {
+ public:
+ /**
+ * Information about how original curves are manipulated during evaluation. This data is used so
+ * that curve sculpt tools can work on evaluated data. It is not stored in #CurveComponent
+ * because the data remains valid even when there is no actual curves geometry anymore, for
+ * example, when the curves have been converted to a mesh.
+ */
+ std::unique_ptr<blender::bke::CurvesEditHints> curves_edit_hints_;
+
+ GeometryComponentEditData();
+
+ GeometryComponent *copy() const final;
+ bool owns_direct_data() const final;
+ void ensure_owns_direct_data() final;
+
+ /**
+ * The first node that does topology changing operations on curves should store the curve point
+ * positions it retrieved as input. Without this, information about the deformed positions is
+ * lost, which would make curves sculpt mode fall back to using original curve positions instead
+ * of deformed ones.
+ */
+ static void remember_deformed_curve_positions_if_necessary(GeometrySet &geometry);
+
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_EDIT;
+};
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
index 356709d8942..0b7d1a1835f 100644
--- a/source/blender/blenkernel/BKE_mesh_sample.hh
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -137,4 +137,15 @@ float3 compute_bary_coord_in_triangle(const Mesh &mesh,
const MLoopTri &looptri,
const float3 &position);
+template<typename T>
+inline T sample_corner_attrribute_with_bary_coords(const float3 &bary_weights,
+ const MLoopTri &looptri,
+ const Span<T> corner_attribute)
+{
+ return attribute_math::mix3(bary_weights,
+ corner_attribute[looptri.tri[0]],
+ corner_attribute[looptri.tri[1]],
+ corner_attribute[looptri.tri[2]]);
+}
+
} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index df4b70d4fe6..2ac42037198 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -139,6 +139,7 @@ set(SRC
intern/freestyle.c
intern/geometry_component_curve.cc
intern/geometry_component_curves.cc
+ intern/geometry_component_edit_data.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -353,6 +354,7 @@ set(SRC
BKE_constraint.h
BKE_context.h
BKE_crazyspace.h
+ BKE_crazyspace.hh
BKE_cryptomatte.h
BKE_cryptomatte.hh
BKE_curve.h
diff --git a/source/blender/blenkernel/intern/crazyspace.cc b/source/blender/blenkernel/intern/crazyspace.cc
index c3db3095343..978606ef1fa 100644
--- a/source/blender/blenkernel/intern/crazyspace.cc
+++ b/source/blender/blenkernel/intern/crazyspace.cc
@@ -19,7 +19,10 @@
#include "BKE_DerivedMesh.h"
#include "BKE_crazyspace.h"
+#include "BKE_crazyspace.hh"
+#include "BKE_curves.hh"
#include "BKE_editmesh.h"
+#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
@@ -586,3 +589,64 @@ void BKE_crazyspace_api_eval_clear(Object *object)
}
/** \} */
+
+namespace blender::bke::crazyspace {
+
+GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
+ const Object &ob_orig)
+{
+ BLI_assert(ob_orig.type == OB_CURVES);
+ const Curves &curves_id_orig = *static_cast<const Curves *>(ob_orig.data);
+ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(curves_id_orig.geometry);
+ const int points_num = curves_orig.points_num();
+
+ GeometryDeformation deformation;
+ /* Use the undeformed positions by default. */
+ deformation.positions = curves_orig.positions();
+
+ const Object *ob_eval = DEG_get_evaluated_object(&depsgraph, const_cast<Object *>(&ob_orig));
+ if (ob_eval == nullptr) {
+ return deformation;
+ }
+ const GeometrySet *geometry_eval = ob_eval->runtime.geometry_set_eval;
+ if (geometry_eval == nullptr) {
+ return deformation;
+ }
+
+ /* If available, use deformation information generated during evaluation. */
+ const GeometryComponentEditData *edit_component_eval =
+ geometry_eval->get_component_for_read<GeometryComponentEditData>();
+ bool uses_extra_positions = false;
+ if (edit_component_eval != nullptr) {
+ const CurvesEditHints *edit_hints = edit_component_eval->curves_edit_hints_.get();
+ if (edit_hints != nullptr && &edit_hints->curves_id_orig == &curves_id_orig) {
+ if (edit_hints->positions.has_value()) {
+ BLI_assert(edit_hints->positions->size() == points_num);
+ deformation.positions = *edit_hints->positions;
+ uses_extra_positions = true;
+ }
+ if (edit_hints->deform_mats.has_value()) {
+ BLI_assert(edit_hints->deform_mats->size() == points_num);
+ deformation.deform_mats = *edit_hints->deform_mats;
+ }
+ }
+ }
+
+ /* Use the positions of the evaluated curves directly, if the number of points matches. */
+ if (!uses_extra_positions) {
+ const CurveComponent *curves_component_eval =
+ geometry_eval->get_component_for_read<CurveComponent>();
+ if (curves_component_eval != nullptr) {
+ const Curves *curves_id_eval = curves_component_eval->get_for_read();
+ if (curves_id_eval != nullptr) {
+ const CurvesGeometry &curves_eval = CurvesGeometry::wrap(curves_id_eval->geometry);
+ if (curves_eval.points_num() == points_num) {
+ deformation.positions = curves_eval.positions();
+ }
+ }
+ }
+ }
+ return deformation;
+}
+
+} // namespace blender::bke::crazyspace
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index 5684a2e5b07..6554f42d3dd 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -319,6 +319,14 @@ void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
Curves *curves = static_cast<Curves *>(object->data);
GeometrySet geometry_set = GeometrySet::create_with_curves(curves,
GeometryOwnershipType::ReadOnly);
+ if (object->mode == OB_MODE_SCULPT_CURVES) {
+ /* Try to propagate deformation data through modifier evaluation, so that sculpt mode can work
+ * on evaluated curves. */
+ GeometryComponentEditData &edit_component =
+ geometry_set.get_component_for_write<GeometryComponentEditData>();
+ edit_component.curves_edit_hints_ = std::make_unique<blender::bke::CurvesEditHints>(
+ *static_cast<const Curves *>(DEG_get_original_object(object)->data));
+ }
curves_evaluate_modifiers(depsgraph, scene, object, geometry_set);
/* Assign evaluated object. */
@@ -409,4 +417,20 @@ CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const
}
}
+bool CurvesEditHints::is_valid() const
+{
+ const int point_num = this->curves_id_orig.geometry.point_num;
+ if (this->positions.has_value()) {
+ if (this->positions->size() != point_num) {
+ return false;
+ }
+ }
+ if (this->deform_mats.has_value()) {
+ if (this->deform_mats->size() != point_num) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/geometry_component_edit_data.cc b/source/blender/blenkernel/intern/geometry_component_edit_data.cc
new file mode 100644
index 00000000000..2c00de3254f
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_edit_data.cc
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
+
+using namespace blender;
+using namespace blender::bke;
+
+GeometryComponentEditData::GeometryComponentEditData() : GeometryComponent(GEO_COMPONENT_TYPE_EDIT)
+{
+}
+
+GeometryComponent *GeometryComponentEditData::copy() const
+{
+ GeometryComponentEditData *new_component = new GeometryComponentEditData();
+ if (curves_edit_hints_) {
+ new_component->curves_edit_hints_ = std::make_unique<CurvesEditHints>(*curves_edit_hints_);
+ }
+ return new_component;
+}
+
+bool GeometryComponentEditData::owns_direct_data() const
+{
+ return true;
+}
+
+void GeometryComponentEditData::ensure_owns_direct_data()
+{
+ /* Nothing to do. */
+}
+
+void GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(
+ GeometrySet &geometry)
+{
+ /* This component should be created at the start of object evaluation if it's necessary. */
+ if (!geometry.has<GeometryComponentEditData>()) {
+ return;
+ }
+ GeometryComponentEditData &edit_component =
+ geometry.get_component_for_write<GeometryComponentEditData>();
+ if (!edit_component.curves_edit_hints_) {
+ return;
+ }
+ if (edit_component.curves_edit_hints_->positions.has_value()) {
+ return;
+ }
+ const Curves *curves_id = geometry.get_curves_for_read();
+ if (curves_id == nullptr) {
+ return;
+ }
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ const int points_num = curves.points_num();
+ if (points_num != edit_component.curves_edit_hints_->curves_id_orig.geometry.point_num) {
+ return;
+ }
+ edit_component.curves_edit_hints_->positions.emplace(points_num);
+ edit_component.curves_edit_hints_->positions->as_mutable_span().copy_from(curves.positions());
+}
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index c6fe8eebc7f..1a0ce4f0893 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -53,6 +53,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new VolumeComponent();
case GEO_COMPONENT_TYPE_CURVE:
return new CurveComponent();
+ case GEO_COMPONENT_TYPE_EDIT:
+ return new GeometryComponentEditData();
}
BLI_assert_unreachable();
return nullptr;
@@ -175,6 +177,20 @@ void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component
}
}
+void GeometrySet::keep_only_during_modify(
+ const blender::Span<GeometryComponentType> component_types)
+{
+ Vector<GeometryComponentType> extended_types = component_types;
+ extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_INSTANCES);
+ extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_EDIT);
+ this->keep_only(extended_types);
+}
+
+void GeometrySet::remove_geometry_during_modify()
+{
+ this->keep_only_during_modify({});
+}
+
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_[component.type()]);
@@ -290,6 +306,13 @@ const Curves *GeometrySet::get_curves_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+const blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_read() const
+{
+ const GeometryComponentEditData *component =
+ this->get_component_for_read<GeometryComponentEditData>();
+ return (component == nullptr) ? nullptr : component->curves_edit_hints_.get();
+}
+
bool GeometrySet::has_pointcloud() const
{
const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
@@ -453,6 +476,16 @@ Curves *GeometrySet::get_curves_for_write()
return component == nullptr ? nullptr : component->get_for_write();
}
+blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write()
+{
+ if (!this->has<GeometryComponentEditData>()) {
+ return nullptr;
+ }
+ GeometryComponentEditData &component =
+ this->get_component_for_write<GeometryComponentEditData>();
+ return component.curves_edit_hints_.get();
+}
+
void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_types,
const bool include_instances,
const AttributeForeachCallback callback) const
@@ -679,6 +712,8 @@ bool BKE_object_has_geometry_set_instances(const Object *ob)
case GEO_COMPONENT_TYPE_CURVE:
is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT);
break;
+ case GEO_COMPONENT_TYPE_EDIT:
+ break;
}
if (is_instance) {
return true;