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
path: root/source
diff options
context:
space:
mode:
authorPeter Kim <pk15950@gmail.com>2022-03-26 03:14:01 +0300
committerPeter Kim <pk15950@gmail.com>2022-03-26 03:14:01 +0300
commit07b0b6e9b78c9fc99f563244aec202acda536f2d (patch)
tree39d5548756e35d87899fe8dfff1a8efe3222ee74 /source
parentd834dcf1da3c2fce89459dbf22da77435feb48aa (diff)
parent1909fd2781c48a2c769d88f0c4ec36177ce523c4 (diff)
Merge branch 'master' into xr-dev
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_curves.hh18
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h58
-rw-r--r--source/blender/blenkernel/intern/collection.c6
-rw-r--r--source/blender/blenkernel/intern/curve_bezier.cc44
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc66
-rw-r--r--source/blender/blenkernel/intern/curves_geometry_test.cc95
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc467
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc12
-rw-r--r--source/blender/blenkernel/intern/object.cc4
-rw-r--r--source/blender/blenlib/BLI_math_base.hh22
-rw-r--r--source/blender/blenlib/BLI_math_vec_types.hh11
-rw-r--r--source/blender/blenlib/BLI_math_vector.hh6
-rw-r--r--source/blender/blenlib/tests/BLI_math_base_test.cc5
-rw-r--r--source/blender/blenlib/tests/BLI_math_vector_test.cc20
-rw-r--r--source/blender/blenloader/intern/versioning_300.c18
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c31
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c11
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c30
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c8
-rw-r--r--source/blender/editors/io/io_usd.c4
-rw-r--r--source/blender/editors/object/object_transform.cc4
-rw-r--r--source/blender/editors/space_image/image_ops.c9
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_libraries.cc2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencildash.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c10
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c77
-rw-r--r--source/blender/io/usd/CMakeLists.txt8
-rw-r--r--source/blender/io/usd/intern/usd_reader_light.cc21
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.cc17
-rw-r--r--source/blender/io/usd/intern/usd_reader_xform.cc2
-rw-r--r--source/blender/io/usd/intern/usd_writer_light.cc48
-rw-r--r--source/blender/io/usd/intern/usd_writer_material.cc12
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc2
-rw-r--r--source/blender/makesdna/DNA_curves_types.h1
-rw-r--r--source/blender/makesdna/DNA_defs.h97
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h11
-rw-r--r--source/blender/makesdna/DNA_object_types.h2
-rw-r--r--source/blender/makesdna/intern/dna_utils.c18
-rw-r--r--source/blender/makesdna/intern/makesdna.c12
-rw-r--r--source/blender/makesrna/intern/rna_brush.c5
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c20
44 files changed, 965 insertions, 383 deletions
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 0f2ae8a02a6..82f77d83bec 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -146,6 +146,8 @@ class CurvesGeometry : public ::CurvesGeometry {
MutableSpan<int8_t> curve_types();
bool has_curve_with_type(const CurveType type) const;
+ /** Return the number of curves with each type. */
+ std::array<int, CURVE_TYPES_NUM> count_curve_types() const;
MutableSpan<float3> positions();
Span<float3> positions() const;
@@ -264,6 +266,15 @@ class CurvesGeometry : public ::CurvesGeometry {
Span<float3> evaluated_positions() const;
+ /**
+ * Evaluate a generic data to the standard evaluated points of a specific curve,
+ * defined by the resolution attribute or other factors, depending on the curve type.
+ *
+ * \warning This function expects offsets to the evaluated points for each curve to be
+ * calculated. That can be ensured with #ensure_evaluated_offsets.
+ */
+ void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const;
+
private:
/**
* Make sure the basis weights for NURBS curve's evaluated points are calculated.
@@ -379,6 +390,13 @@ void calculate_evaluated_positions(Span<float3> positions,
Span<int> evaluated_offsets,
MutableSpan<float3> evaluated_positions);
+/**
+ * Evaluate generic data to the evaluated points, with counts for each segment described by
+ * #evaluated_offsets. Unlike other curve types, for Bezier curves generic data and positions
+ * are treated separately, since attribute values aren't stored for the handle control points.
+ */
+void interpolate_to_evaluated(GSpan src, Span<int> evaluated_offsets, GMutableSpan dst);
+
} // namespace bezier
namespace catmull_rom {
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index 4127030e96f..ad3b1971ca9 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -220,30 +220,78 @@ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd,
* \param gps: Stroke to smooth
* \param i: Point index
* \param inf: Amount of smoothing to apply
+ * \param iterations: Radius of points to consider, equivalent to iterations
* \param smooth_caps: Apply smooth to stroke extremes
+ * \param keep_shape: Smooth out fine details first
+ * \param r_gps: Stroke to put the result into
*/
-bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int i, float inf, bool smooth_caps);
+bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps,
+ int point_index,
+ float influence,
+ int iterations,
+ bool smooth_caps,
+ bool keep_shape,
+ struct bGPDstroke *r_gps);
/**
* Apply smooth strength to stroke point.
* \param gps: Stroke to smooth
* \param point_index: Point index
* \param influence: Amount of smoothing to apply
+ * \param iterations: Radius of points to consider, equivalent to iterations
+ * \param r_gps: Stroke to put the result into
*/
-bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence);
+bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps,
+ int point_index,
+ float influence,
+ int iterations,
+ struct bGPDstroke *r_gps);
/**
* Apply smooth for thickness to stroke point (use pressure).
* \param gps: Stroke to smooth
* \param point_index: Point index
* \param influence: Amount of smoothing to apply
+ * \param iterations: Radius of points to consider, equivalent to iterations
+ * \param r_gps: Stroke to put the result into
*/
-bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence);
+bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps,
+ int point_index,
+ float influence,
+ int iterations,
+ struct bGPDstroke *r_gps);
/**
- * Apply smooth for UV rotation to stroke point (use pressure).
+ * Apply smooth for UV rotation/factor to stroke point.
* \param gps: Stroke to smooth
* \param point_index: Point index
* \param influence: Amount of smoothing to apply
+ * \param iterations: Radius of points to consider, equivalent to iterations
+ * \param r_gps: Stroke to put the result into
*/
-bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence);
+bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps,
+ int point_index,
+ float influence,
+ int iterations,
+ struct bGPDstroke *r_gps);
+/**
+ * Apply smooth operation to the stroke.
+ * \param gps: Stroke to smooth
+ * \param influence: The interpolation factor for the smooth and the original stroke
+ * \param iterations: Radius of points to consider, equivalent to iterations
+ * \param smooth_position: Smooth point locations
+ * \param smooth_strength: Smooth point strength
+ * \param smooth_thickness: Smooth point thickness
+ * \param smooth_uv: Smooth uv rotation/factor
+ * \param keep_shape: Use different distribution for smooth locations to keep the shape
+ * \param weights: per point weights to multiply influence with (optional, can be null)
+ */
+void BKE_gpencil_stroke_smooth(struct bGPDstroke *gps,
+ const float influence,
+ const int iterations,
+ const bool smooth_position,
+ const bool smooth_strength,
+ const bool smooth_thickness,
+ const bool smooth_uv,
+ const bool keep_shape,
+ const float *weights);
/**
* Close grease pencil stroke.
* \param gps: Stroke to close
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 891145b47c9..bdaea487cfb 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -432,7 +432,8 @@ void BKE_collection_add_from_object(Main *bmain,
bool is_instantiated = false;
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
- if (!ID_IS_LINKED(collection) && BKE_collection_has_object(collection, ob_src)) {
+ if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDABLE_LIBRARY(collection) &&
+ BKE_collection_has_object(collection, ob_src)) {
collection_child_add(collection, collection_dst, 0, true);
is_instantiated = true;
}
@@ -454,7 +455,8 @@ void BKE_collection_add_from_collection(Main *bmain,
bool is_instantiated = false;
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
- if (!ID_IS_LINKED(collection) && collection_find_child(collection, collection_src)) {
+ if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDABLE_LIBRARY(collection) &&
+ collection_find_child(collection, collection_src)) {
collection_child_add(collection, collection_dst, 0, true);
is_instantiated = true;
}
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc
index c02555dcf6a..b11216983b2 100644
--- a/source/blender/blenkernel/intern/curve_bezier.cc
+++ b/source/blender/blenkernel/intern/curve_bezier.cc
@@ -134,6 +134,50 @@ void calculate_evaluated_positions(const Span<float3> positions,
}
}
+template<typename T>
+static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> dst)
+{
+ dst.first() = a;
+ const float step = 1.0f / dst.size();
+ for (const int i : dst.index_range().drop_front(1)) {
+ dst[i] = attribute_math::mix2(i * step, a, b);
+ }
+}
+
+template<typename T>
+static void interpolate_to_evaluated(const Span<T> src,
+ const Span<int> evaluated_offsets,
+ MutableSpan<T> dst)
+{
+ BLI_assert(!src.is_empty());
+ BLI_assert(dst.size() == src.size());
+ BLI_assert(evaluated_offsets.last() == dst.size());
+
+ linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first()));
+
+ threading::parallel_for(
+ src.index_range().drop_back(1).drop_front(1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const IndexRange segment_points = offsets_to_range(evaluated_offsets, i - 1);
+ linear_interpolation(src[i], src[i + 1], dst.slice(segment_points));
+ }
+ });
+
+ const IndexRange last_segment_points(evaluated_offsets.last(1),
+ evaluated_offsets.last() - evaluated_offsets.last(1));
+ linear_interpolation(src.last(), src.first(), dst.slice(last_segment_points));
+}
+
+void interpolate_to_evaluated(const GSpan src, const Span<int> evaluated_offsets, GMutableSpan dst)
+{
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ interpolate_to_evaluated(src.typed<T>(), evaluated_offsets, dst.typed<T>());
+ }
+ });
+}
+
/** \} */
} // namespace blender::bke::curves::bezier
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 5bb6a97fa49..7ceaa8f0f37 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -277,6 +277,40 @@ bool CurvesGeometry::has_curve_with_type(const CurveType type) const
return false;
}
+std::array<int, CURVE_TYPES_NUM> CurvesGeometry::count_curve_types() const
+{
+ using CountsType = std::array<int, CURVE_TYPES_NUM>;
+
+ CountsType identity;
+ identity.fill(0);
+
+ const VArray<int8_t> types = this->curve_types();
+ if (types.is_single()) {
+ identity[types.get_internal_single()] = this->curves_num();
+ return identity;
+ }
+
+ Span<int8_t> types_span = types.get_internal_span();
+ return threading::parallel_reduce(
+ this->curves_range(),
+ 2048,
+ identity,
+ [&](const IndexRange curves_range, const CountsType &init) {
+ CountsType result = init;
+ for (const int curve_index : curves_range) {
+ result[types_span[curve_index]]++;
+ }
+ return result;
+ },
+ [](const CountsType &a, const CountsType &b) {
+ CountsType result = a;
+ for (const int i : IndexRange(CURVE_TYPES_NUM)) {
+ result[i] += b[i];
+ }
+ return result;
+ });
+}
+
MutableSpan<float3> CurvesGeometry::positions()
{
this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named(
@@ -655,6 +689,38 @@ Span<float3> CurvesGeometry::evaluated_positions() const
return this->runtime->evaluated_position_cache;
}
+void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
+ const GSpan src,
+ GMutableSpan dst) const
+{
+ BLI_assert(!this->runtime->offsets_cache_dirty);
+ BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
+ const IndexRange points = this->points_for_curve(curve_index);
+ BLI_assert(src.size() == points.size());
+ BLI_assert(dst.size() == this->evaluated_points_for_curve(curve_index).size());
+ switch (this->curve_types()[curve_index]) {
+ case CURVE_TYPE_CATMULL_ROM:
+ curves::catmull_rom::interpolate_to_evaluated(
+ src, this->cyclic()[curve_index], this->resolution()[curve_index], dst);
+ return;
+ case CURVE_TYPE_POLY:
+ dst.type().copy_assign_n(src.data(), dst.data(), src.size());
+ return;
+ case CURVE_TYPE_BEZIER:
+ curves::bezier::interpolate_to_evaluated(
+ src, this->runtime->bezier_evaluated_offsets.as_span().slice(points), dst);
+ return;
+ case CURVE_TYPE_NURBS:
+ curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
+ this->nurbs_orders()[curve_index],
+ this->nurbs_weights().slice(points),
+ src,
+ dst);
+ return;
+ }
+ BLI_assert_unreachable();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/curves_geometry_test.cc b/source/blender/blenkernel/intern/curves_geometry_test.cc
index bc99785de1c..e4dc9eead60 100644
--- a/source/blender/blenkernel/intern/curves_geometry_test.cc
+++ b/source/blender/blenkernel/intern/curves_geometry_test.cc
@@ -63,6 +63,28 @@ TEST(curves_geometry, Move)
EXPECT_EQ(second_other.offsets().data(), offsets_data);
}
+TEST(curves_geometry, TypeCount)
+{
+ CurvesGeometry curves = create_basic_curves(100, 10);
+ curves.curve_types().copy_from({
+ CURVE_TYPE_BEZIER,
+ CURVE_TYPE_NURBS,
+ CURVE_TYPE_NURBS,
+ CURVE_TYPE_NURBS,
+ CURVE_TYPE_CATMULL_ROM,
+ CURVE_TYPE_CATMULL_ROM,
+ CURVE_TYPE_CATMULL_ROM,
+ CURVE_TYPE_POLY,
+ CURVE_TYPE_POLY,
+ CURVE_TYPE_POLY,
+ });
+ std::array<int, CURVE_TYPES_NUM> counts = curves.count_curve_types();
+ EXPECT_EQ(counts[CURVE_TYPE_CATMULL_ROM], 3);
+ EXPECT_EQ(counts[CURVE_TYPE_POLY], 3);
+ EXPECT_EQ(counts[CURVE_TYPE_BEZIER], 1);
+ EXPECT_EQ(counts[CURVE_TYPE_NURBS], 3);
+}
+
TEST(curves_geometry, CatmullRomEvaluation)
{
CurvesGeometry curves(4, 1);
@@ -383,4 +405,77 @@ TEST(curves_geometry, NURBSEvaluation)
}
}
+TEST(curves_geometry, BezierGenericEvaluation)
+{
+ CurvesGeometry curves(3, 1);
+ curves.curve_types().fill(CURVE_TYPE_BEZIER);
+ curves.resolution().fill(8);
+ curves.offsets().last() = 3;
+
+ MutableSpan<float3> handles_left = curves.handle_positions_left();
+ MutableSpan<float3> handles_right = curves.handle_positions_right();
+ MutableSpan<float3> positions = curves.positions();
+ positions.first() = {-1, 0, 0};
+ handles_right.first() = {-1, 1, 0};
+ handles_left[1] = {0, 0, 0};
+ positions[1] = {1, 0, 0};
+ handles_right[1] = {2, 0, 0};
+ handles_left.last() = {1, 1, 0};
+ positions.last() = {2, 1, 0};
+
+ /* Dangling handles shouldn't be used in a non-cyclic curve. */
+ handles_left.first() = {100, 100, 100};
+ handles_right.last() = {100, 100, 100};
+
+ Span<float3> evaluated_positions = curves.evaluated_positions();
+ static const Array<float3> result_1{{
+ {-1.0f, 0.0f, 0.0f},
+ {-0.955078f, 0.287109f, 0.0f},
+ {-0.828125f, 0.421875f, 0.0f},
+ {-0.630859f, 0.439453f, 0.0f},
+ {-0.375f, 0.375f, 0.0f},
+ {-0.0722656f, 0.263672f, 0.0f},
+ {0.265625f, 0.140625f, 0.0f},
+ {0.626953f, 0.0410156f, 0.0f},
+ {1.0f, 0.0f, 0.0f},
+ {1.28906f, 0.0429688f, 0.0f},
+ {1.4375f, 0.15625f, 0.0f},
+ {1.49219f, 0.316406f, 0.0f},
+ {1.5f, 0.5f, 0.0f},
+ {1.50781f, 0.683594f, 0.0f},
+ {1.5625f, 0.84375f, 0.0f},
+ {1.71094f, 0.957031f, 0.0f},
+ {2.0f, 1.0f, 0.0f},
+ }};
+ for (const int i : evaluated_positions.index_range()) {
+ EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
+ }
+
+ Array<float> radii{{0.0f, 1.0f, 2.0f}};
+ Array<float> evaluated_radii(17);
+ curves.interpolate_to_evaluated(0, radii.as_span(), evaluated_radii.as_mutable_span());
+ static const Array<float> result_2{{
+ 0.0f,
+ 0.125f,
+ 0.25f,
+ 0.375f,
+ 0.5f,
+ 0.625f,
+ 0.75f,
+ 0.875f,
+ 1.0f,
+ 1.125f,
+ 1.25f,
+ 1.375f,
+ 1.5f,
+ 1.625f,
+ 1.75f,
+ 1.875f,
+ 2.0f,
+ }};
+ for (const int i : evaluated_radii.index_range()) {
+ EXPECT_NEAR(evaluated_radii[i], result_2[i], 1e-6f);
+ }
+}
+
} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index a5eff1f9d5a..a0b6ab2d654 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -980,74 +980,116 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo
/** \name Stroke Smooth Positions
* \{ */
-bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int i, float inf, const bool smooth_caps)
+bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
+ int i,
+ float influence,
+ int iterations,
+ const bool smooth_caps,
+ const bool keep_shape,
+ bGPDstroke *r_gps)
{
- bGPDspoint *pt = &gps->points[i];
- float sco[3] = {0.0f};
- const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
-
- /* Do nothing if not enough points to smooth out */
- if (gps->totpoints <= 2) {
+ /* If nothing to do, return early */
+ if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
- /* Only affect endpoints by a fraction of the normal strength,
- * to prevent the stroke from shrinking too much
+ /* Overview of the algorithm here and in the following smooth functions:
+ * The smooth functions return the new attribute in question for a single point.
+ * The result is stored in r_gps->points[i], while the data is read from gps.
+ * To get a correct result, duplicate the stroke point data and read from the copy,
+ * while writing to the real stroke. Not doing that will result in acceptable, but
+ * asymmetric results.
+ * This algorithm works as long as all points are being smoothed. If there is
+ * points that should not get smoothed, use the old repeat smooth pattern with
+ * the parameter "iterations" set to 1 or 2. (2 matches the old algorithm).
*/
- if ((!smooth_caps) && (!is_cyclic && ELEM(i, 0, gps->totpoints - 1))) {
- inf *= 0.1f;
- }
-
- /* Compute smoothed coordinate by taking the ones nearby */
- /* XXX: This is potentially slow,
- * and suffers from accumulation error as earlier points are handled before later ones. */
- {
- /* XXX: this is hardcoded to look at 2 points on either side of the current one
- * (i.e. 5 items total). */
- const int steps = 2;
- const float average_fac = 1.0f / (float)(steps * 2 + 1);
- int step;
-
- /* add the point itself */
- madd_v3_v3fl(sco, &pt->x, average_fac);
-
- /* n-steps before/after current point */
- /* XXX: review how the endpoints are treated by this algorithm. */
- /* XXX: falloff measures should also introduce some weighting variations,
- * so that further-out points get less weight. */
- for (step = 1; step <= steps; step++) {
- bGPDspoint *pt1, *pt2;
- int before = i - step;
- int after = i + step;
-
- if (is_cyclic) {
- if (before < 0) {
- /* Sub to end point (before is already negative). */
- before = gps->totpoints + before;
- CLAMP(before, 0, gps->totpoints - 1);
- }
- if (after > gps->totpoints - 1) {
- /* Add to start point. */
- after = after - gps->totpoints;
- CLAMP(after, 0, gps->totpoints - 1);
+
+ const bGPDspoint *pt = &gps->points[i];
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
+ /* If smooth_caps is false, the caps will not be translated by smoothing. */
+ if (!smooth_caps && !is_cyclic && ELEM(i, 0, gps->totpoints - 1)) {
+ copy_v3_v3(&r_gps->points[i].x, &pt->x);
+ return true;
+ }
+
+ /* This function uses a binomial kernel, which is the discrete version of gaussian blur.
+ * The weight for a vertex at the relative index i is
+ * w = nCr(n, j + n/2) / 2^n = (n/1 * (n-1)/2 * ... * (n-j-n/2)/(j+n/2)) / 2^n
+ * All weights together sum up to 1
+ * This is equivalent to doing multiple iterations of averaging neighbors,
+ * where n = iterations * 2 and -n/2 <= j <= n/2
+ *
+ * Now the problem is that nCr(n, j + n/2) is very hard to compute for n > 500, since even
+ * double precision isn't sufficient. A very good robust approximation for n > 20 is
+ * nCr(n, j + n/2) / 2^n = sqrt(2/(pi*n)) * exp(-2*j*j/n)
+ *
+ * There is one more problem left: The old smooth algorithm was doing a more aggressive
+ * smooth. To solve that problem, choose a different n/2, which does not match the range and
+ * normalize the weights on finish. This may cause some artifacts at low values.
+ *
+ * keep_shape is a new option to stop the stroke from severly deforming.
+ * It uses different partially negative weights.
+ * w = 2 * (nCr(n, j + n/2) / 2^n) - (nCr(3*n, j + n) / 2^(3*n))
+ * ~ 2 * sqrt(2/(pi*n)) * exp(-2*j*j/n) - sqrt(2/(pi*3*n)) * exp(-2*j*j/(3*n))
+ * All weigths still sum up to 1.
+ * Note these weights only work because the averaging is done in relative coordinates.
+ */
+ float sco[3] = {0.0f, 0.0f, 0.0f};
+ float tmp[3];
+ const int n_half = keep_shape ? (iterations * iterations) / 8 + iterations :
+ (iterations * iterations) / 4 + 2 * iterations + 12;
+ double w = keep_shape ? 2.0 : 1.0;
+ double w2 = keep_shape ?
+ (1.0 / M_SQRT3) * exp((2 * iterations * iterations) / (double)(n_half * 3)) :
+ 0.0;
+ double total_w = 0.0;
+ for (int step = iterations; step > 0; step--) {
+ int before = i - step;
+ int after = i + step;
+ float w_before = (float)(w - w2);
+ float w_after = (float)(w - w2);
+
+ if (is_cyclic) {
+ before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
+ after = after % gps->totpoints;
+ }
+ else {
+ if (before < 0) {
+ if (!smooth_caps) {
+ w_before *= -before / (float)i;
}
+ before = 0;
}
- else {
- CLAMP_MIN(before, 0);
- CLAMP_MAX(after, gps->totpoints - 1);
+ if (after > gps->totpoints - 1) {
+ if (!smooth_caps) {
+ w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i);
+ }
+ after = gps->totpoints - 1;
}
+ }
- pt1 = &gps->points[before];
- pt2 = &gps->points[after];
+ /* Add both these points in relative coordinates to the weighted average sum. */
+ sub_v3_v3v3(tmp, &gps->points[before].x, &pt->x);
+ madd_v3_v3fl(sco, tmp, w_before);
+ sub_v3_v3v3(tmp, &gps->points[after].x, &pt->x);
+ madd_v3_v3fl(sco, tmp, w_after);
- /* add both these points to the average-sum (s += p[i]/n) */
- madd_v3_v3fl(sco, &pt1->x, average_fac);
- madd_v3_v3fl(sco, &pt2->x, average_fac);
- }
+ total_w += w_before;
+ total_w += w_after;
+
+ w *= (n_half + step) / (double)(n_half + 1 - step);
+ w2 *= (n_half * 3 + step) / (double)(n_half * 3 + 1 - step);
}
+ total_w += w - w2;
+ /* The accumulated weight total_w should be
+ * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
+ * here, but sometimes not quite. */
+ mul_v3_fl(sco, (float)(1.0 / total_w));
+ /* Shift back to global coordinates. */
+ add_v3_v3(sco, &pt->x);
- /* Based on influence factor, blend between original and optimal smoothed coordinate */
- interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
+ /* Based on influence factor, blend between original and optimal smoothed coordinate. */
+ interp_v3_v3v3(&r_gps->points[i].x, &pt->x, sco, influence);
return true;
}
@@ -1058,74 +1100,54 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int i, float inf, const bo
/** \name Stroke Smooth Strength
* \{ */
-bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
+bool BKE_gpencil_stroke_smooth_strength(
+ bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps)
{
- bGPDspoint *ptb = &gps->points[point_index];
- const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
-
- /* Do nothing if not enough points */
- if ((gps->totpoints <= 2) || (point_index < 1)) {
+ /* If nothing to do, return early */
+ if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
- /* Only affect endpoints by a fraction of the normal influence */
- float inf = influence;
- if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
- inf *= 0.01f;
- }
- /* Limit max influence to reduce pop effect. */
- CLAMP_MAX(inf, 0.98f);
-
- float total = 0.0f;
- float max_strength = 0.0f;
- const int steps = 4;
- const float average_fac = 1.0f / (float)(steps * 2 + 1);
- int step;
- /* add the point itself */
- total += ptb->strength * average_fac;
- max_strength = ptb->strength;
+ /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
- /* n-steps before/after current point */
- for (step = 1; step <= steps; step++) {
- bGPDspoint *pt1, *pt2;
- int before = point_index - step;
- int after = point_index + step;
+ const bGPDspoint *pt = &gps->points[i];
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
+ float strength = 0.0f;
+ const int n_half = (iterations * iterations) / 4 + iterations;
+ double w = 1.0;
+ double total_w = 0.0;
+ for (int step = iterations; step > 0; step--) {
+ int before = i - step;
+ int after = i + step;
+ float w_before = (float)w;
+ float w_after = (float)w;
if (is_cyclic) {
- if (before < 0) {
- /* Sub to end point (before is already negative). */
- before = gps->totpoints + before;
- CLAMP(before, 0, gps->totpoints - 1);
- }
- if (after > gps->totpoints - 1) {
- /* Add to start point. */
- after = after - gps->totpoints;
- CLAMP(after, 0, gps->totpoints - 1);
- }
+ before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
+ after = after % gps->totpoints;
}
else {
CLAMP_MIN(before, 0);
CLAMP_MAX(after, gps->totpoints - 1);
}
- pt1 = &gps->points[before];
- pt2 = &gps->points[after];
- /* add both these points to the average-sum (s += p[i]/n) */
- total += pt1->strength * average_fac;
- total += pt2->strength * average_fac;
- /* Save max value. */
- if (max_strength < pt1->strength) {
- max_strength = pt1->strength;
- }
- if (max_strength < pt2->strength) {
- max_strength = pt2->strength;
- }
+ /* Add both these points in relative coordinates to the weighted average sum. */
+ strength += w_before * (gps->points[before].strength - pt->strength);
+ strength += w_after * (gps->points[after].strength - pt->strength);
+
+ total_w += w_before;
+ total_w += w_after;
+
+ w *= (n_half + step) / (double)(n_half + 1 - step);
}
+ total_w += w;
+ /* The accumulated weight total_w should be
+ * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
+ * here, but sometimes not quite. */
+ strength /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
- ptb->strength = interpf(ptb->strength, total, inf);
- /* Clamp to maximum stroke strength to avoid weird results. */
- CLAMP_MAX(ptb->strength, max_strength);
+ r_gps->points[i].strength = pt->strength + strength * influence;
return true;
}
@@ -1136,74 +1158,55 @@ bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float
/** \name Stroke Smooth Thickness
* \{ */
-bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
+bool BKE_gpencil_stroke_smooth_thickness(
+ bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps)
{
- bGPDspoint *ptb = &gps->points[point_index];
- const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
-
- /* Do nothing if not enough points */
- if ((gps->totpoints <= 2) || (point_index < 1)) {
+ /* If nothing to do, return early */
+ if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
- /* Only affect endpoints by a fraction of the normal influence */
- float inf = influence;
- if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
- inf *= 0.01f;
- }
- /* Limit max influence to reduce pop effect. */
- CLAMP_MAX(inf, 0.98f);
-
- float total = 0.0f;
- float max_pressure = 0.0f;
- const int steps = 4;
- const float average_fac = 1.0f / (float)(steps * 2 + 1);
- int step;
- /* add the point itself */
- total += ptb->pressure * average_fac;
- max_pressure = ptb->pressure;
+ /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
- /* n-steps before/after current point */
- for (step = 1; step <= steps; step++) {
- bGPDspoint *pt1, *pt2;
- int before = point_index - step;
- int after = point_index + step;
+ const bGPDspoint *pt = &gps->points[i];
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
+ float pressure = 0.0f;
+ const int n_half = (iterations * iterations) / 4 + iterations;
+ double w = 1.0;
+ double total_w = 0.0;
+ for (int step = iterations; step > 0; step--) {
+ int before = i - step;
+ int after = i + step;
+ float w_before = (float)w;
+ float w_after = (float)w;
if (is_cyclic) {
- if (before < 0) {
- /* Sub to end point (before is already negative). */
- before = gps->totpoints + before;
- CLAMP(before, 0, gps->totpoints - 1);
- }
- if (after > gps->totpoints - 1) {
- /* Add to start point. */
- after = after - gps->totpoints;
- CLAMP(after, 0, gps->totpoints - 1);
- }
+ before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
+ after = after % gps->totpoints;
}
else {
CLAMP_MIN(before, 0);
CLAMP_MAX(after, gps->totpoints - 1);
}
- pt1 = &gps->points[before];
- pt2 = &gps->points[after];
- /* add both these points to the average-sum (s += p[i]/n) */
- total += pt1->pressure * average_fac;
- total += pt2->pressure * average_fac;
- /* Save max value. */
- if (max_pressure < pt1->pressure) {
- max_pressure = pt1->pressure;
- }
- if (max_pressure < pt2->pressure) {
- max_pressure = pt2->pressure;
- }
+ /* Add both these points in relative coordinates to the weighted average sum. */
+ pressure += w_before * (gps->points[before].pressure - pt->pressure);
+ pressure += w_after * (gps->points[after].pressure - pt->pressure);
+
+ total_w += w_before;
+ total_w += w_after;
+
+ w *= (n_half + step) / (double)(n_half + 1 - step);
}
+ total_w += w;
+ /* The accumulated weight total_w should be
+ * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
+ * here, but sometimes not quite. */
+ pressure /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
- ptb->pressure = interpf(ptb->pressure, total, inf);
- /* Clamp to maximum stroke thickness to avoid weird results. */
- CLAMP_MAX(ptb->pressure, max_pressure);
+ r_gps->points[i].pressure = pt->pressure + pressure * influence;
+
return true;
}
@@ -1213,57 +1216,127 @@ bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float
/** \name Stroke Smooth UV
* \{ */
-bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
+bool BKE_gpencil_stroke_smooth_uv(
+ struct bGPDstroke *gps, int i, float influence, int iterations, struct bGPDstroke *r_gps)
{
- bGPDspoint *ptb = &gps->points[point_index];
+ /* If nothing to do, return early */
+ if (gps->totpoints <= 2 || iterations <= 0) {
+ return false;
+ }
+
+ /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
+
+ const bGPDspoint *pt = &gps->points[i];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
- /* Do nothing if not enough points */
- if (gps->totpoints <= 2) {
- return false;
+ /* If don't change the caps. */
+ if (!is_cyclic && ELEM(i, 0, gps->totpoints - 1)) {
+ r_gps->points[i].uv_rot = pt->uv_rot;
+ r_gps->points[i].uv_fac = pt->uv_fac;
+ return true;
}
- /* Compute theoretical optimal value */
- bGPDspoint *pta, *ptc;
- int before = point_index - 1;
- int after = point_index + 1;
+ float uv_rot = 0.0f;
+ float uv_fac = 0.0f;
+ const int n_half = iterations * iterations + iterations;
+ double w = 1.0;
+ double total_w = 0.0;
+ for (int step = iterations; step > 0; step--) {
+ int before = i - step;
+ int after = i + step;
+ float w_before = (float)w;
+ float w_after = (float)w;
- if (is_cyclic) {
- if (before < 0) {
- /* Sub to end point (before is already negative). */
- before = gps->totpoints + before;
- CLAMP(before, 0, gps->totpoints - 1);
+ if (is_cyclic) {
+ before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
+ after = after % gps->totpoints;
}
- if (after > gps->totpoints - 1) {
- /* Add to start point. */
- after = after - gps->totpoints;
- CLAMP(after, 0, gps->totpoints - 1);
+ else {
+ if (before < 0) {
+ w_before *= -before / (float)i;
+ before = 0;
+ }
+ if (after > gps->totpoints - 1) {
+ w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i);
+ after = gps->totpoints - 1;
+ }
}
- }
- else {
- CLAMP_MIN(before, 0);
- CLAMP_MAX(after, gps->totpoints - 1);
- }
- pta = &gps->points[before];
- ptc = &gps->points[after];
- /* the optimal value is the corresponding to the interpolation of the pressure
- * at the distance of point b
- */
- float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
- /* sometimes the factor can be wrong due stroke geometry, so use middle point */
- if ((fac < 0.0f) || (fac > 1.0f)) {
- fac = 0.5f;
+ /* Add both these points in relative coordinates to the weighted average sum. */
+ uv_rot += w_before * (gps->points[before].uv_rot - pt->uv_rot);
+ uv_rot += w_after * (gps->points[after].uv_rot - pt->uv_rot);
+ uv_fac += w_before * (gps->points[before].uv_fac - pt->uv_fac);
+ uv_fac += w_after * (gps->points[after].uv_fac - pt->uv_fac);
+
+ total_w += w_before;
+ total_w += w_after;
+
+ w *= (n_half + step) / (double)(n_half + 1 - step);
}
- float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
+ total_w += w;
+ /* The accumulated weight total_w should be
+ * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
+ * here, but sometimes not quite. */
+ uv_rot /= total_w;
+ uv_fac /= total_w;
- /* Based on influence factor, blend between original and optimal */
- ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
- CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
+ /* Based on influence factor, blend between original and optimal smoothed value. */
+ r_gps->points[i].uv_rot = pt->uv_rot + uv_rot * influence;
+ r_gps->points[i].uv_fac = pt->uv_fac + uv_fac * influence;
return true;
}
+void BKE_gpencil_stroke_smooth(bGPDstroke *gps,
+ const float influence,
+ const int iterations,
+ const bool smooth_position,
+ const bool smooth_strength,
+ const bool smooth_thickness,
+ const bool smooth_uv,
+ const bool keep_shape,
+ const float *weights)
+{
+ if (influence <= 0 || iterations <= 0) {
+ return;
+ }
+
+ /* Make a copy of the point data to avoid directionality of the smooth operation. */
+ bGPDstroke gps_old = *gps;
+ gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
+
+ /* Smooth stroke. */
+ for (int i = 0; i < gps->totpoints; i++) {
+ float val = influence;
+ if (weights != NULL) {
+ val *= weights[i];
+ if (val <= 0.0f) {
+ continue;
+ }
+ }
+
+ /* TODO: Currently the weights only control the influence, but is would be much better if they
+ * would control the distribution used in smooth, similar to how the ends are handled. */
+
+ /* Perform smoothing. */
+ if (smooth_position) {
+ BKE_gpencil_stroke_smooth_point(&gps_old, i, val, iterations, false, keep_shape, gps);
+ }
+ if (smooth_strength) {
+ BKE_gpencil_stroke_smooth_strength(&gps_old, i, val, iterations, gps);
+ }
+ if (smooth_thickness) {
+ BKE_gpencil_stroke_smooth_thickness(&gps_old, i, val, iterations, gps);
+ }
+ if (smooth_uv) {
+ BKE_gpencil_stroke_smooth_uv(&gps_old, i, val, iterations, gps);
+ }
+ }
+
+ /* Free the copied points array. */
+ MEM_freeN(gps_old.points);
+}
+
void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
int totpoints,
float (*points2d)[2],
@@ -3443,7 +3516,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
for (i = start; i < end; i++) {
pt = &gps_a->points[i];
pt->pressure += (avg_pressure - pt->pressure) * ratio;
- BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f, false);
+ BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f, 2, false, true, gps_a);
ratio += step;
/* In the center, reverse the ratio. */
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index 40c6fbcf67e..fc484e73967 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -910,18 +910,20 @@ static void curve_to_mesh_eval_ensure(Object &object)
*
* So we create temporary copy of the object which will use same data as the original bevel, but
* will have no modifiers. */
- Object bevel_object = {{nullptr}};
+ Object bevel_object;
+ blender::dna::zero_memory(bevel_object);
if (curve.bevobj != nullptr) {
- memcpy(&bevel_object, curve.bevobj, sizeof(bevel_object));
+ blender::dna::copy_memory(bevel_object, *curve.bevobj);
BLI_listbase_clear(&bevel_object.modifiers);
BKE_object_runtime_reset(&bevel_object);
curve.bevobj = &bevel_object;
}
/* Same thing for taper. */
- Object taper_object = {{nullptr}};
+ Object taper_object;
+ blender::dna::zero_memory(taper_object);
if (curve.taperobj != nullptr) {
- memcpy(&taper_object, curve.taperobj, sizeof(taper_object));
+ blender::dna::copy_memory(taper_object, *curve.taperobj);
BLI_listbase_clear(&taper_object.modifiers);
BKE_object_runtime_reset(&taper_object);
curve.taperobj = &taper_object;
@@ -1066,7 +1068,7 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
}
Object object_for_eval;
- memcpy(&object_for_eval, object, sizeof(object_for_eval));
+ blender::dna::zero_memory(object_for_eval);
if (object_for_eval.runtime.data_orig != nullptr) {
object_for_eval.data = object_for_eval.runtime.data_orig;
}
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 1e3b5d77fa7..098b7c52664 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -1236,7 +1236,7 @@ IDTypeInfo IDType_ID_OB = {
void BKE_object_workob_clear(Object *workob)
{
- memset(workob, 0, sizeof(Object));
+ blender::dna::zero_memory(*workob);
workob->scale[0] = workob->scale[1] = workob->scale[2] = 1.0f;
workob->dscale[0] = workob->dscale[1] = workob->dscale[2] = 1.0f;
@@ -3946,7 +3946,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
/* pass */
}
else {
- Object temp_ob = *dob->ob;
+ Object temp_ob = blender::dna::shallow_copy(*dob->ob);
/* Do not modify the original boundbox. */
temp_ob.runtime.bb = nullptr;
BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data);
diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh
index 6a988eda8a9..83f414f853a 100644
--- a/source/blender/blenlib/BLI_math_base.hh
+++ b/source/blender/blenlib/BLI_math_base.hh
@@ -12,7 +12,6 @@
#include <type_traits>
#include "BLI_math_base_safe.h"
-#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#ifdef WITH_GMP
@@ -21,6 +20,15 @@
namespace blender::math {
+template<typename T>
+inline constexpr bool is_math_float_type = (std::is_floating_point_v<T>
+#ifdef WITH_GMP
+ || std::is_same_v<T, mpq_class>
+#endif
+);
+
+template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>;
+
template<typename T> inline bool is_zero(const T &a)
{
return a == T(0);
@@ -84,19 +92,23 @@ template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T ceil(const
return std::ceil(a);
}
+template<typename T> inline T distance(const T &a, const T &b)
+{
+ return std::abs(a - b);
+}
+
template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(const T &a)
{
return a - std::floor(a);
}
-template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
-inline T interpolate(const T &a, const T &b, const T &t)
+template<typename T, typename FactorT, BLI_ENABLE_IF((is_math_float_type<FactorT>))>
+inline T interpolate(const T &a, const T &b, const FactorT &t)
{
return a * (1 - t) + b * t;
}
-template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
-inline T midpoint(const T &a, const T &b)
+template<typename T> inline T midpoint(const T &a, const T &b)
{
return (a + b) * T(0.5);
}
diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh
index 389307e331d..d9524eae746 100644
--- a/source/blender/blenlib/BLI_math_vec_types.hh
+++ b/source/blender/blenlib/BLI_math_vec_types.hh
@@ -321,7 +321,7 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size>
BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] * b[i]);
}
- friend vec_base operator*(const vec_base &a, T b)
+ template<typename FactorT> friend vec_base operator*(const vec_base &a, FactorT b)
{
BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] * b);
}
@@ -579,13 +579,4 @@ using double2 = vec_base<double, 2>;
using double3 = vec_base<double, 3>;
using double4 = vec_base<double, 4>;
-template<typename T>
-inline constexpr bool is_math_float_type = (std::is_floating_point_v<T>
-#ifdef WITH_GMP
- || std::is_same_v<T, mpq_class>
-#endif
-);
-
-template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>;
-
} // namespace blender
diff --git a/source/blender/blenlib/BLI_math_vector.hh b/source/blender/blenlib/BLI_math_vector.hh
index b1a3242ae52..b9f0939674e 100644
--- a/source/blender/blenlib/BLI_math_vector.hh
+++ b/source/blender/blenlib/BLI_math_vector.hh
@@ -10,7 +10,7 @@
#include <cmath>
#include <type_traits>
-#include "BLI_math_base_safe.h"
+#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
@@ -339,10 +339,10 @@ inline vec_base<T, 3> cross_poly(Span<vec_base<T, 3>> poly)
return n;
}
-template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+template<typename T, typename FactorT, int Size, BLI_ENABLE_IF((is_math_float_type<FactorT>))>
inline vec_base<T, Size> interpolate(const vec_base<T, Size> &a,
const vec_base<T, Size> &b,
- const T &t)
+ const FactorT &t)
{
return a * (1 - t) + b * t;
}
diff --git a/source/blender/blenlib/tests/BLI_math_base_test.cc b/source/blender/blenlib/tests/BLI_math_base_test.cc
index 62f2b2775d0..dd35deef4a8 100644
--- a/source/blender/blenlib/tests/BLI_math_base_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_base_test.cc
@@ -151,4 +151,9 @@ TEST(math_base, Midpoint)
EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f);
}
+TEST(math_base, InterpolateInt)
+{
+ EXPECT_EQ(math::interpolate(100, 200, 0.4f), 140);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_math_vector_test.cc b/source/blender/blenlib/tests/BLI_math_vector_test.cc
index 8c310645d6d..282be5f1963 100644
--- a/source/blender/blenlib/tests/BLI_math_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_vector_test.cc
@@ -85,4 +85,24 @@ TEST(math_vector, Clamp)
EXPECT_EQ(result_2.z, -50);
}
+TEST(math_vector, InterpolateInt)
+{
+ const int3 a(0, -100, 50);
+ const int3 b(0, 100, 100);
+ const int3 result = math::interpolate(a, b, 0.75);
+ EXPECT_EQ(result.x, 0);
+ EXPECT_EQ(result.y, 50);
+ EXPECT_EQ(result.z, 87);
+}
+
+TEST(math_vector, InterpolateFloat)
+{
+ const float3 a(40.0f, -100.0f, 50.0f);
+ const float3 b(20.0f, 100.0f, 100.0f);
+ const float3 result = math::interpolate(a, b, 0.5);
+ EXPECT_FLOAT_EQ(result.x, 30.0f);
+ EXPECT_FLOAT_EQ(result.y, 0.0f);
+ EXPECT_FLOAT_EQ(result.z, 75.0f);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 51b5cab1f7c..adc6b990869 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -2415,6 +2415,24 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Change grease pencil smooth iterations to match old results with new algorithm. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (md->type == eGpencilModifierType_Smooth) {
+ SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
+ if (gpmd->step == 1 && gpmd->factor <= 0.5f) {
+ gpmd->factor *= 2.0f;
+ }
+ else {
+ gpmd->step = 1 + (int)(gpmd->factor * max_ff(0.0f,
+ min_ff(5.1f * sqrtf(gpmd->step) - 3.0f,
+ gpmd->step + 2.0f)));
+ gpmd->factor = 1.0f;
+ }
+ }
+ }
+ }
}
/**
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index 5788e8efa07..d5566e4be98 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -167,7 +167,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
/* Temporary object to evaluate. */
Object *dupli_parent = data->dupli_parent;
Object *temp_dupli_object = &data->temp_dupli_object;
- *temp_dupli_object = *dob->ob;
+ *temp_dupli_object = blender::dna::shallow_copy(*dob->ob);
temp_dupli_object->base_flag = dupli_parent->base_flag | BASE_FROM_DUPLI;
temp_dupli_object->base_local_view_bits = dupli_parent->base_local_view_bits;
temp_dupli_object->runtime.local_collections_bits =
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 8506e90191f..a8fb344f366 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -3924,31 +3924,36 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
- for (int r = 0; r < repeat; r++) {
+ /* TODO use `BKE_gpencil_stroke_smooth` when the weights are better used. */
+ bGPDstroke gps_old = *gps;
+ gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
+ /* Here the iteration needs to be done outside the smooth functions,
+ * as there are points that don't get smoothed. */
+ for (int n = 0; n < repeat; n++) {
for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) {
+ if (only_selected && (gps->points[i].flag & GP_SPOINT_SELECT) == 0) {
continue;
}
- /* perform smoothing */
+ /* Perform smoothing. */
if (smooth_position) {
- BKE_gpencil_stroke_smooth_point(gps, i, factor, false);
+ BKE_gpencil_stroke_smooth_point(&gps_old, i, factor, 1, false, false, gps);
}
if (smooth_strength) {
- BKE_gpencil_stroke_smooth_strength(gps, i, factor);
+ BKE_gpencil_stroke_smooth_strength(&gps_old, i, factor, 1, gps);
}
if (smooth_thickness) {
- /* thickness need to repeat process several times */
- for (int r2 = 0; r2 < repeat * 2; r2++) {
- BKE_gpencil_stroke_smooth_thickness(gps, i, 1.0f - factor);
- }
+ BKE_gpencil_stroke_smooth_thickness(&gps_old, i, 1.0f - factor, 1, gps);
}
if (smooth_uv) {
- BKE_gpencil_stroke_smooth_uv(gps, i, factor);
+ BKE_gpencil_stroke_smooth_uv(&gps_old, i, factor, 1, gps);
}
}
+ if (n < repeat - 1) {
+ memcpy(gps_old.points, gps->points, sizeof(bGPDspoint) * gps->totpoints);
+ }
}
+ MEM_freeN(gps_old.points);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -4926,10 +4931,10 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20);
+ prop = RNA_def_int(ot->srna, "repeat", 2, 1, 1000, "Repeat", "", 1, 1000);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
+ RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 2.0f, "Factor", "", 0.0f, 1.0f);
RNA_def_boolean(ot->srna,
"only_selected",
true,
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 45a2247c65e..8fc300412d9 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1638,14 +1638,9 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
}
}
- /* smooth stroke */
- float reduce = 0.0f;
- float smoothfac = 1.0f;
- for (int r = 0; r < 1; r++) {
- for (int i = 0; i < gps->totpoints; i++) {
- BKE_gpencil_stroke_smooth_point(gps, i, smoothfac - reduce, false);
- }
- reduce += 0.25f; /* reduce the factor */
+ /* Smooth stroke. No copy of the stroke since there only a minor improvement here. */
+ for (int i = 0; i < gps->totpoints; i++) {
+ BKE_gpencil_stroke_smooth_point(gps, i, 1.0f, 2, false, true, gps);
}
/* if axis locked, reproject to plane locked */
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 65060e1bab5..8630b7f23d4 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -310,23 +310,6 @@ static void gpencil_stroke_pair_table(bContext *C,
}
}
-static void gpencil_interpolate_smooth_stroke(bGPDstroke *gps,
- float smooth_factor,
- int smooth_steps)
-{
- if (smooth_factor == 0.0f) {
- return;
- }
-
- float reduce = 0.0f;
- for (int r = 0; r < smooth_steps; r++) {
- for (int i = 0; i < gps->totpoints - 1; i++) {
- BKE_gpencil_stroke_smooth_point(gps, i, smooth_factor - reduce, false);
- BKE_gpencil_stroke_smooth_strength(gps, i, smooth_factor);
- }
- reduce += 0.25f; /* reduce the factor */
- }
-}
/* Perform interpolation */
static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
const bGPDstroke *gps_to,
@@ -553,7 +536,15 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
/* Update points position. */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
- gpencil_interpolate_smooth_stroke(new_stroke, tgpi->smooth_factor, tgpi->smooth_steps);
+ BKE_gpencil_stroke_smooth(new_stroke,
+ tgpi->smooth_factor,
+ tgpi->smooth_steps,
+ true,
+ true,
+ false,
+ false,
+ true,
+ NULL);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
@@ -1385,7 +1376,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
/* Update points position. */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
- gpencil_interpolate_smooth_stroke(new_stroke, smooth_factor, smooth_steps);
+ BKE_gpencil_stroke_smooth(
+ new_stroke, smooth_factor, smooth_steps, true, true, false, false, true, NULL);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 18dc8d4bc72..93a4a784674 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1197,29 +1197,21 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
gpencil_subdivide_stroke(gpd, gps, subdivide);
}
- /* Smooth stroke after subdiv - only if there's something to do for each iteration,
- * the factor is reduced to get a better smoothing
- * without changing too much the original stroke. */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
- (brush->gpencil_settings->draw_smoothfac > 0.0f)) {
- float reduce = 0.0f;
- for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) {
- for (i = 0; i < gps->totpoints - 1; i++) {
- BKE_gpencil_stroke_smooth_point(
- gps, i, brush->gpencil_settings->draw_smoothfac - reduce, false);
- BKE_gpencil_stroke_smooth_strength(gps, i, brush->gpencil_settings->draw_smoothfac);
- }
- reduce += 0.25f; /* reduce the factor */
- }
+ /* Smooth stroke after subdiv - only if there's something to do for each iteration.
+ * Keep the original stroke shape as much as possible. */
+ const float smoothfac = brush->gpencil_settings->draw_smoothfac;
+ const int iterations = brush->gpencil_settings->draw_smoothlvl;
+ if (brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) {
+ BKE_gpencil_stroke_smooth(gps, smoothfac, iterations, true, true, false, false, true, NULL);
}
/* If reproject the stroke using Stroke mode, need to apply a smooth because
* the reprojection creates small jitter. */
if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) {
float ifac = (float)brush->gpencil_settings->input_samples / 10.0f;
float sfac = interpf(1.0f, 0.2f, ifac);
- for (i = 0; i < gps->totpoints - 1; i++) {
- BKE_gpencil_stroke_smooth_point(gps, i, sfac, false);
- BKE_gpencil_stroke_smooth_strength(gps, i, sfac);
+ for (i = 0; i < gps->totpoints; i++) {
+ BKE_gpencil_stroke_smooth_point(gps, i, sfac, 2, false, true, gps);
+ BKE_gpencil_stroke_smooth_strength(gps, i, sfac, 2, gps);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 216971e514b..1e7159392e2 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -329,16 +329,16 @@ static bool gpencil_brush_smooth_apply(tGP_BrushEditData *gso,
/* perform smoothing */
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
- BKE_gpencil_stroke_smooth_point(gps, pt_index, inf, false);
+ BKE_gpencil_stroke_smooth_point(gps, pt_index, inf, 2, false, false, gps);
}
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
- BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf);
+ BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf, 2, gps);
}
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
- BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf);
+ BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf, 2, gps);
}
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
- BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf);
+ BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf, 2, gps);
}
return true;
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index c4f1ae88d28..cf28c88edf0 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -201,6 +201,8 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
ot->poll = WM_operator_winactive;
ot->ui = wm_usd_export_draw;
+ ot->flag = OPTYPE_REGISTER; /* No UNDO possible. */
+
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_USD,
FILE_BLENDER,
@@ -459,6 +461,8 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
ot->poll = WM_operator_winactive;
ot->ui = wm_usd_import_draw;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_USD,
FILE_BLENDER,
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index 24425b5a991..afd2c048379 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -1695,13 +1695,13 @@ static void object_apply_rotation(Object *ob, const float rmat[3][3])
static void object_apply_location(Object *ob, const float loc[3])
{
/* quick but weak */
- Object ob_prev = *ob;
+ Object ob_prev = blender::dna::shallow_copy(*ob);
float mat[4][4];
copy_m4_m4(mat, ob->obmat);
copy_v3_v3(mat[3], loc);
BKE_object_apply_mat4(ob, mat, true, true);
copy_v3_v3(mat[3], ob->loc);
- *ob = ob_prev;
+ *ob = blender::dna::shallow_copy(ob_prev);
copy_v3_v3(ob->loc, mat[3]);
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 1c4a1d7e8c9..aa77aab2283 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -2790,8 +2790,7 @@ static int image_flip_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
- /* force GPU re-upload, all image is invalid. */
- BKE_image_free_gputextures(ima);
+ BKE_image_partial_update_mark_full_update(ima);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@@ -2910,8 +2909,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
- /* Force GPU re-upload, all image is invalid. */
- BKE_image_free_gputextures(ima);
+ BKE_image_partial_update_mark_full_update(ima);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@@ -3001,8 +2999,7 @@ static int image_scale_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
- /* Force GPU re-upload, all image is invalid. */
- BKE_image_free_gputextures(ima);
+ BKE_image_partial_update_mark_full_update(ima);
DEG_id_tag_update(&ima->id, 0);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
index 0023b7c7b62..476bbdb63ae 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
@@ -150,7 +150,7 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase
}
else {
ten = outliner_add_element(
- &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
+ &space_outliner_, &tenlib->subtree, lib, nullptr, TSE_ID_BASE, a);
ten->directdata = lbarray[a];
ten->name = outliner_idcode_to_plural(GS(id->name));
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
index 25c7fdca9f6..e57b9df03f5 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
@@ -148,6 +148,9 @@ static bool stroke_dash(const bGPDstroke *gps,
bGPDstroke *stroke = BKE_gpencil_stroke_new(
ds->mat_nr < 0 ? gps->mat_nr : ds->mat_nr, size, gps->thickness);
+ if (ds->flag & GP_DASH_USE_CYCLIC) {
+ stroke->flag |= GP_STROKE_CYCLIC;
+ }
for (int is = 0; is < size; is++) {
bGPDspoint *p = &gps->points[new_stroke_offset + is];
@@ -337,6 +340,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(sub, &ds_ptr, "radius", 0, NULL, ICON_NONE);
uiItemR(sub, &ds_ptr, "opacity", 0, NULL, ICON_NONE);
uiItemR(sub, &ds_ptr, "material_index", 0, NULL, ICON_NONE);
+ uiItemR(sub, &ds_ptr, "use_cyclic", 0, NULL, ICON_NONE);
}
gpencil_modifier_panel_end(layout, ptr);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
index 8eaed56dc58..d80224e6639 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
@@ -105,15 +105,15 @@ static void deformStroke(GpencilModifierData *md,
/* Apply deformed coordinates. */
pt = gps->points;
+ bGPDstroke gps_old = *gps;
+ gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
for (i = 0; i < gps->totpoints; i++, pt++) {
copy_v3_v3(&pt->x, vert_coords[i]);
/* Smooth stroke. */
- if (mmd->smooth_factor > 0.0f) {
- for (int r = 0; r < mmd->smooth_step; r++) {
- BKE_gpencil_stroke_smooth_point(gps, i, mmd->smooth_factor, true);
- }
- }
+ BKE_gpencil_stroke_smooth_point(
+ &gps_old, i, mmd->smooth_factor, mmd->smooth_step, true, false, gps);
}
+ MEM_freeN(gps_old.points);
MEM_freeN(vert_coords);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
index f8201eb6b4f..eb51a247d87 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
@@ -34,10 +34,14 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RNA_access.h"
+
#include "MOD_gpencil_modifiertypes.h"
#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
+#include "MEM_guardedalloc.h"
+
static void initData(GpencilModifierData *md)
{
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
@@ -94,45 +98,40 @@ static void deformStroke(GpencilModifierData *md,
return;
}
- /* smooth stroke */
- if (mmd->factor > 0.0f) {
- for (int r = 0; r < mmd->step; r++) {
- for (int i = 0; i < gps->totpoints; i++) {
- MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
-
- /* verify vertex group */
- float weight = get_modifier_point_weight(
- dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr);
- if (weight < 0.0f) {
- continue;
- }
-
- /* Custom curve to modulate value. */
- if (use_curve) {
- float value = (float)i / (gps->totpoints - 1);
- weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
- }
-
- const float val = mmd->factor * weight;
- /* perform smoothing */
- if (mmd->flag & GP_SMOOTH_MOD_LOCATION) {
- BKE_gpencil_stroke_smooth_point(gps, i, val, false);
- }
- if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) {
- BKE_gpencil_stroke_smooth_strength(gps, i, val);
- }
- if ((mmd->flag & GP_SMOOTH_MOD_THICKNESS) && (val > 0.0f)) {
- /* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 10; r2++) {
- BKE_gpencil_stroke_smooth_thickness(gps, i, val);
- }
- }
- if (mmd->flag & GP_SMOOTH_MOD_UV) {
- BKE_gpencil_stroke_smooth_uv(gps, i, val);
- }
+ if (mmd->factor <= 0.0f || mmd->step <= 0) {
+ return;
+ }
+
+ float *weights = NULL;
+ if (def_nr != -1 || use_curve) {
+ weights = MEM_malloc_arrayN(gps->totpoints, sizeof(*weights), __func__);
+ /* Calculate weights. */
+ for (int i = 0; i < gps->totpoints; i++) {
+ MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
+
+ /* Verify vertex group. */
+ float weight = get_modifier_point_weight(
+ dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr);
+
+ /* Custom curve to modulate value. */
+ if (use_curve && weight > 0.0f) {
+ float value = (float)i / (gps->totpoints - 1);
+ weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
}
+
+ weights[i] = weight;
}
}
+ BKE_gpencil_stroke_smooth(gps,
+ mmd->factor,
+ mmd->step,
+ mmd->flag & GP_SMOOTH_MOD_LOCATION,
+ mmd->flag & GP_SMOOTH_MOD_STRENGTH,
+ mmd->flag & GP_SMOOTH_MOD_THICKNESS,
+ mmd->flag & GP_SMOOTH_MOD_UV,
+ mmd->flag & GP_SMOOTH_KEEP_SHAPE,
+ weights);
+ MEM_SAFE_FREE(weights);
}
static void bakeModifier(struct Main *UNUSED(bmain),
@@ -161,7 +160,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
{
- uiLayout *row;
+ uiLayout *row, *col;
uiLayout *layout = panel->layout;
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
@@ -177,6 +176,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "factor", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "step", 0, IFACE_("Repeat"), ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_edit_position"));
+ uiItemR(col, ptr, "keep_shape", 0, NULL, ICON_NONE);
+
gpencil_modifier_panel_end(layout, ptr);
}
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 01d0cfcd302..2b5ea39617e 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -107,10 +107,10 @@ list(APPEND LIB
blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WIN32)
- set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /WHOLEARCHIVE:${USD_DEBUG_LIB}")
- set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /WHOLEARCHIVE:${USD_RELEASE_LIB}")
- set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /WHOLEARCHIVE:${USD_RELEASE_LIB}")
- set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /WHOLEARCHIVE:${USD_RELEASE_LIB}")
+ set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:Debug>:/WHOLEARCHIVE:${USD_DEBUG_LIB}>")
+ set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:Release>:/WHOLEARCHIVE:${USD_RELEASE_LIB}>")
+ set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:RelWithDebInfo>:/WHOLEARCHIVE:${USD_RELEASE_LIB}>")
+ set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:MinSizeRel>:/WHOLEARCHIVE:${USD_RELEASE_LIB}>")
endif()
# Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives
diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc
index 649ff45a6d0..55b9557dfb5 100644
--- a/source/blender/io/usd/intern/usd_reader_light.cc
+++ b/source/blender/io/usd/intern/usd_reader_light.cc
@@ -9,8 +9,6 @@
#include "DNA_light_types.h"
#include "DNA_object_types.h"
-#include <pxr/usd/usdLux/light.h>
-
#include <pxr/usd/usdLux/diskLight.h>
#include <pxr/usd/usdLux/distantLight.h>
#include <pxr/usd/usdLux/rectLight.h>
@@ -40,14 +38,17 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
if (!prim_) {
return;
}
+#if PXR_VERSION >= 2111
+ pxr::UsdLuxLightAPI light_api(prim_);
+#else
+ pxr::UsdLuxLight light_api(prim_);
+#endif
- pxr::UsdLuxLight light_prim(prim_);
-
- if (!light_prim) {
+ if (!light_api) {
return;
}
- pxr::UsdLuxShapingAPI shaping_api(light_prim);
+ pxr::UsdLuxShapingAPI shaping_api;
/* Set light type. */
@@ -63,6 +64,8 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
else if (prim_.IsA<pxr::UsdLuxSphereLight>()) {
blight->type = LA_LOCAL;
+ shaping_api = pxr::UsdLuxShapingAPI(prim_);
+
if (shaping_api && shaping_api.GetShapingConeAngleAttr().IsAuthored()) {
blight->type = LA_SPOT;
}
@@ -73,7 +76,7 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
/* Set light values. */
- if (pxr::UsdAttribute intensity_attr = light_prim.GetIntensityAttr()) {
+ if (pxr::UsdAttribute intensity_attr = light_api.GetIntensityAttr()) {
float intensity = 0.0f;
if (intensity_attr.Get(&intensity, motionSampleTime)) {
blight->energy = intensity * this->import_params_.light_intensity_scale;
@@ -92,14 +95,14 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
light_prim.GetDiffuseAttr().Get(&diffuse, motionSampleTime);
#endif
- if (pxr::UsdAttribute spec_attr = light_prim.GetSpecularAttr()) {
+ if (pxr::UsdAttribute spec_attr = light_api.GetSpecularAttr()) {
float spec = 0.0f;
if (spec_attr.Get(&spec, motionSampleTime)) {
blight->spec_fac = spec;
}
}
- if (pxr::UsdAttribute color_attr = light_prim.GetColorAttr()) {
+ if (pxr::UsdAttribute color_attr = light_api.GetColorAttr()) {
pxr::GfVec3f color;
if (color_attr.Get(&color, motionSampleTime)) {
blight->r = color[0];
diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc
index 06f7d3ce3f5..583c58a1356 100644
--- a/source/blender/io/usd/intern/usd_reader_stage.cc
+++ b/source/blender/io/usd/intern/usd_reader_stage.cc
@@ -18,7 +18,13 @@
#include <pxr/usd/usdGeom/nurbsCurves.h>
#include <pxr/usd/usdGeom/scope.h>
#include <pxr/usd/usdGeom/xform.h>
-#include <pxr/usd/usdLux/light.h>
+
+#if PXR_VERSION >= 2111
+# include <pxr/usd/usdLux/boundableLightBase.h>
+# include <pxr/usd/usdLux/nonboundableLightBase.h>
+#else
+# include <pxr/usd/usdLux/light.h>
+#endif
#include <iostream>
@@ -55,7 +61,12 @@ USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim
if (params_.import_meshes && prim.IsA<pxr::UsdGeomMesh>()) {
return new USDMeshReader(prim, params_, settings_);
}
+#if PXR_VERSION >= 2111
+ if (params_.import_lights && (prim.IsA<pxr::UsdLuxBoundableLightBase>() ||
+ prim.IsA<pxr::UsdLuxNonboundableLightBase>())) {
+#else
if (params_.import_lights && prim.IsA<pxr::UsdLuxLight>()) {
+#endif
return new USDLightReader(prim, params_, settings_);
}
if (params_.import_volumes && prim.IsA<pxr::UsdVolVolume>()) {
@@ -82,7 +93,11 @@ USDPrimReader *USDStageReader::create_reader(const pxr::UsdPrim &prim)
if (prim.IsA<pxr::UsdGeomMesh>()) {
return new USDMeshReader(prim, params_, settings_);
}
+#if PXR_VERSION >= 2111
+ if (prim.IsA<pxr::UsdLuxBoundableLightBase>() || prim.IsA<pxr::UsdLuxNonboundableLightBase>()) {
+#else
if (prim.IsA<pxr::UsdLuxLight>()) {
+#endif
return new USDLightReader(prim, params_, settings_);
}
if (prim.IsA<pxr::UsdVolVolume>()) {
diff --git a/source/blender/io/usd/intern/usd_reader_xform.cc b/source/blender/io/usd/intern/usd_reader_xform.cc
index 4cb7d4e1845..bc6e2dd5297 100644
--- a/source/blender/io/usd/intern/usd_reader_xform.cc
+++ b/source/blender/io/usd/intern/usd_reader_xform.cc
@@ -131,7 +131,7 @@ bool USDXformReader::is_root_xform_prim() const
return false;
}
- if (prim_.IsInMaster()) {
+ if (prim_.IsInPrototype()) {
/* We don't consider prototypes to be root prims,
* because we never want to apply global scaling
* or rotations to the prototypes themselves. */
diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc
index 282393bbcd2..982bc31d767 100644
--- a/source/blender/io/usd/intern/usd_writer_light.cc
+++ b/source/blender/io/usd/intern/usd_writer_light.cc
@@ -33,7 +33,12 @@ void USDLightWriter::do_write(HierarchyContext &context)
pxr::UsdTimeCode timecode = get_export_time_code();
Light *light = static_cast<Light *>(context.object->data);
- pxr::UsdLuxLight usd_light;
+#if PXR_VERSION >= 2111
+ pxr::UsdLuxLightAPI usd_light_api;
+#else
+ pxr::UsdLuxLight usd_light_api;
+
+#endif
switch (light->type) {
case LA_AREA:
@@ -42,21 +47,33 @@ void USDLightWriter::do_write(HierarchyContext &context)
case LA_AREA_ELLIPSE: { /* An ellipse light will deteriorate into a disk light. */
pxr::UsdLuxDiskLight disk_light = pxr::UsdLuxDiskLight::Define(stage, usd_path);
disk_light.CreateRadiusAttr().Set(light->area_size, timecode);
- usd_light = disk_light;
+#if PXR_VERSION >= 2111
+ usd_light_api = disk_light.LightAPI();
+#else
+ usd_light_api = disk_light;
+#endif
break;
}
case LA_AREA_RECT: {
pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path);
rect_light.CreateWidthAttr().Set(light->area_size, timecode);
rect_light.CreateHeightAttr().Set(light->area_sizey, timecode);
- usd_light = rect_light;
+#if PXR_VERSION >= 2111
+ usd_light_api = rect_light.LightAPI();
+#else
+ usd_light_api = rect_light;
+#endif
break;
}
case LA_AREA_SQUARE: {
pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path);
rect_light.CreateWidthAttr().Set(light->area_size, timecode);
rect_light.CreateHeightAttr().Set(light->area_size, timecode);
- usd_light = rect_light;
+#if PXR_VERSION >= 2111
+ usd_light_api = rect_light.LightAPI();
+#else
+ usd_light_api = rect_light;
+#endif
break;
}
}
@@ -64,12 +81,23 @@ void USDLightWriter::do_write(HierarchyContext &context)
case LA_LOCAL: {
pxr::UsdLuxSphereLight sphere_light = pxr::UsdLuxSphereLight::Define(stage, usd_path);
sphere_light.CreateRadiusAttr().Set(light->area_size, timecode);
- usd_light = sphere_light;
+#if PXR_VERSION >= 2111
+ usd_light_api = sphere_light.LightAPI();
+#else
+ usd_light_api = sphere_light;
+#endif
break;
}
- case LA_SUN:
- usd_light = pxr::UsdLuxDistantLight::Define(stage, usd_path);
+ case LA_SUN: {
+ pxr::UsdLuxDistantLight distant_light = pxr::UsdLuxDistantLight::Define(stage, usd_path);
+ /* TODO(makowalski): set angle attribute here. */
+#if PXR_VERSION >= 2111
+ usd_light_api = distant_light.LightAPI();
+#else
+ usd_light_api = distant_light;
+#endif
break;
+ }
default:
BLI_assert_msg(0, "is_supported() returned true for unsupported light type");
}
@@ -85,10 +113,10 @@ void USDLightWriter::do_write(HierarchyContext &context)
else {
usd_intensity = light->energy / 100.0f;
}
- usd_light.CreateIntensityAttr().Set(usd_intensity, timecode);
+ usd_light_api.CreateIntensityAttr().Set(usd_intensity, timecode);
- usd_light.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode);
- usd_light.CreateSpecularAttr().Set(light->spec_fac, timecode);
+ usd_light_api.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode);
+ usd_light_api.CreateSpecularAttr().Set(light->spec_fac, timecode);
}
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc
index 29ab0479f6e..b548a666ef7 100644
--- a/source/blender/io/usd/intern/usd_writer_material.cc
+++ b/source/blender/io/usd/intern/usd_writer_material.cc
@@ -163,7 +163,7 @@ void create_usd_preview_surface_material(const USDExporterContext &usd_export_co
created_shader = create_usd_preview_shader(usd_export_context, usd_material, input_node);
preview_surface.CreateInput(input_spec.input_name, input_spec.input_type)
- .ConnectToSource(created_shader, input_spec.source_name);
+ .ConnectToSource(created_shader.ConnectableAPI(), input_spec.source_name);
}
else if (input_spec.set_default_value) {
/* Set hardcoded value. */
@@ -217,7 +217,7 @@ void create_usd_viewport_material(const USDExporterContext &usd_export_context,
shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic);
/* Connect the shader and the material together. */
- usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
+ usd_material.CreateSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), usdtokens::surface);
}
/* Return USD Preview Surface input map singleton. */
@@ -293,12 +293,12 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token)
.Set(pxr::TfToken(uv_set));
usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
- .ConnectToSource(uv_shader, usdtokens::result);
+ .ConnectToSource(uv_shader.ConnectableAPI(), usdtokens::result);
}
else {
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv);
usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
- .ConnectToSource(uv_shader, usdtokens::result);
+ .ConnectToSource(uv_shader.ConnectableAPI(), usdtokens::result);
}
}
@@ -313,7 +313,7 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
if (uv_shader.GetPrim().IsValid()) {
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv);
usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
- .ConnectToSource(uv_shader, usdtokens::result);
+ .ConnectToSource(uv_shader.ConnectableAPI(), usdtokens::result);
}
}
}
@@ -488,7 +488,7 @@ static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &u
case SH_NODE_BSDF_DIFFUSE:
case SH_NODE_BSDF_PRINCIPLED: {
shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface));
- material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
+ material.CreateSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), usdtokens::surface);
break;
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index aa63e65b1e8..8c9b04a5ac3 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -33,7 +33,7 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj
{
/* We need to copy the object because it may be in temporary space. */
Object *obj_eval = DEG_get_evaluated_object(depsgraph, mesh_object);
- export_object_eval_ = *obj_eval;
+ export_object_eval_ = dna::shallow_copy(*obj_eval);
export_mesh_eval_ = export_params.apply_modifiers ?
BKE_object_get_evaluated_mesh(&export_object_eval_) :
BKE_object_get_pre_modified_mesh(&export_object_eval_);
diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h
index f1626781fc6..97cc588e639 100644
--- a/source/blender/makesdna/DNA_curves_types.h
+++ b/source/blender/makesdna/DNA_curves_types.h
@@ -28,6 +28,7 @@ typedef enum CurveType {
CURVE_TYPE_BEZIER = 2,
CURVE_TYPE_NURBS = 3,
} CurveType;
+#define CURVE_TYPES_NUM 4
typedef enum HandleType {
/** The handle can be moved anywhere, and doesn't influence the point's other handle. */
diff --git a/source/blender/makesdna/DNA_defs.h b/source/blender/makesdna/DNA_defs.h
index b4230209dd5..bfeb809b369 100644
--- a/source/blender/makesdna/DNA_defs.h
+++ b/source/blender/makesdna/DNA_defs.h
@@ -46,3 +46,100 @@
/* non-id name variables should use this length */
#define MAX_NAME 64
+
+/* #DNA_DEFINE_CXX_METHODS is used to define C++ methods which are needed for proper/safe resource
+ * management, making unsafe (from an ownership perspective: i.e. pointers which sometimes needs to
+ * be set to nullptr on copy, sometimes needs to be dupalloc-ed) operations explicit, and taking
+ * care of compiler specific warnings when dealing with members marked with DNA_DEPRECATED.
+ *
+ * The `class_name` argument is to match the structure name the macro is used from.
+ *
+ * Typical usage example:
+ *
+ * typedef struct Object {
+ * DNA_DEFINE_CXX_METHODS(Object)
+ * } Object;
+ */
+#ifndef __cplusplus
+# define DNA_DEFINE_CXX_METHODS(class_name)
+#else
+
+/* Forward-declared here since there is no simple header file to be pulled for this functionality.
+ * Avoids pulling `string.h` from this header to get access to #memcpy. */
+extern "C" void _DNA_internal_memcpy(void *dst, const void *src, size_t size);
+extern "C" void _DNA_internal_memzero(void *dst, size_t size);
+
+namespace blender::dna::internal {
+
+template<class T> class ShallowDataConstRef {
+ public:
+ constexpr explicit ShallowDataConstRef(const T &ref) : ref_(ref)
+ {
+ }
+
+ inline const T *get_pointer() const
+ {
+ return &ref_;
+ }
+
+ private:
+ const T &ref_;
+};
+
+} // namespace blender::dna::internal
+
+# define DNA_DEFINE_CXX_METHODS(class_name) \
+ class_name() = default; \
+ ~class_name() = default; \
+ /* Delete copy and assignment, which are not safe for resource ownership. */ \
+ class_name(const class_name &other) = delete; \
+ class_name(class_name &&other) noexcept = delete; \
+ class_name &operator=(const class_name &other) = delete; \
+ class_name &operator=(class_name &&other) = delete; \
+ /* Support for shallow copy. */ \
+ /* NOTE: Calling the default constructor works-around deprecated warning generated by GCC. */ \
+ class_name(const blender::dna::internal::ShallowDataConstRef<class_name> ref) : class_name() \
+ { \
+ _DNA_internal_memcpy(this, ref.get_pointer(), sizeof(class_name)); \
+ } \
+ class_name &operator=(const blender::dna::internal::ShallowDataConstRef<class_name> ref) \
+ { \
+ if (this != ref.get_pointer()) { \
+ _DNA_internal_memcpy(this, ref.get_pointer(), sizeof(class_name)); \
+ } \
+ return *this; \
+ }
+
+namespace blender::dna {
+
+/* Creates shallow copy of the given object.
+ * The entire object is copied as-is using memory copy.
+ *
+ * Typical usage:
+ * Object temp_object = blender::dna::shallow_copy(*input_object);
+ *
+ * From the implementation detail go via copy constructor/assign operator defined in the structure.
+ */
+template<class T>
+[[nodiscard]] inline internal::ShallowDataConstRef<T> shallow_copy(const T &other)
+{
+ return internal::ShallowDataConstRef(other);
+}
+
+/* Fill underlying memory used by DNA object with zeroes. */
+template<class T> inline void zero_memory(T &object)
+{
+ /* TODO(sergey): Consider adding static assert for T being a trivial type. */
+ _DNA_internal_memzero(&object, sizeof(T));
+}
+
+/* Copy memory from one DNA object to another. */
+template<class T> inline void copy_memory(T &dst, const T &src)
+{
+ /* TODO(sergey): Consider adding static assert for T being a trivial type. */
+ _DNA_internal_memcpy(&dst, &src, sizeof(T));
+}
+
+} // namespace blender::dna
+
+#endif
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index a87e7a7c397..9d14ca039ac 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -193,7 +193,7 @@
.vgname = "", \
.pass_index = 0, \
.flag = GP_SMOOTH_MOD_LOCATION, \
- .factor = 0.5f, \
+ .factor = 1.0f, \
.step = 1, \
.layer_pass = 0, \
.curve_intensity = NULL, \
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 1054c309ee5..7568dc5ff9a 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -522,7 +522,7 @@ typedef struct DashGpencilModifierSegment {
float radius;
float opacity;
int mat_nr;
- int _pad;
+ int flag;
} DashGpencilModifierSegment;
typedef struct DashGpencilModifierData {
@@ -546,6 +546,14 @@ typedef struct DashGpencilModifierData {
} DashGpencilModifierData;
+typedef enum eDashGpencil_Flag {
+ GP_DASH_INVERT_LAYER = (1 << 0),
+ GP_DASH_INVERT_PASS = (1 << 1),
+ GP_DASH_INVERT_LAYERPASS = (1 << 2),
+ GP_DASH_INVERT_MATERIAL = (1 << 3),
+ GP_DASH_USE_CYCLIC = (1 << 7),
+} eDashGpencil_Flag;
+
typedef struct MirrorGpencilModifierData {
GpencilModifierData modifier;
struct Object *object;
@@ -750,6 +758,7 @@ typedef enum eSmoothGpencil_Flag {
GP_SMOOTH_INVERT_LAYERPASS = (1 << 7),
GP_SMOOTH_INVERT_MATERIAL = (1 << 4),
GP_SMOOTH_CUSTOM_CURVE = (1 << 8),
+ GP_SMOOTH_KEEP_SHAPE = (1 << 9),
} eSmoothGpencil_Flag;
typedef struct ArmatureGpencilModifierData {
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 9e0bf7dcc5a..c3708e25ee7 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -233,6 +233,8 @@ enum eObjectLineArt_Flags {
};
typedef struct Object {
+ DNA_DEFINE_CXX_METHODS(Object)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
diff --git a/source/blender/makesdna/intern/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c
index 8c86ef69ebd..bc2584fe57a 100644
--- a/source/blender/makesdna/intern/dna_utils.c
+++ b/source/blender/makesdna/intern/dna_utils.c
@@ -308,3 +308,21 @@ const char *DNA_struct_rename_legacy_hack_alias_from_static(const char *name)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal helpers for C++
+ * \{ */
+
+void _DNA_internal_memcpy(void *dst, const void *src, size_t size);
+void _DNA_internal_memcpy(void *dst, const void *src, const size_t size)
+{
+ memcpy(dst, src, size);
+}
+
+void _DNA_internal_memzero(void *dst, size_t size);
+void _DNA_internal_memzero(void *dst, const size_t size)
+{
+ memset(dst, 0, size);
+}
+
+/** \} */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 0d2f265a9b5..12ec7262906 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -620,6 +620,7 @@ static int preprocess_include(char *maindata, const int maindata_len)
int newlen = 0;
comment = 0;
a = maindata_len;
+ bool skip_until_closing_brace = false;
while (a--) {
if (cp[0] == '/' && cp[1] == '*') {
@@ -646,6 +647,17 @@ static int preprocess_include(char *maindata, const int maindata_len)
a -= 13;
cp += 13;
}
+ else if (match_identifier(cp, "DNA_DEFINE_CXX_METHODS")) {
+ /* single values are skipped already, so decrement 1 less */
+ a -= 21;
+ cp += 21;
+ skip_until_closing_brace = true;
+ }
+ else if (skip_until_closing_brace) {
+ if (cp[0] == ')') {
+ skip_until_closing_brace = false;
+ }
+ }
else {
md[0] = cp[0];
md++;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index c86a852b0ea..b2b6bdbcffc 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1355,7 +1355,8 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
/* Smoothing factor for new strokes */
prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac");
- RNA_def_property_range(prop, 0.0, 2.0f);
+ RNA_def_property_range(prop, 0.0, 2.0);
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 10, 3);
RNA_def_property_ui_text(
prop,
"Smooth",
@@ -1366,7 +1367,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
/* Iterations of the Smoothing factor */
prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl");
- RNA_def_property_range(prop, 1, 3);
+ RNA_def_property_range(prop, 0, 100);
RNA_def_property_ui_text(prop, "Iterations", "Number of times to smooth newly created strokes");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index edeb7443957..9e25ddaf790 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -1026,11 +1026,16 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "step");
- RNA_def_property_range(prop, 1, 10);
+ RNA_def_property_range(prop, 1, 1000);
RNA_def_property_ui_text(
prop, "Step", "Number of times to apply smooth (high numbers can reduce fps)");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "keep_shape", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_KEEP_SHAPE);
+ RNA_def_property_ui_text(prop, "Keep Shape", "Smooth the details, but keep the overall shape");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_INVERT_LAYER);
RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
@@ -3738,6 +3743,11 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna)
"Use this index on generated segment. -1 means using the existing material");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_USE_CYCLIC);
+ RNA_def_property_ui_text(prop, "Use Cyclic", "Enable cyclic on individual stroke dashes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
srna = RNA_def_struct(brna, "DashGpencilModifierData", "GpencilModifier");
RNA_def_struct_ui_text(srna, "Dash Modifier", "Create dot-dash effect for strokes");
RNA_def_struct_sdna(srna, "DashGpencilModifierData");
@@ -3789,17 +3799,17 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_LAYER);
RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_MATERIAL);
RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_PASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -3810,7 +3820,7 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");