diff options
author | Joseph Eagar <joeedh@gmail.com> | 2022-09-24 23:38:38 +0300 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2022-09-24 23:38:38 +0300 |
commit | 6637ad543241211bd49a02bcc83f03eef4f19584 (patch) | |
tree | a54ccdbde87f1cf5926d073cf1f36062c2917047 /source/blender/blenkernel | |
parent | 6482e092d06ee1ab862f9e98ddccbe8c23fb2bb0 (diff) | |
parent | 2ff5d42cd3b41cc1412d4a896d8f9e8d2977686f (diff) |
Merge branch 'master' into temp-pbvh-vbos
Diffstat (limited to 'source/blender/blenkernel')
103 files changed, 1953 insertions, 5190 deletions
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index da1e45ababd..cb9c4256e33 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -104,9 +104,6 @@ struct DerivedMesh { int num_alloc; } looptris; - /* use for converting to BMesh which doesn't store bevel weight and edge crease by default */ - char cd_flag; - short tangent_mask; /* which tangent layers are calculated */ /** Loop tessellation cache (WARNING! Only call inside threading-protected code!) */ diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index fbdacee139c..7b13b8a2b09 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -16,6 +16,10 @@ struct Mesh; struct PointCloud; +namespace blender::fn { +class MultiFunction; +class GField; +} // namespace blender::fn namespace blender::bke { @@ -163,6 +167,27 @@ template<typename T> struct AttributeReader { }; /** + * A utility to make sure attribute values are valid, for attributes like "material_index" which + * can only be positive, or attributes that represent enum options. This is usually only necessary + * when writing attributes from an untrusted/arbitrary user input. + */ +struct AttributeValidator { + /** + * Single input, single output function that corrects attribute values if necessary. + */ + const fn::MultiFunction *function; + + operator bool() const + { + return this->function != nullptr; + } + /** + * Return a field that creates corrected attribute values. + */ + fn::GField validate_field_if_necessary(const fn::GField &field) const; +}; + +/** * Result when looking up an attribute from some geometry with read and write access. After writing * to the attribute, the #finish method has to be called. This may invalidate caches based on this * attribute. @@ -239,7 +264,9 @@ template<typename T> struct SpanAttributeWriter { */ void finish() { - this->span.save(); + if (this->span.varray()) { + this->span.save(); + } if (this->tag_modified_fn) { this->tag_modified_fn(); } @@ -314,7 +341,9 @@ struct GSpanAttributeWriter { void finish() { - this->span.save(); + if (this->span.varray()) { + this->span.save(); + } if (this->tag_modified_fn) { this->tag_modified_fn(); } @@ -343,7 +372,7 @@ struct AttributeAccessorFunctions { eAttrDomain to_domain); bool (*for_all)(const void *owner, FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn); - + AttributeValidator (*lookup_validator)(const void *owner, const AttributeIDRef &attribute_id); GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id); bool (*remove)(void *owner, const AttributeIDRef &attribute_id); bool (*add)(void *owner, @@ -498,6 +527,14 @@ class AttributeAccessor { } /** + * Same as the generic version above, but should be used when the type is known at compile time. + */ + AttributeValidator lookup_validator(const AttributeIDRef &attribute_id) const + { + return fn_->lookup_validator(owner_, attribute_id); + } + + /** * Interpolate data from one domain to another. */ GVArray adapt_domain(const GVArray &varray, @@ -659,6 +696,8 @@ class MutableAttributeAccessor : public AttributeAccessor { * The "only" in the name indicates that the caller should not read existing values from the * span. If the attribute is not stored as span internally, the existing values won't be copied * over to the span. + * + * For trivial types, the values in a newly created attribute will not be initialized. */ GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, const eAttrDomain domain, @@ -671,7 +710,9 @@ class MutableAttributeAccessor : public AttributeAccessor { SpanAttributeWriter<T> lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, const eAttrDomain domain) { - AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(attribute_id, domain); + AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>( + attribute_id, domain, AttributeInitConstruct()); + if (attribute) { return SpanAttributeWriter<T>{std::move(attribute), false}; } diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 770937688d7..abd8b33b260 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -46,6 +46,58 @@ inline void convert_to_static_type(const eCustomDataType data_type, const Func & } /* -------------------------------------------------------------------- */ +/** \name Mix two values of the same type. + * + * This is just basic linear interpolation. + * \{ */ + +template<typename T> T mix2(float factor, const T &a, const T &b); + +template<> inline bool mix2(const float factor, const bool &a, const bool &b) +{ + return ((1.0f - factor) * a + factor * b) >= 0.5f; +} + +template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b) +{ + return static_cast<int8_t>(std::round((1.0f - factor) * a + factor * b)); +} + +template<> inline int mix2(const float factor, const int &a, const int &b) +{ + return static_cast<int>(std::round((1.0f - factor) * a + factor * b)); +} + +template<> inline float mix2(const float factor, const float &a, const float &b) +{ + return (1.0f - factor) * a + factor * b; +} + +template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b) +{ + return math::interpolate(a, b, factor); +} + +template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b) +{ + return math::interpolate(a, b, factor); +} + +template<> +inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) +{ + return math::interpolate(a, b, factor); +} + +template<> +inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b) +{ + return math::interpolate(a, b, factor); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Mix three values of the same type. * * This is typically used to interpolate values within a triangle. @@ -117,53 +169,85 @@ inline ColorGeometry4b mix3(const float3 &weights, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Mix two values of the same type. +/** \name Mix four values of the same type. * - * This is just basic linear interpolation. * \{ */ -template<typename T> T mix2(float factor, const T &a, const T &b); +template<typename T> +T mix4(const float4 &weights, const T &v0, const T &v1, const T &v2, const T &v3); -template<> inline bool mix2(const float factor, const bool &a, const bool &b) +template<> +inline int8_t mix4( + const float4 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2, const int8_t &v3) { - return ((1.0f - factor) * a + factor * b) >= 0.5f; + return static_cast<int8_t>( + std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3)); } -template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b) +template<> +inline bool mix4( + const float4 &weights, const bool &v0, const bool &v1, const bool &v2, const bool &v3) { - return static_cast<int8_t>(std::round((1.0f - factor) * a + factor * b)); + return (weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3) >= 0.5f; } -template<> inline int mix2(const float factor, const int &a, const int &b) +template<> +inline int mix4(const float4 &weights, const int &v0, const int &v1, const int &v2, const int &v3) { - return static_cast<int>(std::round((1.0f - factor) * a + factor * b)); + return static_cast<int>( + std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3)); } -template<> inline float mix2(const float factor, const float &a, const float &b) +template<> +inline float mix4( + const float4 &weights, const float &v0, const float &v1, const float &v2, const float &v3) { - return (1.0f - factor) * a + factor * b; + return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3; } -template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b) +template<> +inline float2 mix4( + const float4 &weights, const float2 &v0, const float2 &v1, const float2 &v2, const float2 &v3) { - return math::interpolate(a, b, factor); + return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3; } -template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b) +template<> +inline float3 mix4( + const float4 &weights, const float3 &v0, const float3 &v1, const float3 &v2, const float3 &v3) { - return math::interpolate(a, b, factor); + return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3; } template<> -inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) +inline ColorGeometry4f mix4(const float4 &weights, + const ColorGeometry4f &v0, + const ColorGeometry4f &v1, + const ColorGeometry4f &v2, + const ColorGeometry4f &v3) { - return math::interpolate(a, b, factor); + ColorGeometry4f result; + interp_v4_v4v4v4v4(result, v0, v1, v2, v3, weights); + return result; } template<> -inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b) +inline ColorGeometry4b mix4(const float4 &weights, + const ColorGeometry4b &v0, + const ColorGeometry4b &v1, + const ColorGeometry4b &v2, + const ColorGeometry4b &v3) { - return math::interpolate(a, b, factor); + const float4 v0_f{&v0.r}; + const float4 v1_f{&v1.r}; + const float4 v2_f{&v2.r}; + const float4 v3_f{&v3.r}; + float4 mixed; + interp_v4_v4v4v4v4(mixed, v0_f, v1_f, v2_f, v3_f, weights); + return ColorGeometry4b{static_cast<uint8_t>(mixed[0]), + static_cast<uint8_t>(mixed[1]), + static_cast<uint8_t>(mixed[2]), + static_cast<uint8_t>(mixed[3])}; } /** \} */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index ee9c7a964d9..46d6c97e1ff 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -30,8 +30,8 @@ extern "C" { /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file * was written with too new a version. */ -#define BLENDER_FILE_MIN_VERSION 300 -#define BLENDER_FILE_MIN_SUBVERSION 43 +#define BLENDER_FILE_MIN_VERSION 304 +#define BLENDER_FILE_MIN_SUBVERSION 0 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 9f150c13d6e..0d67152dec8 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -218,7 +218,7 @@ class CurvesGeometry : public ::CurvesGeometry { /** * How many evaluated points to create for each segment when evaluating Bezier, - * Catmull Rom, and NURBS curves. On the curve domain. + * Catmull Rom, and NURBS curves. On the curve domain. Values must be one or greater. */ VArray<int> resolution() const; /** Mutable access to curve resolution. Call #tag_topology_changed after changes. */ @@ -709,7 +709,7 @@ void interpolate_to_evaluated(const GSpan src, const Span<int> evaluated_offsets, GMutableSpan dst); -void calculate_basis(const float parameter, float r_weights[4]); +void calculate_basis(const float parameter, float4 &r_weights); /** * Interpolate the control point values for the given parameter on the piecewise segment. @@ -720,22 +720,15 @@ void calculate_basis(const float parameter, float r_weights[4]); template<typename T> T interpolate(const T &a, const T &b, const T &c, const T &d, const float parameter) { - float n[4]; + BLI_assert(0.0f <= parameter && parameter <= 1.0f); + float4 n; calculate_basis(parameter, n); - /* TODO: Use DefaultMixer or other generic mixing in the basis evaluation function to simplify - * supporting more types. */ - if constexpr (!is_same_any_v<T, float, float2, float3, float4, int8_t, int, int64_t>) { - T return_value; - attribute_math::DefaultMixer<T> mixer({&return_value, 1}); - mixer.mix_in(0, a, n[0] * 0.5f); - mixer.mix_in(0, b, n[1] * 0.5f); - mixer.mix_in(0, c, n[2] * 0.5f); - mixer.mix_in(0, d, n[3] * 0.5f); - mixer.finalize(); - return return_value; + if constexpr (is_same_any_v<T, float, float2, float3>) { + /* Save multiplications by adjusting weights after mix. */ + return 0.5f * attribute_math::mix4<T>(n, a, b, c, d); } else { - return 0.5f * (a * n[0] + b * n[1] + c * n[2] + d * n[3]); + return attribute_math::mix4<T>(n * 0.5f, a, b, c, d); } } diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh index 7d81847f4c1..f9155023db7 100644 --- a/source/blender/blenkernel/BKE_curves_utils.hh +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -326,8 +326,8 @@ void copy_point_data(const CurvesGeometry &src_curves, template<typename T> void copy_point_data(const CurvesGeometry &src_curves, const CurvesGeometry &dst_curves, - const IndexMask src_curve_selection, - const Span<T> src, + IndexMask src_curve_selection, + Span<T> src, MutableSpan<T> dst) { copy_point_data(src_curves, dst_curves, src_curve_selection, GSpan(src), GMutableSpan(dst)); @@ -340,13 +340,27 @@ void fill_points(const CurvesGeometry &curves, template<typename T> void fill_points(const CurvesGeometry &curves, - const IndexMask curve_selection, + IndexMask curve_selection, const T &value, MutableSpan<T> dst) { fill_points(curves, curve_selection, &value, dst); } +void fill_points(const CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + GPointer value, + GMutableSpan dst); + +template<typename T> +void fill_points(const CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + const T &value, + MutableSpan<T> dst) +{ + fill_points(curves, curve_ranges, &value, dst); +} + /** * Copy only the information on the point domain, but not the offsets or any point attributes, * meant for operations that change the number of points but not the number of curves. diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 5e313443dac..0e172abd9a2 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -137,7 +137,7 @@ void CustomData_data_add(int type, void *data1, const void *data2); /** * Initializes a CustomData object with the same layer setup as source. - * mask is a bitfield where `(mask & (1 << (layer type)))` indicates + * mask is a bit-field where `(mask & (1 << (layer type)))` indicates * if a layer should be copied or not. alloctype must be one of the above. */ void CustomData_copy(const struct CustomData *source, @@ -635,8 +635,7 @@ enum { CD_SHAPEKEY, /* Not available as real CD layer in non-bmesh context. */ /* Edges. */ - CD_FAKE_SEAM = CD_FAKE | 100, /* UV seam flag for edges. */ - CD_FAKE_CREASE = CD_FAKE | CD_CREASE, /* *sigh*. */ + CD_FAKE_SEAM = CD_FAKE | 100, /* UV seam flag for edges. */ /* Multiple types of mesh elements... */ CD_FAKE_UV = CD_FAKE | @@ -741,6 +740,8 @@ void CustomData_blend_write(BlendWriter *writer, void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); +size_t CustomData_get_elem_size(struct CustomDataLayer *layer); + #ifndef NDEBUG struct DynStr; /** Use to inspect mesh data when debugging. */ diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index c5788c07336..c11e6353bc0 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -106,7 +106,7 @@ typedef enum eFMI_Action_Types { /* Flags for the requirements of a FModifier Type */ typedef enum eFMI_Requirement_Flags { - /* modifier requires original data-points (kindof beats the purpose of a modifier stack?) */ + /* modifier requires original data-points (kind of beats the purpose of a modifier stack?) */ FMI_REQUIRES_ORIGINAL_DATA = (1 << 0), /* modifier doesn't require on any preceding data (i.e. it will generate a curve). * Use in conjunction with FMI_TYPE_GENRATE_CURVE diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index a5c4d5e1365..2ef9556afc7 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -26,7 +26,6 @@ struct Curves; struct Collection; struct Curve; -struct CurveEval; struct Mesh; struct Object; struct PointCloud; @@ -465,46 +464,7 @@ class PointCloudComponent : public GeometryComponent { }; /** - * Legacy runtime-only curves type. - * These curves are stored differently than other geometry components, because the data structure - * used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored - * here instead, though the component does give access to a #Curve for interfacing with render - * engines and other areas of Blender that expect to use a data-block with an #ID. - */ -class CurveComponentLegacy : public GeometryComponent { - private: - CurveEval *curve_ = nullptr; - GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; - - public: - CurveComponentLegacy(); - ~CurveComponentLegacy(); - GeometryComponent *copy() const override; - - void clear(); - bool has_curve() const; - /** - * Clear the component and replace it with the new curve. - */ - void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); - CurveEval *release(); - - const CurveEval *get_for_read() const; - CurveEval *get_for_write(); - - bool is_empty() const final; - - bool owns_direct_data() const override; - void ensure_owns_direct_data() override; - - std::optional<blender::bke::AttributeAccessor> attributes() const final; - std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; - - static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; -}; - -/** - * A geometry component that stores a group of curves, corresponding the #Curves data-block type + * A geometry component that stores a group of curves, corresponding the #Curves data-block * and the #CurvesGeometry type. Attributes are stored on the control point domain and the * curve domain. */ @@ -514,10 +474,9 @@ class CurveComponent : public GeometryComponent { GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; /** - * Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws. - * This is necessary because Blender assumes that objects evaluate to an object data type, and - * we use #CurveEval rather than #Curve here. It also allows us to mostly reuse the same - * batch cache implementation. + * Because rendering #Curves isn't fully working yet, we must provide a #Curve for the render + * engine and depsgraph object iterator in some cases. This allows using the old curve rendering + * even when the new curve data structure is used. */ mutable Curve *curve_for_render_ = nullptr; mutable std::mutex curve_for_render_mutex_; diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 96b6f7a53b0..e28c87cd7d6 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -80,7 +80,7 @@ typedef struct Global { * * -1: Disable faster motion paths computation (since 08/2018). * * 1 - 30: EEVEE debug/stats values (01/2018). * * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021). - * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). + * * 101: Enable UI debug drawing of full-screen area's corner widget (10/2014). * * 102: Enable extra items in string search UI (05/2022). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index e9b075aeb49..9ac4b4e4619 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -328,7 +328,7 @@ typedef enum eIDPropertyUIDataType { IDP_UI_DATA_TYPE_UNSUPPORTED = -1, /** IDP_INT or IDP_ARRAY with subtype IDP_INT. */ IDP_UI_DATA_TYPE_INT = 0, - /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double subtypes. */ + /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double sub-types. */ IDP_UI_DATA_TYPE_FLOAT = 1, /** IDP_STRING properties. */ IDP_UI_DATA_TYPE_STRING = 2, diff --git a/source/blender/blenkernel/BKE_idprop.hh b/source/blender/blenkernel/BKE_idprop.hh index 6a42ab1669f..e8ac4119970 100644 --- a/source/blender/blenkernel/BKE_idprop.hh +++ b/source/blender/blenkernel/BKE_idprop.hh @@ -49,7 +49,7 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, ID *id); /** - * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_INT. + * \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_INT. * * \param values: The values will be copied into the IDProperty. */ @@ -57,14 +57,14 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, Span<int32_t> values); /** - * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_FLOAT. + * \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_FLOAT. * * \param values: The values will be copied into the IDProperty. */ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, Span<float> values); /** - * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_DOUBLE. + * \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_DOUBLE. * * \param values: The values will be copied into the IDProperty. */ diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 45a72e8d7a3..0fe351c0aa4 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -22,7 +22,7 @@ extern "C" { #endif /** - * Free (or release) any data used by this shapekey (does not free the key itself). + * Free (or release) any data used by this shape-key (does not free the key itself). */ void BKE_key_free_data(struct Key *key); void BKE_key_free_nolib(struct Key *key); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index e5b013ce201..aa3bdb502f8 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -77,19 +77,19 @@ void BKE_libblock_runtime_reset_remapping_status(struct ID *id) ATTR_NONNULL(1); /* *** ID's session_uuid management. *** */ /** - * When an ID's uuid is of that value, it is unset/invalid (e.g. for runtime IDs, etc.). + * When an ID's UUID is of that value, it is unset/invalid (e.g. for runtime IDs, etc.). */ #define MAIN_ID_SESSION_UUID_UNSET 0 /** - * Generate a session-wise uuid for the given \a id. + * Generate a session-wise UUID for the given \a id. * * \note "session-wise" here means while editing a given .blend file. Once a new .blend file is - * loaded or created, undo history is cleared/reset, and so is the uuid counter. + * loaded or created, undo history is cleared/reset, and so is the UUID counter. */ void BKE_lib_libblock_session_uuid_ensure(struct ID *id); /** - * Re-generate a new session-wise uuid for the given \a id. + * Re-generate a new session-wise UUID for the given \a id. * * \warning This has a few very specific use-cases, no other usage is expected currently: * - To handle UI-related data-blocks that are kept across new file reading, when we do keep @@ -117,14 +117,14 @@ void *BKE_id_new_nomain(short type, const char *name); */ enum { /* *** Generic options (should be handled by all ID types copying, ID creation, etc.). *** */ - /** Create datablock outside of any main database - + /** Create data-block outside of any main database - * similar to 'localize' functions of materials etc. */ LIB_ID_CREATE_NO_MAIN = 1 << 0, - /** Do not affect user refcount of datablocks used by new one - * (which also gets zero usercount then). + /** Do not affect user refcount of data-blocks used by new one + * (which also gets zero user-count then). * Implies LIB_ID_CREATE_NO_MAIN. */ LIB_ID_CREATE_NO_USER_REFCOUNT = 1 << 1, - /** Assume given 'newid' already points to allocated memory for whole datablock + /** Assume given 'newid' already points to allocated memory for whole data-block * (ID + data) - USE WITH CAUTION! * Implies LIB_ID_CREATE_NO_MAIN. */ LIB_ID_CREATE_NO_ALLOCATE = 1 << 2, @@ -150,7 +150,7 @@ enum { LIB_ID_COPY_NO_PREVIEW = 1 << 17, /** Copy runtime data caches. */ LIB_ID_COPY_CACHES = 1 << 18, - /** Don't copy id->adt, used by ID data-block localization routines. */ + /** Don't copy `id->adt`, used by ID data-block localization routines. */ LIB_ID_COPY_NO_ANIMDATA = 1 << 19, /** Mesh: Reference CD data layers instead of doing real copy - USE WITH CAUTION! */ LIB_ID_COPY_CD_REFERENCE = 1 << 20, @@ -239,12 +239,12 @@ enum { /** Do not try to remove freed ID from given Main (passed Main may be NULL). */ LIB_ID_FREE_NO_MAIN = 1 << 0, /** - * Do not affect user refcount of datablocks used by freed one. + * Do not affect user refcount of data-blocks used by freed one. * Implies LIB_ID_FREE_NO_MAIN. */ LIB_ID_FREE_NO_USER_REFCOUNT = 1 << 1, /** - * Assume freed ID datablock memory is managed elsewhere, do not free it + * Assume freed ID data-block memory is managed elsewhere, do not free it * (still calls relevant ID type's freeing function though) - USE WITH CAUTION! * Implies LIB_ID_FREE_NO_MAIN. */ @@ -254,7 +254,7 @@ enum { LIB_ID_FREE_NO_DEG_TAG = 1 << 8, /** Do not attempt to remove freed ID from UI data/notifiers/... */ LIB_ID_FREE_NO_UI_USER = 1 << 9, - /** Do not remove freed ID's name from a potential runtime namemap. */ + /** Do not remove freed ID's name from a potential runtime name-map. */ LIB_ID_FREE_NO_NAMEMAP_REMOVE = 1 << 10, }; @@ -283,7 +283,7 @@ void BKE_libblock_free_data_py(struct ID *id); * \param idv: Pointer to ID to be freed. * \param flag: Set of \a LIB_ID_FREE_... flags controlling/overriding usual freeing process, * 0 to get default safe behavior. - * \param use_flag_from_idtag: Still use freeing info flags from given #ID datablock, + * \param use_flag_from_idtag: Still use freeing info flags from given #ID data-block, * even if some overriding ones are passed in \a flag parameter. */ void BKE_id_free_ex(struct Main *bmain, void *idv, int flag, bool use_flag_from_idtag); @@ -300,7 +300,7 @@ void BKE_id_free(struct Main *bmain, void *idv); /** * Not really a freeing function by itself, - * it decrements usercount of given id, and only frees it if it reaches 0. + * it decrements user-count of given id, and only frees it if it reaches 0. */ void BKE_id_free_us(struct Main *bmain, void *idv) ATTR_NONNULL(); @@ -316,12 +316,12 @@ void BKE_id_delete(struct Main *bmain, void *idv) ATTR_NONNULL(); * * \warning Considered experimental for now, seems to be working OK but this is * risky code in a complicated area. - * \return Number of deleted datablocks. + * \return Number of deleted data-blocks. */ size_t BKE_id_multi_tagged_delete(struct Main *bmain) ATTR_NONNULL(); /** - * Add a 'NO_MAIN' data-block to given main (also sets usercounts of its IDs if needed). + * Add a 'NO_MAIN' data-block to given main (also sets user-counts of its IDs if needed). */ void BKE_libblock_management_main_add(struct Main *bmain, void *idv); /** Remove a data-block from given main (set it to 'NO_MAIN' status). */ diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 37b626fb4da..a17ef8c7c5d 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -180,12 +180,12 @@ typedef enum IDRemapperApplyResult { typedef enum IDRemapperApplyOptions { /** - * Update the user count of the old and new ID datablock. + * Update the user count of the old and new ID data-block. * * For remapping the old ID users will be decremented and the new ID users will be * incremented. When un-assigning the old ID users will be decremented. * - * NOTE: Currently unused by main remapping code, since usercount is handled by + * NOTE: Currently unused by main remapping code, since user-count is handled by * `foreach_libblock_remap_callback_apply` there, depending on whether the remapped pointer does * use it or not. Need for rare cases in UI handling though (see e.g. `image_id_remap` in * `space_image.c`). @@ -193,14 +193,14 @@ typedef enum IDRemapperApplyOptions { ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0), /** - * Make sure that the new ID datablock will have a 'real' user. + * Make sure that the new ID data-block will have a 'real' user. * * NOTE: See Note for #ID_REMAP_APPLY_UPDATE_REFCOUNT above. */ ID_REMAP_APPLY_ENSURE_REAL = (1 << 1), /** - * Unassign in stead of remap when the new ID datablock would become id_self. + * Unassign in stead of remap when the new ID data-block would become id_self. * * To use this option 'BKE_id_remapper_apply_ex' must be used with a not-null id_self parameter. */ diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index b741489d929..0048ad4dde5 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -113,8 +113,10 @@ typedef struct Main { char filepath[1024]; /* 1024 = FILE_MAX */ short versionfile, subversionfile; /* see BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION */ short minversionfile, minsubversionfile; - uint64_t build_commit_timestamp; /* commit's timestamp from buildinfo */ - char build_hash[16]; /* hash from buildinfo */ + /** Commit timestamp from `buildinfo`. */ + uint64_t build_commit_timestamp; + /** Commit Hash from `buildinfo`. */ + char build_hash[16]; /** Indicate the #Main.filepath (file) is the recovered one. */ bool recovered; /** All current ID's exist in the last memfile undo step. */ diff --git a/source/blender/blenkernel/BKE_mball.h b/source/blender/blenkernel/BKE_mball.h index 7d265ceb102..5ffaa13d9d2 100644 --- a/source/blender/blenkernel/BKE_mball.h +++ b/source/blender/blenkernel/BKE_mball.h @@ -88,7 +88,7 @@ void BKE_mball_translate(struct MetaBall *mb, const float offset[3]); */ struct MetaElem *BKE_mball_element_add(struct MetaBall *mb, int type); -/* *** select funcs *** */ +/* *** Select functions *** */ int BKE_mball_select_count(const struct MetaBall *mb); int BKE_mball_select_count_multi(struct Base **bases, int bases_len); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index ef57c9a2e0e..de89abf9cf6 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -870,16 +870,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me); */ void BKE_mesh_flush_hidden_from_verts(struct Mesh *me); void BKE_mesh_flush_hidden_from_polys(struct Mesh *me); -/** - * simple poly -> vert/edge selection. - */ -void BKE_mesh_flush_select_from_polys_ex(struct MVert *mvert, - int totvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - const struct MPoly *mpoly, - int totpoly); + void BKE_mesh_flush_select_from_polys(struct Mesh *me); void BKE_mesh_flush_select_from_verts(struct Mesh *me); @@ -1148,7 +1139,11 @@ inline blender::MutableSpan<MLoop> Mesh::loops_for_write() inline blender::Span<MDeformVert> Mesh::deform_verts() const { - return {BKE_mesh_deform_verts(this), this->totvert}; + const MDeformVert *dverts = BKE_mesh_deform_verts(this); + if (!dverts) { + return {}; + } + return {dverts, this->totvert}; } inline blender::MutableSpan<MDeformVert> Mesh::deform_verts_for_write() { diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h index e67aec0b9ce..d3e582ff197 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h @@ -18,6 +18,24 @@ struct Mesh; struct MFace; /** + * Move face sets to the legacy type from a generic type. + */ +void BKE_mesh_legacy_face_set_from_generic(struct Mesh *mesh); +/** + * Copy face sets to the generic data type from the legacy type. + */ +void BKE_mesh_legacy_face_set_to_generic(struct Mesh *mesh); + +/** + * Copy edge creases from a separate layer into edges. + */ +void BKE_mesh_legacy_edge_crease_from_layers(struct Mesh *mesh); +/** + * Copy edge creases from edges to a separate layer. + */ +void BKE_mesh_legacy_edge_crease_to_layers(struct Mesh *mesh); + +/** * Copy bevel weights from separate layers into vertices and edges. */ void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh); @@ -37,6 +55,16 @@ void BKE_mesh_legacy_convert_hide_layers_to_flags(struct Mesh *mesh); void BKE_mesh_legacy_convert_flags_to_hide_layers(struct Mesh *mesh); /** + * Convert the selected element attributes to the old flag format for writing. + */ +void BKE_mesh_legacy_convert_selection_layers_to_flags(struct Mesh *mesh); +/** + * Convert the old selection flags (#SELECT/#ME_FACE_SEL) to the selected element attribute for + * reading. Only add the attributes when there are any elements in each domain selected. + */ +void BKE_mesh_legacy_convert_flags_to_selection_layers(struct Mesh *mesh); + +/** * Move material indices from a generic attribute to #MPoly. */ void BKE_mesh_legacy_convert_material_indices_to_mpoly(struct Mesh *mesh); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index cf9763d50a4..2ee50fbaaee 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -93,6 +93,7 @@ typedef struct MeshElemMap { /* mapping */ UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly, const bool *hide_poly, + const bool *select_poly, const struct MLoop *mloop, const struct MLoopUV *mloopuv, unsigned int totpoly, diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 2613f4286f0..9d3000759ab 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -7,7 +7,7 @@ * \ingroup bke */ -/* temp constant defined for these funcs only... */ +/** Temp constant defined for these functions only. */ #define NLASTRIP_MIN_LEN_THRESH 0.1f #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 55bf24f943e..14cf8164b79 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -477,7 +477,7 @@ struct bNodeTree *ntreeAddTreeEmbedded(struct Main *bmain, const char *name, const char *idname); -/* copy/free funcs, need to manage ID users */ +/* Copy/free functions, need to manage ID users. */ /** * Free (or release) any data used by this node-tree. @@ -960,8 +960,8 @@ void nodeLabel(const struct bNodeTree *ntree, const struct bNode *node, char *la */ const char *nodeSocketLabel(const struct bNodeSocket *sock); -bool nodeGroupPoll(struct bNodeTree *nodetree, - struct bNodeTree *grouptree, +bool nodeGroupPoll(const struct bNodeTree *nodetree, + const struct bNodeTree *grouptree, const char **r_disabled_hint); /** @@ -1480,7 +1480,7 @@ struct TexResult; #define GEO_NODE_ROTATE_INSTANCES 1122 #define GEO_NODE_SPLIT_EDGES 1123 #define GEO_NODE_MESH_TO_CURVE 1124 -#define GEO_NODE_TRANSFER_ATTRIBUTE 1125 +#define GEO_NODE_TRANSFER_ATTRIBUTE_DEPRECATED 1125 #define GEO_NODE_SUBDIVISION_SURFACE 1126 #define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127 #define GEO_NODE_RAYCAST 1128 @@ -1525,6 +1525,12 @@ struct TexResult; #define GEO_NODE_INPUT_SHORTEST_EDGE_PATHS 1168 #define GEO_NODE_EDGE_PATHS_TO_CURVES 1169 #define GEO_NODE_EDGE_PATHS_TO_SELECTION 1170 +#define GEO_NODE_MESH_FACE_SET_BOUNDARIES 1171 +#define GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME 1172 +#define GEO_NODE_SELF_OBJECT 1173 +#define GEO_NODE_SAMPLE_INDEX 1174 +#define GEO_NODE_SAMPLE_NEAREST 1175 +#define GEO_NODE_SAMPLE_NEAREST_SURFACE 1176 /** \} */ diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index 194820aa4ba..65c801a087b 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -81,7 +81,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { Vector<bNode *> toposort_left_to_right; Vector<bNode *> toposort_right_to_left; Vector<bNode *> group_nodes; - bool has_link_cycle = false; + bool has_available_link_cycle = false; bool has_undefined_nodes_or_sockets = false; bNode *group_output_node = nullptr; }; @@ -152,18 +152,17 @@ class bNodeRuntime : NonCopyable, NonMovable { Map<StringRefNull, bNodeSocket *> inputs_by_identifier; Map<StringRefNull, bNodeSocket *> outputs_by_identifier; int index_in_tree = -1; - bool has_linked_inputs = false; - bool has_linked_outputs = false; + bool has_available_linked_inputs = false; + bool has_available_linked_outputs = false; bNodeTree *owner_tree = nullptr; }; namespace node_tree_runtime { /** - * Is executed when the depsgraph determines that something in the node group changed that will - * affect the output. + * Is executed when the node tree changed in the depsgraph. */ -void handle_node_tree_output_changed(bNodeTree &tree_cow); +void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow); class AllowUsingOutdatedInfo : NonCopyable, NonMovable { private: @@ -270,10 +269,10 @@ inline blender::Span<bNode *> bNodeTree::group_nodes() return this->runtime->group_nodes; } -inline bool bNodeTree::has_link_cycle() const +inline bool bNodeTree::has_available_link_cycle() const { BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->has_link_cycle; + return this->runtime->has_available_link_cycle; } inline bool bNodeTree::has_undefined_nodes_or_sockets() const @@ -456,6 +455,11 @@ inline bool bNodeLink::is_muted() const return this->flag & NODE_LINK_MUTED; } +inline bool bNodeLink::is_available() const +{ + return this->fromsock->is_available() && this->tosock->is_available(); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 774765c3ca1..ed0969a6306 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -8,7 +8,9 @@ */ #include "BLI_bitmap.h" +#include "BLI_compiler_compat.h" #include "BLI_utildefines.h" + #include "DNA_brush_enums.h" #include "DNA_object_enums.h" @@ -415,7 +417,6 @@ typedef struct SculptBoundaryPreviewEdge { typedef struct SculptBoundary { /* Vertex indices of the active boundary. */ PBVHVertRef *verts; - int *verts_i; int verts_capacity; int verts_num; @@ -485,6 +486,74 @@ typedef struct SculptFakeNeighbors { /* Session data (mode-specific) */ +/* Custom Temporary Attributes */ + +typedef struct SculptAttributeParams { + /* Allocate a flat array outside the CustomData system. Cannot be combined with permanent. */ + int simple_array : 1; + + /* Do not mark CustomData layer as temporary. Cannot be combined with simple_array. Doesn't + * work with PBVH_GRIDS. + */ + int permanent : 1; /* Cannot be combined with simple_array. */ + int stroke_only : 1; /* Release layer at end of struct */ +} SculptAttributeParams; + +typedef struct SculptAttribute { + /* Domain, data type and name */ + eAttrDomain domain; + eCustomDataType proptype; + char name[MAX_CUSTOMDATA_LAYER_NAME]; + + /* Source layer on mesh/bmesh, if any. */ + struct CustomDataLayer *layer; + + /* Data stored as flat array. */ + void *data; + int elem_size, elem_num; + bool data_for_bmesh; /* Temporary data store as array outside of bmesh. */ + + /* Data stored per BMesh element. */ + int bmesh_cd_offset; + + /* Sculpt usage */ + SculptAttributeParams params; + + /* Used to keep track of which preallocated SculptAttribute instances + * inside of SculptSession.temp_attribute are used. + */ + bool used; +} SculptAttribute; + +#define SCULPT_MAX_ATTRIBUTES 64 + +/* Get a standard attribute name. Key must match up with a member + * of SculptAttributePointers. + */ + +#define SCULPT_ATTRIBUTE_NAME(key) \ + (offsetof(SculptAttributePointers, key) >= 0 ? /* Spellcheck name. */ \ + (".sculpt_" #key) /* Make name. */ \ + : \ + "You misspelled the layer name key") + +/* Convenience pointers for standard sculpt attributes. */ + +typedef struct SculptAttributePointers { + /* Persistent base. */ + SculptAttribute *persistent_co; + SculptAttribute *persistent_no; + SculptAttribute *persistent_disp; + + /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and + * initialized in #SCULPT_automasking_cache_init when needed. */ + SculptAttribute *automasking_factor; + + /* BMesh */ + SculptAttribute *dyntopo_node_id_vertex; + SculptAttribute *dyntopo_node_id_face; +} SculptAttributePointers; + typedef struct SculptSession { /* Mesh data (not copied) can come either directly from a Mesh, or from a MultiresDM */ struct { /* Special handling for multires meshes */ @@ -529,6 +598,7 @@ typedef struct SculptSession { /* Mesh Face Sets */ /* Total number of polys of the base mesh. */ int totfaces; + /* The 0 ID is not used by the tools or the visibility system, it is just used when creating new * geometry (the trim tool, for example) to detect which geometry was just added, so it can be * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set @@ -542,8 +612,6 @@ typedef struct SculptSession { /* BMesh for dynamic topology sculpting */ struct BMesh *bm; - int cd_vert_node_offset; - int cd_face_node_offset; bool bm_smooth_shading; /* Undo/redo log for dynamic topology sculpting */ struct BMLog *bm_log; @@ -575,7 +643,8 @@ typedef struct SculptSession { int active_face_index; int active_grid_index; - /* When active, the cursor draws with faded colors, indicating that there is an action enabled. + /* When active, the cursor draws with faded colors, indicating that there is an action + * enabled. */ bool draw_faded_cursor; float cursor_radius; @@ -584,8 +653,10 @@ typedef struct SculptSession { float cursor_sampled_normal[3]; float cursor_view_normal[3]; - /* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse when - * the gesture starts (intersection with the surface and if they ray hit the surface or not). */ + /* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse + * when + * the gesture starts (intersection with the surface and if they ray hit the surface or not). + */ float gesture_initial_location[3]; float gesture_initial_normal[3]; bool gesture_initial_hit; @@ -606,10 +677,6 @@ typedef struct SculptSession { /* Boundary Brush Preview */ SculptBoundary *boundary_preview; - /* Mesh State Persistence */ - /* This is freed with the PBVH, so it is always in sync with the mesh. */ - SculptPersistentBase *persistent_base; - SculptVertexInfo vertex_info; SculptFakeNeighbors fake_neighbors; @@ -655,6 +722,14 @@ typedef struct SculptSession { */ char needs_flush_to_id; + /* This is a fixed-size array so we can pass pointers to its elements + * to client code. This is important to keep bmesh offsets up to date. + */ + struct SculptAttribute temp_attributes[SCULPT_MAX_ATTRIBUTES]; + + /* Convenience #SculptAttribute pointers. */ + SculptAttributePointers attrs; + /** * Some tools follows the shading chosen by the last used tool canvas. * When not set the viewport shading color would be used. @@ -675,6 +750,75 @@ void BKE_sculptsession_free_deformMats(struct SculptSession *ss); void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss); void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder); void BKE_sculptsession_bm_to_me_for_render(struct Object *object); +int BKE_sculptsession_vertex_count(const SculptSession *ss); + +/* Ensure an attribute layer exists. */ +SculptAttribute *BKE_sculpt_attribute_ensure(struct Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + const SculptAttributeParams *params); + +/* Returns nullptr if attribute does not exist. */ +SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name); + +bool BKE_sculpt_attribute_exists(struct Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name); + +bool BKE_sculpt_attribute_destroy(struct Object *ob, SculptAttribute *attr); + +/* Destroy all attributes and pseudo-attributes created by sculpt mode. */ +void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob); + +/* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ +void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); + +BLI_INLINE void *BKE_sculpt_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr) +{ + if (attr->data) { + char *p = (char *)attr->data; + int idx = (int)vertex.i; + + if (attr->data_for_bmesh) { + BMElem *v = (BMElem *)vertex.i; + idx = v->head.index; + } + + return p + attr->elem_size * (int)idx; + } + else { + BMElem *v = (BMElem *)vertex.i; + return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); + } + + return NULL; +} + +BLI_INLINE void *BKE_sculpt_face_attr_get(const PBVHFaceRef vertex, const SculptAttribute *attr) +{ + if (attr->data) { + char *p = (char *)attr->data; + int idx = (int)vertex.i; + + if (attr->data_for_bmesh) { + BMElem *v = (BMElem *)vertex.i; + idx = v->head.index; + } + + return p + attr->elem_size * (int)idx; + } + else { + BMElem *v = (BMElem *)vertex.i; + return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); + } + + return NULL; +} /** * Create new color layer on object if it doesn't have one and if experimental feature set has @@ -713,6 +857,7 @@ struct PBVH *BKE_sculpt_object_pbvh_ensure(struct Depsgraph *depsgraph, struct O void BKE_sculpt_bvh_update_from_ccg(struct PBVH *pbvh, struct SubdivCCG *subdiv_ccg); +void BKE_sculpt_ensure_orig_mesh_data(struct Scene *scene, struct Object *object); void BKE_sculpt_sync_face_visibility_to_grids(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg); /** diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index d4badbf2dc1..9e0884a8c76 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -238,7 +238,7 @@ typedef void (*BKE_pbvh_SearchNearestCallback)(PBVHNode *node, void *data, float /* Building */ -PBVH *BKE_pbvh_new(void); +PBVH *BKE_pbvh_new(PBVHType type); /** * Do a full rebuild with on Mesh data structure. * @@ -276,6 +276,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); +void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); + void BKE_pbvh_build_pixels(PBVH *pbvh, struct Mesh *mesh, struct Image *image, diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 48402123365..efb8c2a9bb1 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -285,7 +285,7 @@ void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Scene *scene, int duplis); -/****************** Query funcs ****************************/ +/****************** Query functions ****************************/ /** * Check whether object has a point cache. diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 3691ab677b7..62b04785983 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -113,7 +113,7 @@ typedef struct SpaceType { /* read and write... */ - /* default keymaps to add */ + /** Default key-maps to add. */ int keymapflag; } SpaceType; diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh deleted file mode 100644 index 27542aa3586..00000000000 --- a/source/blender/blenkernel/BKE_spline.hh +++ /dev/null @@ -1,688 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** \file - * \ingroup bke - */ - -#include <mutex> - -#include "DNA_curves_types.h" - -#include "BLI_float4x4.hh" -#include "BLI_generic_virtual_array.hh" -#include "BLI_math_vec_types.hh" -#include "BLI_vector.hh" - -#include "BKE_attribute.hh" -#include "BKE_attribute_math.hh" - -struct Curve; -struct Curves; -struct ListBase; - -class Spline; -using SplinePtr = std::unique_ptr<Spline>; - -/** - * A spline is an abstraction of a single branch-less curve section, its evaluation methods, - * and data. The spline data itself is just control points and a set of attributes by the set - * of "evaluated" data is often used instead. Conceptually, the derived vs. original data is - * an essential distinction. Derived data is usually calculated lazily and cached on the spline. - * - * Any derived class of Spline has to manage two things: - * 1. Interpolating arbitrary attribute data from the control points to evaluated points. - * 2. Evaluating the positions based on the stored control point data. - * - * Beyond that, everything is the base class's responsibility, with minor exceptions. Further - * evaluation happens in a layer on top of the evaluated points generated by the derived types. - * - * There are a few methods to evaluate a spline: - * 1. #evaluated_positions and #interpolate_to_evaluated give data for the initial - * evaluated points, depending on the resolution. - * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups - * along the length of a curve. - * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples - * along the spline which can be used to interpolate data from method 1. - * - * Commonly used evaluated data is stored in caches on the spline itself so that operations on - * splines don't need to worry about taking ownership of evaluated data when they don't need to. - */ -class Spline { - public: - NormalMode normal_mode = NORMAL_MODE_MINIMUM_TWIST; - - blender::bke::CustomDataAttributes attributes; - - protected: - CurveType type_; - bool is_cyclic_ = false; - - /** Direction of the spline at each evaluated point. */ - mutable blender::Vector<blender::float3> evaluated_tangents_cache_; - mutable std::mutex tangent_cache_mutex_; - mutable bool tangent_cache_dirty_ = true; - - /** Normal direction vectors for each evaluated point. */ - mutable blender::Vector<blender::float3> evaluated_normals_cache_; - mutable std::mutex normal_cache_mutex_; - mutable bool normal_cache_dirty_ = true; - - /** Accumulated lengths along the evaluated points. */ - mutable blender::Vector<float> evaluated_lengths_cache_; - mutable std::mutex length_cache_mutex_; - mutable bool length_cache_dirty_ = true; - - public: - virtual ~Spline() = default; - Spline(const CurveType type) : type_(type) - { - } - Spline(Spline &other) : attributes(other.attributes), type_(other.type_) - { - copy_base_settings(other, *this); - } - - /** - * Return a new spline with the same data, settings, and attributes. - */ - SplinePtr copy() const; - /** - * Return a new spline with the same type and settings like "cyclic", but without any data. - */ - SplinePtr copy_only_settings() const; - /** - * The same as #copy, but skips copying dynamic attributes to the new spline. - */ - SplinePtr copy_without_attributes() const; - static void copy_base_settings(const Spline &src, Spline &dst); - - CurveType type() const; - - /** Return the number of control points. */ - virtual int size() const = 0; - int segments_num() const; - bool is_cyclic() const; - void set_cyclic(bool value); - - virtual void resize(int size) = 0; - virtual blender::MutableSpan<blender::float3> positions() = 0; - virtual blender::Span<blender::float3> positions() const = 0; - virtual blender::MutableSpan<float> radii() = 0; - virtual blender::Span<float> radii() const = 0; - virtual blender::MutableSpan<float> tilts() = 0; - virtual blender::Span<float> tilts() const = 0; - - virtual void translate(const blender::float3 &translation); - virtual void transform(const blender::float4x4 &matrix); - - /** - * Change the direction of the spline (switch the start and end) without changing its shape. - */ - void reverse(); - - /** - * Mark all caches for re-computation. This must be called after any operation that would - * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. - */ - virtual void mark_cache_invalid() = 0; - virtual int evaluated_points_num() const = 0; - int evaluated_edges_num() const; - - float length() const; - - virtual blender::Span<blender::float3> evaluated_positions() const = 0; - - /** - * Return non-owning access to the cache of accumulated lengths along the spline. Each item is - * the length of the subsequent segment, i.e. the first value is the length of the first segment - * rather than 0. This calculation is rather trivial, and only depends on the evaluated - * positions. However, the results are used often, and it is necessarily single threaded, so it - * is cached. - */ - blender::Span<float> evaluated_lengths() const; - /** - * Return non-owning access to the direction of the curve at each evaluated point. - */ - blender::Span<blender::float3> evaluated_tangents() const; - /** - * Return non-owning access to the direction vectors perpendicular to the tangents at every - * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode. - */ - blender::Span<blender::float3> evaluated_normals() const; - - void bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const; - - struct LookupResult { - /** - * The index of the evaluated point before the result location. In other words, the index of - * the edge that the result lies on. If the sampled factor/length is the very end of the - * spline, this will be the second to last index, if it's the very beginning, this will be 0. - */ - int evaluated_index; - /** - * The index of the evaluated point after the result location, accounting for wrapping when - * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will - * be the last index (#evaluated_points_num - 1). - */ - int next_evaluated_index; - /** - * The portion of the way from the evaluated point at #evaluated_index to the next point. - * If the sampled factor/length is the very end of the spline, this will be the 1.0f - */ - float factor; - }; - /** - * Find the position on the evaluated spline at the given portion of the total length. - * The return value is the indices of the two neighboring points at that location and the - * factor between them, which can be used to look up any attribute on the evaluated points. - * \note This does not support extrapolation. - */ - LookupResult lookup_evaluated_factor(float factor) const; - /** - * The same as #lookup_evaluated_factor, but looks up a length directly instead of - * a portion of the total. - */ - LookupResult lookup_evaluated_length(float length) const; - - /** - * Return an array of evenly spaced samples along the length of the spline. The samples are - * indices and factors to the next index encoded in floats. The logic for converting from the - * float values to interpolation data is in #lookup_data_from_index_factor. - */ - blender::Array<float> sample_uniform_index_factors(int samples_num) const; - LookupResult lookup_data_from_index_factor(float index_factor) const; - - /** - * Sample any input data with a value for each evaluated point (already interpolated to evaluated - * points) to arbitrary parameters in between the evaluated points. The interpolation is quite - * simple, but this handles the cyclic and end point special cases. - */ - void sample_with_index_factors(const blender::GVArray &src, - blender::Span<float> index_factors, - blender::GMutableSpan dst) const; - template<typename T> - void sample_with_index_factors(const blender::VArray<T> &src, - blender::Span<float> index_factors, - blender::MutableSpan<T> dst) const - { - this->sample_with_index_factors( - blender::GVArray(src), index_factors, blender::GMutableSpan(dst)); - } - template<typename T> - void sample_with_index_factors(blender::Span<T> src, - blender::Span<float> index_factors, - blender::MutableSpan<T> dst) const - { - this->sample_with_index_factors(blender::VArray<T>::ForSpan(src), index_factors, dst); - } - - /** - * Interpolate a virtual array of data with the size of the number of control points to the - * evaluated points. For poly splines, the lifetime of the returned virtual array must not - * exceed the lifetime of the input data. - */ - virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const = 0; - blender::GVArray interpolate_to_evaluated(blender::GSpan data) const; - template<typename T> blender::VArray<T> interpolate_to_evaluated(blender::Span<T> data) const - { - return this->interpolate_to_evaluated(blender::GSpan(data)).typed<T>(); - } - - protected: - virtual void correct_end_tangents() const = 0; - virtual void copy_settings(Spline &dst) const = 0; - virtual void copy_data(Spline &dst) const = 0; - virtual void reverse_impl() = 0; -}; - -/** - * A Bezier spline is made up of a many curve segments, possibly achieving continuity of curvature - * by constraining the alignment of curve handles. Evaluation stores the positions and a map of - * factors and indices in a list of floats, which is then used to interpolate any other data. - */ -class BezierSpline final : public Spline { - blender::Vector<blender::float3> positions_; - blender::Vector<float> radii_; - blender::Vector<float> tilts_; - int resolution_; - - blender::Vector<int8_t> handle_types_left_; - blender::Vector<int8_t> handle_types_right_; - - /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */ - mutable blender::Vector<blender::float3> handle_positions_left_; - mutable blender::Vector<blender::float3> handle_positions_right_; - - mutable std::mutex auto_handle_mutex_; - mutable bool auto_handles_dirty_ = true; - - /** Start index in evaluated points array for every control point. */ - mutable blender::Vector<int> offset_cache_; - mutable std::mutex offset_cache_mutex_; - mutable bool offset_cache_dirty_ = true; - - /** Cache of evaluated positions. */ - mutable blender::Vector<blender::float3> evaluated_position_cache_; - mutable std::mutex position_cache_mutex_; - mutable bool position_cache_dirty_ = true; - - /** Cache of "index factors" based calculated from the evaluated positions. */ - mutable blender::Vector<float> evaluated_mapping_cache_; - mutable std::mutex mapping_cache_mutex_; - mutable bool mapping_cache_dirty_ = true; - - public: - BezierSpline() : Spline(CURVE_TYPE_BEZIER) - { - } - BezierSpline(const BezierSpline &other) - : Spline((Spline &)other), - positions_(other.positions_), - radii_(other.radii_), - tilts_(other.tilts_), - resolution_(other.resolution_), - handle_types_left_(other.handle_types_left_), - handle_types_right_(other.handle_types_right_), - handle_positions_left_(other.handle_positions_left_), - handle_positions_right_(other.handle_positions_right_) - { - } - - int size() const final; - int resolution() const; - void set_resolution(int value); - - void resize(int size) final; - blender::MutableSpan<blender::float3> positions() final; - blender::Span<blender::float3> positions() const final; - blender::MutableSpan<float> radii() final; - blender::Span<float> radii() const final; - blender::MutableSpan<float> tilts() final; - blender::Span<float> tilts() const final; - blender::Span<int8_t> handle_types_left() const; - blender::MutableSpan<int8_t> handle_types_left(); - blender::Span<blender::float3> handle_positions_left() const; - /** - * Get writable access to the handle position. - * - * \param write_only: pass true for an uninitialized spline, this prevents accessing - * uninitialized memory while auto-generating handles. - */ - blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false); - blender::Span<int8_t> handle_types_right() const; - blender::MutableSpan<int8_t> handle_types_right(); - blender::Span<blender::float3> handle_positions_right() const; - /** - * Get writable access to the handle position. - * - * \param write_only: pass true for an uninitialized spline, this prevents accessing - * uninitialized memory while auto-generating handles. - */ - blender::MutableSpan<blender::float3> handle_positions_right(bool write_only = false); - /** - * Recalculate all #Auto and #Vector handles with positions automatically - * derived from the neighboring control points. - */ - void ensure_auto_handles() const; - - void translate(const blender::float3 &translation) override; - void transform(const blender::float4x4 &matrix) override; - - /** - * Set positions for the right handle of the control point, ensuring that - * aligned handles stay aligned. Has no effect for auto and vector type handles. - */ - void set_handle_position_right(int index, const blender::float3 &value); - /** - * Set positions for the left handle of the control point, ensuring that - * aligned handles stay aligned. Has no effect for auto and vector type handles. - */ - void set_handle_position_left(int index, const blender::float3 &value); - - bool point_is_sharp(int index) const; - - void mark_cache_invalid() final; - int evaluated_points_num() const final; - - /** - * Returns access to a cache of offsets into the evaluated point array for each control point. - * While most control point edges generate the number of edges specified by the resolution, - * vector segments only generate one edge. - * - * \note The length of the result is one greater than the number of points, so that the last item - * is the total number of evaluated points. This is useful to avoid recalculating the size of the - * last segment everywhere. - */ - blender::Span<int> control_point_offsets() const; - /** - * Returns non-owning access to an array of values containing the information necessary to - * interpolate values from the original control points to evaluated points. The control point - * index is the integer part of each value, and the factor used for interpolating to the next - * control point is the remaining fractional part. - */ - blender::Span<float> evaluated_mappings() const; - blender::Span<blender::float3> evaluated_positions() const final; - struct InterpolationData { - int control_point_index; - int next_control_point_index; - /** - * Linear interpolation weight between the two indices, from 0 to 1. - * Higher means closer to next control point. - */ - float factor; - }; - /** - * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary - * to interpolate data from control points to evaluated points between them. The next control - * point index result will not overflow the size of the control point vectors. - */ - InterpolationData interpolation_data_from_index_factor(float index_factor) const; - - virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const override; - - void evaluate_segment(int index, - int next_index, - blender::MutableSpan<blender::float3> positions) const; - /** - * \warning This functional assumes that the spline has more than one point. - */ - bool segment_is_vector(int start_index) const; - - /** See comment and diagram for #calculate_segment_insertion. */ - struct InsertResult { - blender::float3 handle_prev; - blender::float3 left_handle; - blender::float3 position; - blender::float3 right_handle; - blender::float3 handle_next; - }; - /** - * De Casteljau Bezier subdivision. - * \param index: The index of the segment's start control point. - * \param next_index: The index of the control point at the end of the segment. Could be 0, - * if the spline is cyclic. - * \param parameter: The factor along the segment, between 0 and 1. Note that this is used - * directly by the calculation, it doesn't correspond to a portion of the evaluated length. - * - * <pre> - * handle_prev handle_next - * x----------------x - * / \ - * / x---O---x \ - * / result \ - * / \ - * O O - * point_prev point_next - * </pre> - */ - InsertResult calculate_segment_insertion(int index, int next_index, float parameter); - - private: - /** - * If the spline is not cyclic, the direction for the first and last points is just the - * direction formed by the corresponding handles and control points. In the unlikely situation - * that the handles define a zero direction, fallback to using the direction defined by the - * first and last evaluated segments already calculated in #Spline::evaluated_tangents(). - */ - void correct_end_tangents() const final; - void copy_settings(Spline &dst) const final; - void copy_data(Spline &dst) const final; - - protected: - void reverse_impl() override; -}; - -/** - * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is - * influenced by a vector of knots, weights for each point, and the order of the spline. Every - * mapping of data to evaluated points is handled the same way, but the positions are cached in - * the spline. - */ -class NURBSpline final : public Spline { - public: - /** Method used to recalculate the knots vector when points are added or removed. */ - KnotsMode knots_mode; - - struct BasisCache { - /** - * For each evaluated point, the weight for all control points that influences it. - * The vector's size is the evaluated point count multiplied by the spline's order. - */ - blender::Vector<float> weights; - /** - * An offset for the start of #weights: the first control point index with a non-zero weight. - */ - blender::Vector<int> start_indices; - }; - - private: - blender::Vector<blender::float3> positions_; - blender::Vector<float> radii_; - blender::Vector<float> tilts_; - blender::Vector<float> weights_; - int resolution_; - /** - * Defines the number of nearby control points that influence a given evaluated point. Higher - * orders give smoother results. The number of control points must be greater than or equal to - * this value. - */ - uint8_t order_; - - /** - * Determines where and how the control points affect the evaluated points. The length should - * always be the value returned by #knots_num(), and each value should be greater than or equal - * to the previous. Only invalidated when a point is added or removed. - */ - mutable blender::Vector<float> knots_; - mutable std::mutex knots_mutex_; - mutable bool knots_dirty_ = true; - - /** Cache of control point influences on each evaluated point. */ - mutable BasisCache basis_cache_; - mutable std::mutex basis_cache_mutex_; - mutable bool basis_cache_dirty_ = true; - - /** - * Cache of position data calculated from the basis cache. Though it is interpolated - * in the same way as any other attribute, it is stored to save unnecessary recalculation. - */ - mutable blender::Vector<blender::float3> evaluated_position_cache_; - mutable std::mutex position_cache_mutex_; - mutable bool position_cache_dirty_ = true; - - public: - NURBSpline() : Spline(CURVE_TYPE_NURBS) - { - } - NURBSpline(const NURBSpline &other) - : Spline((Spline &)other), - knots_mode(other.knots_mode), - positions_(other.positions_), - radii_(other.radii_), - tilts_(other.tilts_), - weights_(other.weights_), - resolution_(other.resolution_), - order_(other.order_) - { - } - - int size() const final; - int resolution() const; - void set_resolution(int value); - uint8_t order() const; - void set_order(uint8_t value); - - bool check_valid_num_and_order() const; - int knots_num() const; - - void resize(int size) final; - blender::MutableSpan<blender::float3> positions() final; - blender::Span<blender::float3> positions() const final; - blender::MutableSpan<float> radii() final; - blender::Span<float> radii() const final; - blender::MutableSpan<float> tilts() final; - blender::Span<float> tilts() const final; - blender::Span<float> knots() const; - - blender::MutableSpan<float> weights(); - blender::Span<float> weights() const; - - void mark_cache_invalid() final; - int evaluated_points_num() const final; - - blender::Span<blender::float3> evaluated_positions() const final; - - blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final; - - protected: - void correct_end_tangents() const final; - void copy_settings(Spline &dst) const final; - void copy_data(Spline &dst) const final; - void reverse_impl() override; - - void calculate_knots() const; - const BasisCache &calculate_basis_cache() const; -}; - -/** - * A Poly spline is like a Bezier spline with a resolution of one. The main reason to distinguish - * the two is for reduced complexity and increased performance, since interpolating data to control - * points does not change it. - * - * Poly spline code is very simple, since it doesn't do anything that the base #Spline doesn't - * handle. Mostly it just worries about storing the data used by the base class. - */ -class PolySpline final : public Spline { - blender::Vector<blender::float3> positions_; - blender::Vector<float> radii_; - blender::Vector<float> tilts_; - - public: - PolySpline() : Spline(CURVE_TYPE_POLY) - { - } - PolySpline(const PolySpline &other) - : Spline((Spline &)other), - positions_(other.positions_), - radii_(other.radii_), - tilts_(other.tilts_) - { - } - - int size() const final; - - void resize(int size) final; - blender::MutableSpan<blender::float3> positions() final; - blender::Span<blender::float3> positions() const final; - blender::MutableSpan<float> radii() final; - blender::Span<float> radii() const final; - blender::MutableSpan<float> tilts() final; - blender::Span<float> tilts() const final; - - void mark_cache_invalid() final; - int evaluated_points_num() const final; - - blender::Span<blender::float3> evaluated_positions() const final; - - /** - * Poly spline interpolation from control points to evaluated points is a special case, since - * the result data is the same as the input data. This function returns a #GVArray that points to - * the original data. Therefore the lifetime of the returned virtual array must not be longer - * than the source data. - */ - blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final; - - protected: - void correct_end_tangents() const final; - void copy_settings(Spline &dst) const final; - void copy_data(Spline &dst) const final; - void reverse_impl() override; -}; - -/** - * A collection of #Spline objects with the same attribute types and names. Most data and - * functionality is in splines, but this contains some helpers for working with them as a group. - * - * \note A #CurveEval corresponds to the #Curve object data. The name is different for clarity, - * since more of the data is stored in the splines, but also just to be different than the name in - * DNA. - */ -struct CurveEval { - private: - blender::Vector<SplinePtr> splines_; - - public: - blender::bke::CustomDataAttributes attributes; - - CurveEval() = default; - CurveEval(const CurveEval &other) : attributes(other.attributes) - { - for (const SplinePtr &spline : other.splines()) { - this->add_spline(spline->copy()); - } - } - - blender::Span<SplinePtr> splines() const; - blender::MutableSpan<SplinePtr> splines(); - /** - * \return True if the curve contains a spline with the given type. - * - * \note If you are looping over all of the splines in the same scope anyway, - * it's better to avoid calling this function, in case there are many splines. - */ - bool has_spline_with_type(const CurveType type) const; - - void resize(int size); - /** - * \warning Call #reallocate on the spline's attributes after adding all splines. - */ - void add_spline(SplinePtr spline); - void add_splines(blender::MutableSpan<SplinePtr> splines); - void remove_splines(blender::IndexMask mask); - - void translate(const blender::float3 &translation); - void transform(const blender::float4x4 &matrix); - bool bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const; - - blender::bke::MutableAttributeAccessor attributes_for_write(); - - /** - * Return the start indices for each of the curve spline's control points, if they were part - * of a flattened array. This can be used to facilitate parallelism by avoiding the need to - * accumulate an offset while doing more complex calculations. - * - * \note The result is one longer than the spline count; the last element is the total size. - */ - blender::Array<int> control_point_offsets() const; - /** - * Exactly like #control_point_offsets, but uses the number of evaluated points instead. - */ - blender::Array<int> evaluated_point_offsets() const; - /** - * Return the accumulated length at the start of every spline in the curve. - * \note The result is one longer than the spline count; the last element is the total length. - */ - blender::Array<float> accumulated_spline_lengths() const; - - float total_length() const; - int total_control_point_num() const; - - void mark_cache_invalid(); - - /** - * Check the invariants that curve control point attributes should always uphold, necessary - * because attributes are stored on splines rather than in a flat array on the curve: - * - The same set of attributes exists on every spline. - * - Attributes with the same name have the same type on every spline. - * - Attributes are in the same order on every spline. - */ - void assert_valid_point_attributes() const; -}; - -std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve, - const ListBase &nurbs_list); -std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve); -std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves); -Curves *curve_eval_to_curves(const CurveEval &curve_eval); diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h index 486c9430279..5a6e8cbb64a 100644 --- a/source/blender/blenkernel/BKE_subdiv.h +++ b/source/blender/blenkernel/BKE_subdiv.h @@ -307,7 +307,6 @@ BLI_INLINE void BKE_subdiv_rotate_grid_to_quad( /* Convert Blender edge crease value to OpenSubdiv sharpness. */ BLI_INLINE float BKE_subdiv_crease_to_sharpness_f(float edge_crease); -BLI_INLINE float BKE_subdiv_crease_to_sharpness_char(char edge_crease); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 2f1e1897f8d..877407a644c 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -110,7 +110,6 @@ set(SRC intern/curve_convert.c intern/curve_decimate.c intern/curve_deform.c - intern/curve_eval.cc intern/curve_legacy_convert.cc intern/curve_nurbs.cc intern/curve_poly.cc @@ -137,7 +136,6 @@ set(SRC intern/fluid.c intern/fmodifier.c intern/freestyle.c - intern/geometry_component_curve.cc intern/geometry_component_curves.cc intern/geometry_component_edit_data.cc intern/geometry_component_instances.cc @@ -203,7 +201,7 @@ set(SRC intern/mesh_fair.cc intern/mesh_iterators.c intern/mesh_legacy_convert.cc - intern/mesh_mapping.c + intern/mesh_mapping.cc intern/mesh_merge.c intern/mesh_merge_customdata.cc intern/mesh_mirror.c @@ -266,10 +264,6 @@ set(SRC intern/softbody.c intern/sound.c intern/speaker.c - intern/spline_base.cc - intern/spline_bezier.cc - intern/spline_nurbs.cc - intern/spline_poly.cc intern/studiolight.c intern/subdiv.c intern/subdiv_ccg.c @@ -306,7 +300,7 @@ set(SRC intern/volume.cc intern/volume_render.cc intern/volume_to_mesh.cc - intern/workspace.c + intern/workspace.cc intern/world.c intern/writeavi.c @@ -469,7 +463,6 @@ set(SRC BKE_softbody.h BKE_sound.h BKE_speaker.h - BKE_spline.hh BKE_studiolight.h BKE_subdiv.h BKE_subdiv_ccg.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 9c05a2c4061..375e7b456cd 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -188,7 +188,7 @@ void DM_init_funcs(DerivedMesh *dm) dm->getLoopTriArray = dm_getLoopTriArray; - /* subtypes handle getting actual data */ + /* Sub-types handle getting actual data. */ dm->getNumLoopTri = dm_getNumLoopTri; dm->getVertDataArray = DM_get_vert_data_layer; @@ -240,8 +240,6 @@ void DM_from_template(DerivedMesh *dm, CustomData_copy(&source->loopData, &dm->loopData, mask->lmask, CD_SET_DEFAULT, numLoops); CustomData_copy(&source->polyData, &dm->polyData, mask->pmask, CD_SET_DEFAULT, numPolys); - dm->cd_flag = source->cd_flag; - dm->type = type; dm->numVertData = numVerts; dm->numEdgeData = numEdges; diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index e0ae1d88760..10aa4ec7906 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -113,7 +113,7 @@ static void action_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, BLI_addtail(&action_dst->curves, fcurve_dst); - /* Fix group links (kindof bad list-in-list search, but this is the most reliable way). */ + /* Fix group links (kind of bad list-in-list search, but this is the most reliable way). */ for (group_dst = action_dst->groups.first, group_src = action_src->groups.first; group_dst && group_src; group_dst = group_dst->next, group_src = group_src->next) { diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index b5b00e031b2..9b68c19c6e2 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -136,7 +136,7 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act) return false; } - /* Reduce usercount for current action. */ + /* Reduce user-count for current action. */ if (adt->action) { id_us_min((ID *)adt->action); } @@ -287,11 +287,11 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag) /* make a copy of action - at worst, user has to delete copies... */ if (do_action) { - /* Recursive copy of 'real' IDs is a bit hairy. Even if do not want to deal with usercount - * when copying ID's data itself, we still need to do so with sub-IDs, since those will not be - * handled by later 'update usercounts of used IDs' code as used e.g. at end of - * BKE_id_copy_ex(). - * So in case we do copy the ID and its sub-IDs in bmain, silence the 'no usercount' flag for + /* Recursive copy of 'real' IDs is a bit hairy. Even if do not want to deal with user-count + * when copying ID's data itself, we still need to do so with sub-IDs, since those will not be + * handled by later 'update user-counts of used IDs' code as used e.g. at end of + * #BKE_id_copy_ex(). + * So in case we do copy the ID and its sub-IDs in bmain, silence the 'no user-count' flag for * the sub-IDs copying. * NOTE: This is a bit weak, as usually when it comes to recursive ID copy. Should work for * now, but we may have to revisit this at some point and add a proper extra flag to deal with @@ -659,6 +659,8 @@ void BKE_animdata_transfer_by_basepath(Main *bmain, ID *srcID, ID *dstID, ListBa srcAdt, dstAdt, basepath_change->src_basepath, basepath_change->dst_basepath); } } + /* Tag source action because list of fcurves changed. */ + DEG_id_tag_update(&srcAdt->action->id, ID_RECALC_COPY_ON_WRITE); } /* Path Validation -------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 96ac81fdb63..e3c42c8bb78 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -888,7 +888,7 @@ void BKE_appdir_program_path_init(const char *argv0) const char *BKE_appdir_program_path(void) { -#ifndef WITH_PYTHON_MODULE /* Default's to empty when building as as Python module. */ +#ifndef WITH_PYTHON_MODULE /* Default's to empty when building as a Python module. */ BLI_assert(g_app.program_filepath[0]); #endif return g_app.program_filepath; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 0027f6dd707..9b00d427320 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -97,7 +97,7 @@ static void armature_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src Bone *bone_src, *bone_dst; Bone *bone_dst_act = NULL; - /* We never handle usercount here for own data. */ + /* We never handle user-count here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; armature_dst->bonehash = NULL; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 1e237da8119..b86353bdb74 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -19,6 +19,8 @@ #include "BLI_math_vec_types.hh" #include "BLI_span.hh" +#include "FN_field.hh" + #include "BLT_translation.h" #include "CLG_log.h" @@ -56,7 +58,8 @@ const char *no_procedural_access_message = bool allow_procedural_attribute_access(StringRef attribute_name) { - return !attribute_name.startswith(".selection") && !attribute_name.startswith(".hide"); + return !attribute_name.startswith(".sculpt") && !attribute_name.startswith(".select") && + !attribute_name.startswith(".hide"); } static int attribute_data_type_complexity(const eCustomDataType data_type) @@ -944,6 +947,15 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span return {}; } +fn::GField AttributeValidator::validate_field_if_necessary(const fn::GField &field) const +{ + if (function) { + auto validate_op = fn::FieldOperation::Create(*function, {field}); + return fn::GField(validate_op); + } + return field; +} + Vector<AttributeTransferData> retrieve_attributes_for_transfer( const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes, diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 8050f45da94..5fbca283399 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -53,6 +53,7 @@ class BuiltinAttributeProvider { const CreatableEnum createable_; const WritableEnum writable_; const DeletableEnum deletable_; + const AttributeValidator validator_; public: BuiltinAttributeProvider(std::string name, @@ -60,13 +61,15 @@ class BuiltinAttributeProvider { const eCustomDataType data_type, const CreatableEnum createable, const WritableEnum writable, - const DeletableEnum deletable) + const DeletableEnum deletable, + AttributeValidator validator = {}) : name_(std::move(name)), domain_(domain), data_type_(data_type), createable_(createable), writable_(writable), - deletable_(deletable) + deletable_(deletable), + validator_(validator) { } @@ -90,6 +93,11 @@ class BuiltinAttributeProvider { { return data_type_; } + + AttributeValidator validator() const + { + return validator_; + } }; /** @@ -241,9 +249,15 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const CustomDataAccessInfo custom_data_access, const AsReadAttribute as_read_attribute, const AsWriteAttribute as_write_attribute, - const UpdateOnChange update_on_write) - : BuiltinAttributeProvider( - std::move(attribute_name), domain, attribute_type, creatable, writable, deletable), + const UpdateOnChange update_on_write, + const AttributeValidator validator = {}) + : BuiltinAttributeProvider(std::move(attribute_name), + domain, + attribute_type, + creatable, + writable, + deletable, + validator), stored_type_(stored_type), custom_data_access_(custom_data_access), as_read_attribute_(as_read_attribute), @@ -379,6 +393,21 @@ inline bool for_all(const void *owner, } template<const ComponentAttributeProviders &providers> +inline AttributeValidator lookup_validator(const void * /*owner*/, + const blender::bke::AttributeIDRef &attribute_id) +{ + if (!attribute_id.is_named()) { + return {}; + } + const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (!provider) { + return {}; + } + return provider->validator(); +} + +template<const ComponentAttributeProviders &providers> inline bool contains(const void *owner, const blender::bke::AttributeIDRef &attribute_id) { bool found = false; @@ -489,6 +518,7 @@ inline AttributeAccessorFunctions accessor_functions_for_providers() lookup<providers>, nullptr, for_all<providers>, + lookup_validator<providers>, lookup_for_write<providers>, remove<providers>, add<providers>}; diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index c206a04fecc..a998fc0a75f 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -982,7 +982,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) case GP_BRUSH_PRESET_FILL_AREA: { brush->size = 5.0f; - brush->gpencil_settings->fill_leak = 3; brush->gpencil_settings->fill_threshold = 0.1f; brush->gpencil_settings->fill_simplylvl = 1; brush->gpencil_settings->fill_factor = 1.0f; diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 158e0bb776c..c3384239cb6 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -72,7 +72,7 @@ static void camera_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, Camera *cam_dst = (Camera *)id_dst; const Camera *cam_src = (const Camera *)id_src; - /* We never handle usercount here for own data. */ + /* We never handle user-count here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; BLI_listbase_clear(&cam_dst->bg_images); @@ -350,7 +350,7 @@ void BKE_camera_params_from_view3d(CameraParams *params, /* orthographic view */ float sensor_size = BKE_camera_sensor_size( params->sensor_fit, params->sensor_x, params->sensor_y); - /* Halve, otherwise too extreme low zbuffer quality. */ + /* Halve, otherwise too extreme low Z-buffer quality. */ params->clip_end *= 0.5f; params->clip_start = -params->clip_end; @@ -401,7 +401,7 @@ void BKE_camera_params_compute_viewplane( pixsize *= params->zoom; /* compute view plane: - * fully centered, zbuffer fills in jittered between -.5 and +.5 */ + * Fully centered, Z-buffer fills in jittered between `-.5` and `+.5`. */ viewplane.xmin = -0.5f * (float)winx; viewplane.ymin = -0.5f * params->ycor * (float)winy; viewplane.xmax = 0.5f * (float)winx; diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 0261b2d7674..bcdd01aa8b3 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -206,7 +206,6 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, * but only if the original mesh had its deformed_only flag correctly set * (which isn't generally the case). */ dm->deformedOnly = 1; - dm->cd_flag = mesh->cd_flag; CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, alloctype, mesh->totvert); CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, alloctype, mesh->totedge); diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index bcc3db48947..cd381e15635 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1283,9 +1283,8 @@ static void trackto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar cob->matrix[2][1] = 0; cob->matrix[2][2] = size[2]; - /* targetmat[2] instead of ownermat[2] is passed to vectomat - * for backwards compatibility it seems... (Aligorith) - */ + /* NOTE(@joshualung): `targetmat[2]` instead of `ownermat[2]` is passed to #vectomat + * for backwards compatibility it seems. */ sub_v3_v3v3(vec, cob->matrix[3], ct->matrix[3]); vectomat( vec, ct->matrix[2], (short)data->reserved1, (short)data->reserved2, data->flags, totmat); @@ -5932,12 +5931,12 @@ static void constraint_copy_data_ex(bConstraint *dst, cti->copy_data(dst, src); } - /* Fix usercounts for all referenced data that need it. */ + /* Fix user-counts for all referenced data that need it. */ if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { con_invoke_id_looper(cti, dst, con_fix_copied_refs_cb, NULL); } - /* for proxies we don't want to make extern */ + /* For proxies we don't want to make external. */ if (do_extern) { /* go over used ID-links for this constraint to ensure that they are valid for proxies */ con_invoke_id_looper(cti, dst, con_extern_cb, NULL); diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index aebdb8cc690..ca390fae424 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -2247,7 +2247,7 @@ static void minimum_twist_between_two_points(BevPoint *current_point, BevPoint * static void make_bevel_list_3D_minimum_twist(BevList *bl) { - BevPoint *bevp2, *bevp1, *bevp0; /* standard for all make_bevel_list_3D_* funcs */ + BevPoint *bevp2, *bevp1, *bevp0; /* Standard for all make_bevel_list_3D_* functions. */ int nr; float q[4]; @@ -2358,7 +2358,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl) static void make_bevel_list_3D_tangent(BevList *bl) { - BevPoint *bevp2, *bevp1, *bevp0; /* standard for all make_bevel_list_3D_* funcs */ + BevPoint *bevp2, *bevp1, *bevp0; /* Standard for all make_bevel_list_3D_* functions. */ int nr; float bevp0_tan[3]; diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index dac88948036..b5f1a7cc450 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -17,7 +17,7 @@ int calculate_evaluated_num(const int points_num, const bool cyclic, const int r } /* Adapted from Cycles #catmull_rom_basis_eval function. */ -void calculate_basis(const float parameter, float r_weights[4]) +void calculate_basis(const float parameter, float4 &r_weights) { const float t = parameter; const float s = 1.0f - parameter; @@ -139,11 +139,7 @@ void interpolate_to_evaluated(const GSpan src, { attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); - /* TODO: Use DefaultMixer or other generic mixing in the basis evaluation function to simplify - * supporting more types. */ - if constexpr (is_same_any_v<T, float, float2, float3, float4, int8_t, int, int64_t>) { - interpolate_to_evaluated(src.typed<T>(), cyclic, resolution, dst.typed<T>()); - } + interpolate_to_evaluated(src.typed<T>(), cyclic, resolution, dst.typed<T>()); }); } @@ -154,11 +150,7 @@ void interpolate_to_evaluated(const GSpan src, { attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); - /* TODO: Use DefaultMixer or other generic mixing in the basis evaluation function to simplify - * supporting more types. */ - if constexpr (is_same_any_v<T, float, float2, float3, float4, int8_t, int, int64_t>) { - interpolate_to_evaluated(src.typed<T>(), cyclic, evaluated_offsets, dst.typed<T>()); - } + interpolate_to_evaluated(src.typed<T>(), cyclic, evaluated_offsets, dst.typed<T>()); }); } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc deleted file mode 100644 index 3bee82fadab..00000000000 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ /dev/null @@ -1,587 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_array.hh" -#include "BLI_index_range.hh" -#include "BLI_listbase.h" -#include "BLI_map.hh" -#include "BLI_span.hh" -#include "BLI_string_ref.hh" -#include "BLI_task.hh" -#include "BLI_vector.hh" - -#include "DNA_curve_types.h" - -#include "BKE_anonymous_attribute.hh" -#include "BKE_curve.h" -#include "BKE_curves.hh" -#include "BKE_geometry_set.hh" -#include "BKE_spline.hh" - -using blender::Array; -using blender::float3; -using blender::float4x4; -using blender::GVArray; -using blender::GVArraySpan; -using blender::IndexRange; -using blender::Map; -using blender::MutableSpan; -using blender::Span; -using blender::StringRefNull; -using blender::VArray; -using blender::VArraySpan; -using blender::Vector; -using blender::bke::AttributeIDRef; -using blender::bke::AttributeMetaData; - -blender::Span<SplinePtr> CurveEval::splines() const -{ - return splines_; -} - -blender::MutableSpan<SplinePtr> CurveEval::splines() -{ - return splines_; -} - -bool CurveEval::has_spline_with_type(const CurveType type) const -{ - for (const SplinePtr &spline : this->splines()) { - if (spline->type() == type) { - return true; - } - } - return false; -} - -void CurveEval::resize(const int size) -{ - splines_.resize(size); - attributes.reallocate(size); -} - -void CurveEval::add_spline(SplinePtr spline) -{ - splines_.append(std::move(spline)); -} - -void CurveEval::add_splines(MutableSpan<SplinePtr> splines) -{ - for (SplinePtr &spline : splines) { - this->add_spline(std::move(spline)); - } -} - -void CurveEval::remove_splines(blender::IndexMask mask) -{ - for (int i = mask.size() - 1; i >= 0; i--) { - splines_.remove_and_reorder(mask.indices()[i]); - } -} - -void CurveEval::translate(const float3 &translation) -{ - for (SplinePtr &spline : this->splines()) { - spline->translate(translation); - spline->mark_cache_invalid(); - } -} - -void CurveEval::transform(const float4x4 &matrix) -{ - for (SplinePtr &spline : this->splines()) { - spline->transform(matrix); - } -} - -bool CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const -{ - bool have_minmax = false; - for (const SplinePtr &spline : this->splines()) { - if (spline->size()) { - spline->bounds_min_max(min, max, use_evaluated); - have_minmax = true; - } - } - - return have_minmax; -} - -float CurveEval::total_length() const -{ - float length = 0.0f; - for (const SplinePtr &spline : this->splines()) { - length += spline->length(); - } - return length; -} - -int CurveEval::total_control_point_num() const -{ - int count = 0; - for (const SplinePtr &spline : this->splines()) { - count += spline->size(); - } - return count; -} - -blender::Array<int> CurveEval::control_point_offsets() const -{ - Array<int> offsets(splines_.size() + 1); - int offset = 0; - for (const int i : splines_.index_range()) { - offsets[i] = offset; - offset += splines_[i]->size(); - } - offsets.last() = offset; - return offsets; -} - -blender::Array<int> CurveEval::evaluated_point_offsets() const -{ - Array<int> offsets(splines_.size() + 1); - int offset = 0; - for (const int i : splines_.index_range()) { - offsets[i] = offset; - offset += splines_[i]->evaluated_points_num(); - } - offsets.last() = offset; - return offsets; -} - -blender::Array<float> CurveEval::accumulated_spline_lengths() const -{ - Array<float> spline_lengths(splines_.size() + 1); - float spline_length = 0.0f; - for (const int i : splines_.index_range()) { - spline_lengths[i] = spline_length; - spline_length += splines_[i]->length(); - } - spline_lengths.last() = spline_length; - return spline_lengths; -} - -void CurveEval::mark_cache_invalid() -{ - for (SplinePtr &spline : splines_) { - spline->mark_cache_invalid(); - } -} - -static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) -{ - switch (dna_handle_type) { - case HD_FREE: - return BEZIER_HANDLE_FREE; - case HD_AUTO: - return BEZIER_HANDLE_AUTO; - case HD_VECT: - return BEZIER_HANDLE_VECTOR; - case HD_ALIGN: - return BEZIER_HANDLE_ALIGN; - case HD_AUTO_ANIM: - return BEZIER_HANDLE_AUTO; - case HD_ALIGN_DOUBLESIDE: - return BEZIER_HANDLE_ALIGN; - } - BLI_assert_unreachable(); - return BEZIER_HANDLE_AUTO; -} - -static NormalMode normal_mode_from_dna_curve(const int twist_mode) -{ - switch (twist_mode) { - case CU_TWIST_Z_UP: - case CU_TWIST_TANGENT: - return NORMAL_MODE_Z_UP; - case CU_TWIST_MINIMUM: - return NORMAL_MODE_MINIMUM_TWIST; - } - BLI_assert_unreachable(); - return NORMAL_MODE_MINIMUM_TWIST; -} - -static KnotsMode knots_mode_from_dna_nurb(const short flag) -{ - switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) { - case CU_NURB_ENDPOINT: - return NURBS_KNOT_MODE_ENDPOINT; - case CU_NURB_BEZIER: - return NURBS_KNOT_MODE_BEZIER; - case CU_NURB_ENDPOINT | CU_NURB_BEZIER: - return NURBS_KNOT_MODE_ENDPOINT_BEZIER; - default: - return NURBS_KNOT_MODE_NORMAL; - } - - BLI_assert_unreachable(); - return NURBS_KNOT_MODE_NORMAL; -} - -static SplinePtr spline_from_dna_bezier(const Nurb &nurb) -{ - std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); - spline->set_resolution(nurb.resolu); - spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); - - Span<const BezTriple> src_points{nurb.bezt, nurb.pntsu}; - spline->resize(src_points.size()); - MutableSpan<float3> positions = spline->positions(); - MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true); - MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true); - MutableSpan<int8_t> handle_types_left = spline->handle_types_left(); - MutableSpan<int8_t> handle_types_right = spline->handle_types_right(); - MutableSpan<float> radii = spline->radii(); - MutableSpan<float> tilts = spline->tilts(); - - blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { - for (const int i : range) { - const BezTriple &bezt = src_points[i]; - positions[i] = bezt.vec[1]; - handle_positions_left[i] = bezt.vec[0]; - handle_types_left[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1); - handle_positions_right[i] = bezt.vec[2]; - handle_types_right[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2); - radii[i] = bezt.radius; - tilts[i] = bezt.tilt; - } - }); - - return spline; -} - -static SplinePtr spline_from_dna_nurbs(const Nurb &nurb) -{ - std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); - spline->set_resolution(nurb.resolu); - spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); - spline->set_order(nurb.orderu); - spline->knots_mode = knots_mode_from_dna_nurb(nurb.flagu); - - Span<const BPoint> src_points{nurb.bp, nurb.pntsu}; - spline->resize(src_points.size()); - MutableSpan<float3> positions = spline->positions(); - MutableSpan<float> weights = spline->weights(); - MutableSpan<float> radii = spline->radii(); - MutableSpan<float> tilts = spline->tilts(); - - blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { - for (const int i : range) { - const BPoint &bp = src_points[i]; - positions[i] = bp.vec; - weights[i] = bp.vec[3]; - radii[i] = bp.radius; - tilts[i] = bp.tilt; - } - }); - - return spline; -} - -static SplinePtr spline_from_dna_poly(const Nurb &nurb) -{ - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); - - Span<const BPoint> src_points{nurb.bp, nurb.pntsu}; - spline->resize(src_points.size()); - MutableSpan<float3> positions = spline->positions(); - MutableSpan<float> radii = spline->radii(); - MutableSpan<float> tilts = spline->tilts(); - - blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { - for (const int i : range) { - const BPoint &bp = src_points[i]; - positions[i] = bp.vec; - radii[i] = bp.radius; - tilts[i] = bp.tilt; - } - }); - - return spline; -} - -std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve, - const ListBase &nurbs_list) -{ - Vector<const Nurb *> nurbs(nurbs_list); - - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - curve->resize(nurbs.size()); - MutableSpan<SplinePtr> splines = curve->splines(); - - blender::threading::parallel_for(nurbs.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - switch (nurbs[i]->type) { - case CU_BEZIER: - splines[i] = spline_from_dna_bezier(*nurbs[i]); - break; - case CU_NURBS: - splines[i] = spline_from_dna_nurbs(*nurbs[i]); - break; - case CU_POLY: - splines[i] = spline_from_dna_poly(*nurbs[i]); - break; - default: - BLI_assert_unreachable(); - break; - } - } - }); - - /* Normal mode is stored separately in each spline to facilitate combining - * splines from multiple curve objects, where the value may be different. */ - const NormalMode normal_mode = normal_mode_from_dna_curve(dna_curve.twist_mode); - for (SplinePtr &spline : curve->splines()) { - spline->normal_mode = normal_mode; - } - - return curve; -} - -std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) -{ - return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve)); -} - -static void copy_attributes_between_components( - const blender::bke::AttributeAccessor &src_attributes, - blender::bke::MutableAttributeAccessor &dst_attributes, - Span<std::string> skip) -{ - src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (id.is_named() && skip.contains(id.name())) { - return true; - } - - GVArray src_attribute = src_attributes.lookup(id, meta_data.domain, meta_data.data_type); - if (!src_attribute) { - return true; - } - GVArraySpan src_attribute_data{src_attribute}; - - blender::bke::GAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write( - id, meta_data.domain, meta_data.data_type); - if (!dst_attribute) { - return true; - } - dst_attribute.varray.set_all(src_attribute_data.data()); - dst_attribute.finish(); - return true; - }); -} - -std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) -{ - CurveComponent src_component; - src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly); - const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( - curves_id.geometry); - const blender::bke::AttributeAccessor src_attributes = curves.attributes(); - - VArray<int> resolution = curves.resolution(); - VArray<int8_t> normal_mode = curves.normal_mode(); - - VArraySpan<float> nurbs_weights{ - src_attributes.lookup_or_default<float>("nurbs_weight", ATTR_DOMAIN_POINT, 1.0f)}; - VArraySpan<int8_t> nurbs_orders{ - src_attributes.lookup_or_default<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; - VArraySpan<int8_t> nurbs_knots_modes{ - src_attributes.lookup_or_default<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; - - VArraySpan<int8_t> handle_types_right{ - src_attributes.lookup_or_default<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)}; - VArraySpan<int8_t> handle_types_left{ - src_attributes.lookup_or_default<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)}; - - /* Create splines with the correct size and type. */ - VArray<int8_t> curve_types = curves.curve_types(); - std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>(); - for (const int curve_index : curve_types.index_range()) { - const IndexRange points = curves.points_for_curve(curve_index); - - std::unique_ptr<Spline> spline; - /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */ - switch (std::max<int8_t>(1, curve_types[curve_index])) { - case CURVE_TYPE_POLY: { - spline = std::make_unique<PolySpline>(); - spline->resize(points.size()); - break; - } - case CURVE_TYPE_BEZIER: { - std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>(); - bezier_spline->resize(points.size()); - bezier_spline->set_resolution(resolution[curve_index]); - bezier_spline->handle_types_left().copy_from(handle_types_left.slice(points)); - bezier_spline->handle_types_right().copy_from(handle_types_right.slice(points)); - - spline = std::move(bezier_spline); - break; - } - case CURVE_TYPE_NURBS: { - std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>(); - nurb_spline->resize(points.size()); - nurb_spline->set_resolution(resolution[curve_index]); - nurb_spline->weights().copy_from(nurbs_weights.slice(points)); - nurb_spline->set_order(nurbs_orders[curve_index]); - nurb_spline->knots_mode = static_cast<KnotsMode>(nurbs_knots_modes[curve_index]); - - spline = std::move(nurb_spline); - break; - } - case CURVE_TYPE_CATMULL_ROM: - /* Not supported yet. */ - BLI_assert_unreachable(); - continue; - } - spline->positions().fill(float3(0)); - spline->tilts().fill(0.0f); - spline->radii().fill(1.0f); - spline->normal_mode = static_cast<NormalMode>(normal_mode[curve_index]); - curve_eval->add_spline(std::move(spline)); - } - - curve_eval->attributes.reallocate(curve_eval->splines().size()); - - CurveComponentLegacy dst_component; - dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable); - blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); - - copy_attributes_between_components(src_attributes, - dst_attributes, - {"curve_type", - "resolution", - "normal_mode", - "nurbs_weight", - "nurbs_order", - "knots_mode", - "handle_type_right", - "handle_type_left"}); - - return curve_eval; -} - -Curves *curve_eval_to_curves(const CurveEval &curve_eval) -{ - Curves *curves_id = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(), - curve_eval.splines().size()); - CurveComponent dst_component; - dst_component.replace(curves_id, GeometryOwnershipType::Editable); - blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); - - blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry); - curves.offsets_for_write().copy_from(curve_eval.control_point_offsets()); - MutableSpan<int8_t> curve_types = curves.curve_types_for_write(); - - blender::bke::SpanAttributeWriter<int8_t> normal_mode = - dst_attributes.lookup_or_add_for_write_only_span<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); - blender::bke::SpanAttributeWriter<float> nurbs_weight; - blender::bke::SpanAttributeWriter<int8_t> nurbs_order; - blender::bke::SpanAttributeWriter<int8_t> nurbs_knots_mode; - if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) { - nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span<float>("nurbs_weight", - ATTR_DOMAIN_POINT); - nurbs_order = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("nurbs_order", - ATTR_DOMAIN_CURVE); - nurbs_knots_mode = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("knots_mode", - ATTR_DOMAIN_CURVE); - } - blender::bke::SpanAttributeWriter<int8_t> handle_type_right; - blender::bke::SpanAttributeWriter<int8_t> handle_type_left; - if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) { - handle_type_right = dst_attributes.lookup_or_add_for_write_only_span<int8_t>( - "handle_type_right", ATTR_DOMAIN_POINT); - handle_type_left = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("handle_type_left", - ATTR_DOMAIN_POINT); - } - - for (const int curve_index : curve_eval.splines().index_range()) { - const Spline &spline = *curve_eval.splines()[curve_index]; - curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); - normal_mode.span[curve_index] = curve_eval.splines()[curve_index]->normal_mode; - const IndexRange points = curves.points_for_curve(curve_index); - - switch (spline.type()) { - case CURVE_TYPE_POLY: - break; - case CURVE_TYPE_BEZIER: { - const BezierSpline &src = static_cast<const BezierSpline &>(spline); - handle_type_right.span.slice(points).copy_from(src.handle_types_right()); - handle_type_left.span.slice(points).copy_from(src.handle_types_left()); - break; - } - case CURVE_TYPE_NURBS: { - const NURBSpline &src = static_cast<const NURBSpline &>(spline); - nurbs_knots_mode.span[curve_index] = static_cast<int8_t>(src.knots_mode); - nurbs_order.span[curve_index] = src.order(); - nurbs_weight.span.slice(points).copy_from(src.weights()); - break; - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - break; - } - } - } - - curves.update_curve_types(); - - normal_mode.finish(); - nurbs_weight.finish(); - nurbs_order.finish(); - nurbs_knots_mode.finish(); - handle_type_right.finish(); - handle_type_left.finish(); - - CurveComponentLegacy src_component; - src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly); - const blender::bke::AttributeAccessor src_attributes = *src_component.attributes(); - - copy_attributes_between_components(src_attributes, dst_attributes, {}); - - return curves_id; -} - -void CurveEval::assert_valid_point_attributes() const -{ -#ifdef DEBUG - if (splines_.size() == 0) { - return; - } - const int layer_len = splines_.first()->attributes.data.totlayer; - - Array<AttributeIDRef> ids_in_order(layer_len); - Array<AttributeMetaData> meta_data_in_order(layer_len); - - { - int i = 0; - splines_.first()->attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - ids_in_order[i] = attribute_id; - meta_data_in_order[i] = meta_data; - i++; - return true; - }, - ATTR_DOMAIN_POINT); - } - - for (const SplinePtr &spline : splines_) { - /* All splines should have the same number of attributes. */ - BLI_assert(spline->attributes.data.totlayer == layer_len); - - int i = 0; - spline->attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - /* Attribute names and IDs should have the same order and exist on all splines. */ - BLI_assert(attribute_id == ids_in_order[i]); - - /* Attributes with the same ID different splines should all have the same type. */ - BLI_assert(meta_data == meta_data_in_order[i]); - - i++; - return true; - }, - ATTR_DOMAIN_POINT); - } - -#endif -} diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 06789e34ad4..86bf3115c36 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1558,6 +1558,11 @@ GVArray CurvesGeometry::adapt_domain(const GVArray &varray, if (from == to) { return varray; } + if (varray.is_single()) { + BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value); + varray.get_internal_single(value); + return GVArray::ForSingle(varray.type(), this->attributes().domain_size(to), value); + } if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) { return adapt_curve_domain_point_to_curve(*this, varray); diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc index d98832e796c..f5a69a995a3 100644 --- a/source/blender/blenkernel/intern/curves_utils.cc +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -84,6 +84,21 @@ void fill_points(const CurvesGeometry &curves, }); } +void fill_points(const CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + GPointer value, + GMutableSpan dst) +{ + BLI_assert(*value.type() == dst.type()); + const CPPType &type = dst.type(); + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange points = curves.points_for_curves(range); + type.fill_assign_n(value.get(), dst.slice(points).data(), points.size()); + } + }); +} + bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves) { bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 64e2d00dc1b..51c3b405ebc 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1869,9 +1869,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 29: CD_BWEIGHT */ {sizeof(MFloatProperty), "MFloatProperty", 1, nullptr, nullptr, nullptr, layerInterp_bweight}, /* 30: CD_CREASE */ - /* NOTE: we do not interpolate crease data as it should be either inherited for subdivided - * edges, or for vertex creases, only present on the original vertex. */ - {sizeof(float), "", 0, N_("SubSurfCrease"), nullptr, nullptr, nullptr}, + {sizeof(float), "", 0, N_("SubSurfCrease"), nullptr, nullptr, layerInterp_propFloat}, /* 31: CD_ORIGSPACE_MLOOP */ {sizeof(OrigSpaceLoop), "OrigSpaceLoop", @@ -1967,7 +1965,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(short[4][3]), "", 0, nullptr, nullptr, nullptr, nullptr, layerSwap_flnor, nullptr}, /* 41: CD_CUSTOMLOOPNORMAL */ {sizeof(short[2]), "vec2s", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /* 42: CD_SCULPT_FACE_SETS */ + /* 42: CD_SCULPT_FACE_SETS */ /* DEPRECATED */ {sizeof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 43: CD_LOCATION */ {sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, @@ -2124,11 +2122,11 @@ const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = { const CustomData_MeshMasks CD_MASK_MESH = { /* vmask */ (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE | CD_MASK_BWEIGHT), - /* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_BWEIGHT), + /* emask */ + (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_BWEIGHT | CD_MASK_CREASE), /* fmask */ 0, /* pmask */ - (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | - CD_MASK_SCULPT_FACE_SETS), + (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL), /* lmask */ (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), @@ -2137,11 +2135,12 @@ const CustomData_MeshMasks CD_MASK_DERIVEDMESH = { /* vmask */ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL | CD_MASK_CREASE | CD_MASK_BWEIGHT), - /* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_BWEIGHT | CD_MASK_PROP_ALL), + /* emask */ + (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_BWEIGHT | CD_MASK_PROP_ALL | + CD_MASK_CREASE), /* fmask */ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT), /* pmask */ - (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | - CD_MASK_SCULPT_FACE_SETS), + (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL), /* lmask */ (CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_PROP_ALL), /* XXX MISSING CD_MASK_MLOOPTANGENT ? */ @@ -2152,7 +2151,7 @@ const CustomData_MeshMasks CD_MASK_BMESH = { /* emask */ (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), /* fmask */ 0, /* pmask */ - (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), + (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL), /* lmask */ (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), @@ -2171,7 +2170,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = { CD_MASK_PROP_ALL), /* pmask */ (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_FACEMAP | - CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), + CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL), /* lmask */ (CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_MLOOPTANGENT | CD_MASK_PREVIEW_MLOOPCOL | @@ -2373,7 +2372,14 @@ bool CustomData_merge(const CustomData *source, static bool attribute_stored_in_bmesh_flag(const StringRef name) { - return ELEM(name, ".hide_vert", ".hide_edge", ".hide_poly", "material_index"); + return ELEM(name, + ".hide_vert", + ".hide_edge", + ".hide_poly", + ".select_vert", + ".select_edge", + ".select_poly", + "material_index"); } static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src) @@ -5546,3 +5552,8 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) /** \} */ } // namespace blender::bke + +size_t CustomData_get_elem_size(CustomDataLayer *layer) +{ + return LAYERTYPEINFO[layer->type].size; +} diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 6c7715c625e..36f038e68ab 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -199,7 +199,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) case DT_TYPE_SEAM: return CD_FAKE_SEAM; case DT_TYPE_CREASE: - return CD_FAKE_CREASE; + return CD_CREASE; case DT_TYPE_BWEIGHT_EDGE: return CD_BWEIGHT; case DT_TYPE_FREESTYLE_EDGE: @@ -403,31 +403,6 @@ float data_transfer_interp_float_do(const int mix_mode, return interpf(val_ret, val_dst, mix_factor); } -static void data_transfer_interp_char(const CustomDataTransferLayerMap *laymap, - void *dest, - const void **sources, - const float *weights, - const int count, - const float mix_factor) -{ - const char **data_src = (const char **)sources; - char *data_dst = (char *)dest; - - const int mix_mode = laymap->mix_mode; - float val_src = 0.0f; - const float val_dst = (float)(*data_dst) / 255.0f; - - for (int i = count; i--;) { - val_src += ((float)(*data_src[i]) / 255.0f) * weights[i]; - } - - val_src = data_transfer_interp_float_do(mix_mode, val_dst, val_src, mix_factor); - - CLAMP(val_src, 0.0f, 1.0f); - - *data_dst = (char)(val_src * 255.0f); -} - /* Helpers to match sources and destinations data layers * (also handles 'conversions' in CD_FAKE cases). */ @@ -981,39 +956,6 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map, } return true; } - if (cddata_type == CD_FAKE_CREASE) { - const size_t elem_size = sizeof(*((MEdge *)NULL)); - const size_t data_size = sizeof(((MEdge *)NULL)->crease); - const size_t data_offset = offsetof(MEdge, crease); - const uint64_t data_flag = 0; - - if (!(me_src->cd_flag & ME_CDFLAG_EDGE_CREASE)) { - if (use_delete) { - me_dst->cd_flag &= ~ME_CDFLAG_EDGE_CREASE; - } - return true; - } - me_dst->cd_flag |= ME_CDFLAG_EDGE_CREASE; - if (r_map) { - data_transfer_layersmapping_add_item(r_map, - cddata_type, - mix_mode, - mix_factor, - mix_weights, - BKE_mesh_edges(me_src), - BKE_mesh_edges_for_write(me_dst), - me_src->totedge, - me_dst->totedge, - elem_size, - data_size, - data_offset, - data_flag, - data_transfer_interp_char, - interp_data); - } - return true; - } - if (r_map && ELEM(cddata_type, CD_FAKE_SHARP, CD_FAKE_SEAM)) { const size_t elem_size = sizeof(*((MEdge *)NULL)); const size_t data_size = sizeof(((MEdge *)NULL)->flag); diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index b87d675496c..279166297ec 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -634,7 +634,7 @@ void BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, /** * \return True if the deformed curve control point data should be implicitly - * converted directly to a mesh, or false if it can be left as curve data via #CurveEval. + * converted directly to a mesh, or false if it can be left as curve data via the #Curves type. */ static bool do_curve_implicit_mesh_conversion(const Curve *curve, ModifierData *first_modifier, diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 6e53254d162..349614b93dd 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -80,7 +80,7 @@ /** Max value for phi initialization */ #define PHI_MAX 9999.0f -static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock); +static void fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock); #ifdef WITH_FLUID // #define DEBUG_PRINT @@ -482,7 +482,7 @@ static void update_final_gravity(FluidDomainSettings *fds, Scene *scene) mul_v3_fl(fds->gravity_final, fds->effector_weights->global_gravity); } -static bool BKE_fluid_modifier_init( +static bool fluid_modifier_init( FluidModifierData *fmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me) { int scene_framenr = (int)DEG_get_ctime(depsgraph); @@ -998,7 +998,6 @@ static void obstacles_from_mesh(Object *coll_ob, float dt) { if (fes->mesh) { - Mesh *me = NULL; const MLoopTri *looptri; BVHTreeFromMesh tree_data = {NULL}; int numverts, i; @@ -1006,13 +1005,11 @@ static void obstacles_from_mesh(Object *coll_ob, float *vert_vel = NULL; bool has_velocity = false; - me = BKE_mesh_copy_for_eval(fes->mesh, true); + Mesh *me = BKE_mesh_copy_for_eval(fes->mesh, false); + MVert *verts = BKE_mesh_verts_for_write(me); int min[3], max[3], res[3]; - /* Duplicate vertices to modify. */ - MVert *verts = MEM_dupallocN(BKE_mesh_verts(me)); - const MLoop *mloop = BKE_mesh_loops(me); looptri = BKE_mesh_runtime_looptri_ensure(me); numverts = me->totvert; @@ -1097,7 +1094,6 @@ static void obstacles_from_mesh(Object *coll_ob, if (vert_vel) { MEM_freeN(vert_vel); } - MEM_SAFE_FREE(verts); BKE_id_free(NULL, me); } } @@ -2074,10 +2070,8 @@ static void emit_from_mesh( /* Copy mesh for thread safety as we modify it. * Main issue is its VertArray being modified, then replaced and freed. */ - Mesh *me = BKE_mesh_copy_for_eval(ffs->mesh, true); - - /* Duplicate vertices to modify. */ - MVert *verts = MEM_dupallocN(BKE_mesh_verts(me)); + Mesh *me = BKE_mesh_copy_for_eval(ffs->mesh, false); + MVert *verts = BKE_mesh_verts_for_write(me); const MLoop *mloop = BKE_mesh_loops(me); const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me); @@ -2102,7 +2096,8 @@ static void emit_from_mesh( /* Transform mesh vertices to domain grid space for fast lookups. * This is valid because the mesh is copied above. */ - float(*vert_normals)[3] = MEM_dupallocN(BKE_mesh_vertex_normals_ensure(me)); + BKE_mesh_vertex_normals_ensure(me); + float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(me); for (i = 0; i < numverts; i++) { /* Vertex position. */ mul_m4_v3(flow_ob->obmat, verts[i].co); @@ -2178,8 +2173,6 @@ static void emit_from_mesh( if (vert_vel) { MEM_freeN(vert_vel); } - MEM_SAFE_FREE(verts); - MEM_SAFE_FREE(vert_normals); BKE_id_free(NULL, me); } } @@ -3622,15 +3615,15 @@ static void manta_guiding( BLI_mutex_unlock(&object_update_lock); } -static void BKE_fluid_modifier_processFlow(FluidModifierData *fmd, - Depsgraph *depsgraph, - Scene *scene, - Object *ob, - Mesh *me, - const int scene_framenr) +static void fluid_modifier_processFlow(FluidModifierData *fmd, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + Mesh *me, + const int scene_framenr) { if (scene_framenr >= fmd->time) { - BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me); + fluid_modifier_init(fmd, depsgraph, ob, scene, me); } if (fmd->flow) { @@ -3645,19 +3638,19 @@ static void BKE_fluid_modifier_processFlow(FluidModifierData *fmd, } else if (scene_framenr < fmd->time) { fmd->time = scene_framenr; - BKE_fluid_modifier_reset_ex(fmd, false); + fluid_modifier_reset_ex(fmd, false); } } -static void BKE_fluid_modifier_processEffector(FluidModifierData *fmd, - Depsgraph *depsgraph, - Scene *scene, - Object *ob, - Mesh *me, - const int scene_framenr) +static void fluid_modifier_processEffector(FluidModifierData *fmd, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + Mesh *me, + const int scene_framenr) { if (scene_framenr >= fmd->time) { - BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me); + fluid_modifier_init(fmd, depsgraph, ob, scene, me); } if (fmd->effector) { @@ -3672,16 +3665,16 @@ static void BKE_fluid_modifier_processEffector(FluidModifierData *fmd, } else if (scene_framenr < fmd->time) { fmd->time = scene_framenr; - BKE_fluid_modifier_reset_ex(fmd, false); + fluid_modifier_reset_ex(fmd, false); } } -static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, - Depsgraph *depsgraph, - Scene *scene, - Object *ob, - Mesh *me, - const int scene_framenr) +static void fluid_modifier_processDomain(FluidModifierData *fmd, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + Mesh *me, + const int scene_framenr) { FluidDomainSettings *fds = fmd->domain; Object *guide_parent = NULL; @@ -3727,7 +3720,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Reset fluid if no fluid present. Also resets active fields. */ if (!fds->fluid) { - BKE_fluid_modifier_reset_ex(fmd, false); + fluid_modifier_reset_ex(fmd, false); } /* Ensure cache directory is not relative. */ @@ -3755,12 +3748,12 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, if (pid.cache->flag & PTCACHE_OUTDATED) { BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); BKE_fluid_cache_free_all(fds, ob); - BKE_fluid_modifier_reset_ex(fmd, false); + fluid_modifier_reset_ex(fmd, false); } } /* Fluid domain init must not fail in order to continue modifier evaluation. */ - if (!fds->fluid && !BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me)) { + if (!fds->fluid && !fluid_modifier_init(fmd, depsgraph, ob, scene, me)) { CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!"); return; } @@ -4088,19 +4081,19 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, fmd->time = scene_framenr; } -static void BKE_fluid_modifier_process( +static void fluid_modifier_process( FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me) { const int scene_framenr = (int)DEG_get_ctime(depsgraph); if (fmd->type & MOD_FLUID_TYPE_FLOW) { - BKE_fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr); + fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr); } else if (fmd->type & MOD_FLUID_TYPE_EFFEC) { - BKE_fluid_modifier_processEffector(fmd, depsgraph, scene, ob, me, scene_framenr); + fluid_modifier_processEffector(fmd, depsgraph, scene, ob, me, scene_framenr); } else if (fmd->type & MOD_FLUID_TYPE_DOMAIN) { - BKE_fluid_modifier_processDomain(fmd, depsgraph, scene, ob, me, scene_framenr); + fluid_modifier_processDomain(fmd, depsgraph, scene, ob, me, scene_framenr); } } @@ -4118,7 +4111,7 @@ struct Mesh *BKE_fluid_modifier_do( BLI_rw_mutex_lock(fmd->domain->fluid_mutex, THREAD_LOCK_WRITE); } - BKE_fluid_modifier_process(fmd, depsgraph, scene, ob, me); + fluid_modifier_process(fmd, depsgraph, scene, ob, me); if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { BLI_rw_mutex_unlock(fmd->domain->fluid_mutex); @@ -4693,7 +4686,7 @@ void BKE_fluid_fields_sanitize(FluidDomainSettings *settings) * Use for versioning, even when fluids are disabled. * \{ */ -static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd) +static void fluid_modifier_freeDomain(FluidModifierData *fmd) { if (fmd->domain) { if (fmd->domain->fluid) { @@ -4722,7 +4715,7 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd) } } -static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd) +static void fluid_modifier_freeFlow(FluidModifierData *fmd) { if (fmd->flow) { if (fmd->flow->mesh) { @@ -4739,7 +4732,7 @@ static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd) } } -static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd) +static void fluid_modifier_freeEffector(FluidModifierData *fmd) { if (fmd->effector) { if (fmd->effector->mesh) { @@ -4756,7 +4749,7 @@ static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd) } } -static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock) +static void fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock) { if (!fmd) { return; @@ -4796,7 +4789,7 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need void BKE_fluid_modifier_reset(struct FluidModifierData *fmd) { - BKE_fluid_modifier_reset_ex(fmd, true); + fluid_modifier_reset_ex(fmd, true); } void BKE_fluid_modifier_free(FluidModifierData *fmd) @@ -4805,9 +4798,9 @@ void BKE_fluid_modifier_free(FluidModifierData *fmd) return; } - BKE_fluid_modifier_freeDomain(fmd); - BKE_fluid_modifier_freeFlow(fmd); - BKE_fluid_modifier_freeEffector(fmd); + fluid_modifier_freeDomain(fmd); + fluid_modifier_freeFlow(fmd); + fluid_modifier_freeEffector(fmd); } void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd) @@ -4818,7 +4811,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd) if (fmd->type & MOD_FLUID_TYPE_DOMAIN) { if (fmd->domain) { - BKE_fluid_modifier_freeDomain(fmd); + fluid_modifier_freeDomain(fmd); } fmd->domain = DNA_struct_default_alloc(FluidDomainSettings); @@ -4850,7 +4843,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd) } else if (fmd->type & MOD_FLUID_TYPE_FLOW) { if (fmd->flow) { - BKE_fluid_modifier_freeFlow(fmd); + fluid_modifier_freeFlow(fmd); } fmd->flow = DNA_struct_default_alloc(FluidFlowSettings); @@ -4858,7 +4851,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd) } else if (fmd->type & MOD_FLUID_TYPE_EFFEC) { if (fmd->effector) { - BKE_fluid_modifier_freeEffector(fmd); + fluid_modifier_freeEffector(fmd); } fmd->effector = DNA_struct_default_alloc(FluidEffectorSettings); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc deleted file mode 100644 index 8e482e4c691..00000000000 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ /dev/null @@ -1,1464 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" - -#include "DNA_ID_enums.h" -#include "DNA_curve_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_curve.h" -#include "BKE_geometry_set.hh" -#include "BKE_lib_id.h" -#include "BKE_spline.hh" - -#include "attribute_access_intern.hh" - -using blender::GMutableSpan; -using blender::GSpan; -using blender::GVArray; -using blender::GVArraySpan; - -/* -------------------------------------------------------------------- */ -/** \name Geometry Component Implementation - * \{ */ - -CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) -{ -} - -CurveComponentLegacy::~CurveComponentLegacy() -{ - this->clear(); -} - -GeometryComponent *CurveComponentLegacy::copy() const -{ - CurveComponentLegacy *new_component = new CurveComponentLegacy(); - if (curve_ != nullptr) { - new_component->curve_ = new CurveEval(*curve_); - new_component->ownership_ = GeometryOwnershipType::Owned; - } - return new_component; -} - -void CurveComponentLegacy::clear() -{ - BLI_assert(this->is_mutable()); - if (curve_ != nullptr) { - if (ownership_ == GeometryOwnershipType::Owned) { - delete curve_; - } - curve_ = nullptr; - } -} - -bool CurveComponentLegacy::has_curve() const -{ - return curve_ != nullptr; -} - -void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership) -{ - BLI_assert(this->is_mutable()); - this->clear(); - curve_ = curve; - ownership_ = ownership; -} - -CurveEval *CurveComponentLegacy::release() -{ - BLI_assert(this->is_mutable()); - CurveEval *curve = curve_; - curve_ = nullptr; - return curve; -} - -const CurveEval *CurveComponentLegacy::get_for_read() const -{ - return curve_; -} - -CurveEval *CurveComponentLegacy::get_for_write() -{ - BLI_assert(this->is_mutable()); - if (ownership_ == GeometryOwnershipType::ReadOnly) { - curve_ = new CurveEval(*curve_); - ownership_ = GeometryOwnershipType::Owned; - } - return curve_; -} - -bool CurveComponentLegacy::is_empty() const -{ - return curve_ == nullptr; -} - -bool CurveComponentLegacy::owns_direct_data() const -{ - return ownership_ == GeometryOwnershipType::Owned; -} - -void CurveComponentLegacy::ensure_owns_direct_data() -{ - BLI_assert(this->is_mutable()); - if (ownership_ != GeometryOwnershipType::Owned) { - curve_ = new CurveEval(*curve_); - ownership_ = GeometryOwnershipType::Owned; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Attribute Access Helper Functions - * \{ */ - -namespace blender::bke { - -namespace { -struct PointIndices { - int spline_index; - int point_index; -}; -} // namespace -static PointIndices lookup_point_indices(Span<int> offsets, const int index) -{ - const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - - offsets.begin() - 1; - const int index_in_spline = index - offsets[spline_index]; - return {spline_index, index_in_spline}; -} - -/** - * Mix together all of a spline's control point values. - * - * \note Theoretically this interpolation does not need to compute all values at once. - * However, doing that makes the implementation simpler, and this can be optimized in the future if - * only some values are required. - */ -template<typename T> -static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, - const VArray<T> &old_values, - MutableSpan<T> r_values) -{ - const int splines_len = curve.splines().size(); - Array<int> offsets = curve.control_point_offsets(); - BLI_assert(r_values.size() == splines_len); - attribute_math::DefaultMixer<T> mixer(r_values); - - for (const int i_spline : IndexRange(splines_len)) { - const int spline_offset = offsets[i_spline]; - const int spline_point_len = offsets[i_spline + 1] - spline_offset; - for (const int i_point : IndexRange(spline_point_len)) { - const T value = old_values[spline_offset + i_point]; - mixer.mix_in(i_spline, value); - } - } - - mixer.finalize(); -} - -/** - * A spline is selected if all of its control points were selected. - * - * \note Theoretically this interpolation does not need to compute all values at once. - * However, doing that makes the implementation simpler, and this can be optimized in the future if - * only some values are required. - */ -template<> -void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, - const VArray<bool> &old_values, - MutableSpan<bool> r_values) -{ - const int splines_len = curve.splines().size(); - Array<int> offsets = curve.control_point_offsets(); - BLI_assert(r_values.size() == splines_len); - - r_values.fill(true); - - for (const int i_spline : IndexRange(splines_len)) { - const int spline_offset = offsets[i_spline]; - const int spline_point_len = offsets[i_spline + 1] - spline_offset; - - for (const int i_point : IndexRange(spline_point_len)) { - if (!old_values[spline_offset + i_point]) { - r_values[i_spline] = false; - break; - } - } - } -} - -static GVArray adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArray varray) -{ - GVArray new_varray; - attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { - using T = decltype(dummy); - if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { - Array<T> values(curve.splines().size()); - adapt_curve_domain_point_to_spline_impl<T>(curve, varray.typed<T>(), values); - new_varray = VArray<T>::ForContainer(std::move(values)); - } - }); - return new_varray; -} - -/** - * A virtual array implementation for the conversion of spline attributes to control point - * attributes. The goal is to avoid copying the spline value for every one of its control points - * unless it is necessary (in that case the materialize functions will be called). - */ -template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> { - GVArray original_varray_; - /* Store existing data materialized if it was not already a span. This is expected - * to be worth it because a single spline's value will likely be accessed many times. */ - VArraySpan<T> original_data_; - Array<int> offsets_; - - public: - VArray_For_SplineToPoint(GVArray original_varray, Array<int> offsets) - : VArrayImpl<T>(offsets.last()), - original_varray_(std::move(original_varray)), - original_data_(original_varray_.typed<T>()), - offsets_(std::move(offsets)) - { - } - - T get(const int64_t index) const final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - return original_data_[indices.spline_index]; - } - - void materialize(const IndexMask mask, MutableSpan<T> r_span) const final - { - const int total_num = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { - for (const int spline_index : original_data_.index_range()) { - const int offset = offsets_[spline_index]; - const int next_offset = offsets_[spline_index + 1]; - r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]); - } - } - else { - int spline_index = 0; - for (const int dst_index : mask) { - while (offsets_[spline_index] < dst_index) { - spline_index++; - } - r_span[dst_index] = original_data_[spline_index]; - } - } - } - - void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final - { - T *dst = r_span.data(); - const int total_num = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { - for (const int spline_index : original_data_.index_range()) { - const int offset = offsets_[spline_index]; - const int next_offset = offsets_[spline_index + 1]; - uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]); - } - } - else { - int spline_index = 0; - for (const int dst_index : mask) { - while (offsets_[spline_index] < dst_index) { - spline_index++; - } - new (dst + dst_index) T(original_data_[spline_index]); - } - } - } -}; - -static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArray varray) -{ - GVArray new_varray; - attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { - using T = decltype(dummy); - - Array<int> offsets = curve.control_point_offsets(); - new_varray = VArray<T>::template For<VArray_For_SplineToPoint<T>>(std::move(varray), - std::move(offsets)); - }); - return new_varray; -} - -} // namespace blender::bke - -static GVArray adapt_curve_attribute_domain(const CurveEval &curve, - const GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) -{ - if (!varray) { - return {}; - } - if (varray.is_empty()) { - return {}; - } - if (from_domain == to_domain) { - return varray; - } - - if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) { - return blender::bke::adapt_curve_domain_point_to_spline(curve, std::move(varray)); - } - if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) { - return blender::bke::adapt_curve_domain_spline_to_point(curve, std::move(varray)); - } - - return {}; -} - -/** \} */ - -namespace blender::bke { - -/* -------------------------------------------------------------------- */ -/** \name Builtin Spline Attributes - * - * Attributes with a value for every spline, stored contiguously or in every spline separately. - * \{ */ - -class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const CurveEval &data); - using AsWriteAttribute = GVMutableArray (*)(CurveEval &data); - const AsReadAttribute as_read_attribute_; - const AsWriteAttribute as_write_attribute_; - - public: - BuiltinSplineAttributeProvider(std::string attribute_name, - const eCustomDataType attribute_type, - const WritableEnum writable, - const AsReadAttribute as_read_attribute, - const AsWriteAttribute as_write_attribute) - : BuiltinAttributeProvider(std::move(attribute_name), - ATTR_DOMAIN_CURVE, - attribute_type, - BuiltinAttributeProvider::NonCreatable, - writable, - BuiltinAttributeProvider::NonDeletable), - as_read_attribute_(as_read_attribute), - as_write_attribute_(as_write_attribute) - { - } - - GVArray try_get_for_read(const void *owner) const final - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - return as_read_attribute_(*curve); - } - - GAttributeWriter try_get_for_write(void *owner) const final - { - if (writable_ != Writable) { - return {}; - } - CurveEval *curve = static_cast<CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - return {as_write_attribute_(*curve), domain_}; - } - - bool try_delete(void *UNUSED(owner)) const final - { - return false; - } - - bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final - { - return false; - } - - bool exists(const void *owner) const final - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - return !curve->splines().is_empty(); - } -}; - -static int get_spline_resolution(const SplinePtr &spline) -{ - if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) { - return bezier_spline->resolution(); - } - if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) { - return nurb_spline->resolution(); - } - return 1; -} - -static void set_spline_resolution(SplinePtr &spline, const int resolution) -{ - if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) { - bezier_spline->set_resolution(std::max(resolution, 1)); - } - if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) { - nurb_spline->set_resolution(std::max(resolution, 1)); - } -} - -static GVArray make_resolution_read_attribute(const CurveEval &curve) -{ - return VArray<int>::ForDerivedSpan<SplinePtr, get_spline_resolution>(curve.splines()); -} - -static GVMutableArray make_resolution_write_attribute(CurveEval &curve) -{ - return VMutableArray<int>:: - ForDerivedSpan<SplinePtr, get_spline_resolution, set_spline_resolution>(curve.splines()); -} - -static bool get_cyclic_value(const SplinePtr &spline) -{ - return spline->is_cyclic(); -} - -static void set_cyclic_value(SplinePtr &spline, const bool value) -{ - if (spline->is_cyclic() != value) { - spline->set_cyclic(value); - spline->mark_cache_invalid(); - } -} - -static GVArray make_cyclic_read_attribute(const CurveEval &curve) -{ - return VArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value>(curve.splines()); -} - -static GVMutableArray make_cyclic_write_attribute(CurveEval &curve) -{ - return VMutableArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value, set_cyclic_value>( - curve.splines()); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Builtin Control Point Attributes - * - * Attributes with a value for every control point. Most of the complexity here is due to the fact - * that we must provide access to the attribute data as if it was a contiguous array when it is - * really stored separately on each spline. That will be inherently rather slow, but these virtual - * array implementations try to make it workable in common situations. - * \{ */ - -/** - * Individual spans in \a data may be empty if that spline contains no data for the attribute. - */ -template<typename T> -static void point_attribute_materialize(Span<Span<T>> data, - Span<int> offsets, - const IndexMask mask, - MutableSpan<T> r_span) -{ - const int total_num = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { - for (const int spline_index : data.index_range()) { - const int offset = offsets[spline_index]; - const int next_offset = offsets[spline_index + 1]; - - Span<T> src = data[spline_index]; - MutableSpan<T> dst = r_span.slice(offset, next_offset - offset); - if (src.is_empty()) { - dst.fill(T()); - } - else { - dst.copy_from(src); - } - } - } - else { - int spline_index = 0; - for (const int dst_index : mask) { - /* Skip splines that don't have any control points in the mask. */ - while (dst_index >= offsets[spline_index + 1]) { - spline_index++; - } - - const int index_in_spline = dst_index - offsets[spline_index]; - Span<T> src = data[spline_index]; - if (src.is_empty()) { - r_span[dst_index] = T(); - } - else { - r_span[dst_index] = src[index_in_spline]; - } - } - } -} - -/** - * Individual spans in \a data may be empty if that spline contains no data for the attribute. - */ -template<typename T> -static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, - Span<int> offsets, - const IndexMask mask, - MutableSpan<T> r_span) -{ - T *dst = r_span.data(); - const int total_num = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { - for (const int spline_index : data.index_range()) { - const int offset = offsets[spline_index]; - const int next_offset = offsets[spline_index + 1]; - - Span<T> src = data[spline_index]; - if (src.is_empty()) { - uninitialized_fill_n(dst + offset, next_offset - offset, T()); - } - else { - uninitialized_copy_n(src.data(), next_offset - offset, dst + offset); - } - } - } - else { - int spline_index = 0; - for (const int dst_index : mask) { - /* Skip splines that don't have any control points in the mask. */ - while (dst_index >= offsets[spline_index + 1]) { - spline_index++; - } - - const int index_in_spline = dst_index - offsets[spline_index]; - Span<T> src = data[spline_index]; - if (src.is_empty()) { - new (dst + dst_index) T(); - } - else { - new (dst + dst_index) T(src[index_in_spline]); - } - } - } -} - -static GVArray varray_from_initializer(const AttributeInit &initializer, - const eCustomDataType data_type, - const Span<SplinePtr> splines) -{ - switch (initializer.type) { - case AttributeInit::Type::Construct: - case AttributeInit::Type::DefaultValue: - /* This function shouldn't be called in this case, since there - * is no need to copy anything to the new custom data array. */ - BLI_assert_unreachable(); - return {}; - case AttributeInit::Type::VArray: - return static_cast<const AttributeInitVArray &>(initializer).varray; - case AttributeInit::Type::MoveArray: - int total_num = 0; - for (const SplinePtr &spline : splines) { - total_num += spline->size(); - } - return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type), - static_cast<const AttributeInitMoveArray &>(initializer).data, - total_num)); - } - BLI_assert_unreachable(); - return {}; -} - -static bool create_point_attribute(CurveEval *curve, - const AttributeIDRef &attribute_id, - const AttributeInit &initializer, - const eCustomDataType data_type) -{ - if (curve == nullptr || curve->splines().size() == 0) { - return false; - } - - MutableSpan<SplinePtr> splines = curve->splines(); - - /* First check the one case that allows us to avoid copying the input data. */ - if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { - void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data; - if (!splines.first()->attributes.create_by_move(attribute_id, data_type, source_data)) { - MEM_freeN(source_data); - return false; - } - return true; - } - - /* Otherwise just create a custom data layer on each of the splines. */ - for (const int i : splines.index_range()) { - if (!splines[i]->attributes.create(attribute_id, data_type)) { - /* If attribute creation fails on one of the splines, we cannot leave the custom data - * layers in the previous splines around, so delete them before returning. However, - * this is not an expected case. */ - BLI_assert_unreachable(); - return false; - } - } - - /* With a default initializer type, we can keep the values at their initial values. */ - if (ELEM(initializer.type, AttributeInit::Type::DefaultValue, AttributeInit::Type::Construct)) { - return true; - } - - GAttributeWriter write_attribute = curve->attributes_for_write().lookup_for_write(attribute_id); - /* We just created the attribute, it should exist. */ - BLI_assert(write_attribute); - - GVArray source_varray = varray_from_initializer(initializer, data_type, splines); - /* TODO: When we can call a variant of #set_all with a virtual array argument, - * this theoretically unnecessary materialize step could be removed. */ - GVArraySpan source_VArraySpan{source_varray}; - write_attribute.varray.set_all(source_VArraySpan.data()); - write_attribute.finish(); - - if (initializer.type == AttributeInit::Type::MoveArray) { - MEM_freeN(static_cast<const AttributeInitMoveArray &>(initializer).data); - } - - return true; -} - -static bool remove_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id) -{ - if (curve == nullptr) { - return false; - } - - /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ - bool layer_freed = false; - for (SplinePtr &spline : curve->splines()) { - layer_freed = spline->attributes.remove(attribute_id); - } - return layer_freed; -} - -/** - * Mutable virtual array for any control point data accessed with spans and an offset array. - */ -template<typename T> class VArrayImpl_For_SplinePoints final : public VMutableArrayImpl<T> { - private: - Array<MutableSpan<T>> data_; - Array<int> offsets_; - - public: - VArrayImpl_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets) - : VMutableArrayImpl<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets)) - { - } - - T get(const int64_t index) const final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - return data_[indices.spline_index][indices.point_index]; - } - - void set(const int64_t index, T value) final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - data_[indices.spline_index][indices.point_index] = value; - } - - void set_all(Span<T> src) final - { - for (const int spline_index : data_.index_range()) { - const int offset = offsets_[spline_index]; - const int next_offsets = offsets_[spline_index + 1]; - data_[spline_index].copy_from(src.slice(offset, next_offsets - offset)); - } - } - - void materialize(const IndexMask mask, MutableSpan<T> r_span) const final - { - point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span); - } - - void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final - { - point_attribute_materialize_to_uninitialized( - {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span); - } -}; - -template<typename T> VArray<T> point_data_varray(Array<MutableSpan<T>> spans, Array<int> offsets) -{ - return VArray<T>::template For<VArrayImpl_For_SplinePoints<T>>(std::move(spans), - std::move(offsets)); -} - -template<typename T> -VMutableArray<T> point_data_varray_mutable(Array<MutableSpan<T>> spans, Array<int> offsets) -{ - return VMutableArray<T>::template For<VArrayImpl_For_SplinePoints<T>>(std::move(spans), - std::move(offsets)); -} - -/** - * Virtual array implementation specifically for control point positions. This is only needed for - * Bezier splines, where adjusting the position also requires adjusting handle positions depending - * on handle types. We pay a small price for this when other spline types are mixed with Bezier. - * - * \note There is no need to check the handle type to avoid changing auto handles, since - * retrieving write access to the position data will mark them for recomputation anyway. - */ -class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> { - private: - MutableSpan<SplinePtr> splines_; - Array<int> offsets_; - - public: - VArrayImpl_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets) - : VMutableArrayImpl<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets)) - { - } - - float3 get(const int64_t index) const final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - return splines_[indices.spline_index]->positions()[indices.point_index]; - } - - void set(const int64_t index, float3 value) final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - Spline &spline = *splines_[indices.spline_index]; - spline.positions()[indices.point_index] = value; - } - - void set_all(Span<float3> src) final - { - for (const int spline_index : splines_.index_range()) { - Spline &spline = *splines_[spline_index]; - const int offset = offsets_[spline_index]; - const int next_offset = offsets_[spline_index + 1]; - spline.positions().copy_from(src.slice(offset, next_offset - offset)); - } - } - - /** Utility so we can pass positions to the materialize functions above. */ - Array<Span<float3>> get_position_spans() const - { - Array<Span<float3>> spans(splines_.size()); - for (const int i : spans.index_range()) { - spans[i] = splines_[i]->positions(); - } - return spans; - } - - void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final - { - Array<Span<float3>> spans = this->get_position_spans(); - point_attribute_materialize(spans.as_span(), offsets_, mask, r_span); - } - - void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final - { - Array<Span<float3>> spans = this->get_position_spans(); - point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span); - } -}; - -class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { - private: - MutableSpan<SplinePtr> splines_; - Array<int> offsets_; - bool is_right_; - - public: - VArrayImpl_For_BezierHandles(MutableSpan<SplinePtr> splines, - Array<int> offsets, - const bool is_right) - : VMutableArrayImpl<float3>(offsets.last()), - splines_(splines), - offsets_(std::move(offsets)), - is_right_(is_right) - { - } - - float3 get(const int64_t index) const final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - const Spline &spline = *splines_[indices.spline_index]; - if (spline.type() == CURVE_TYPE_BEZIER) { - const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); - return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] : - bezier_spline.handle_positions_left()[indices.point_index]; - } - return float3(0); - } - - void set(const int64_t index, float3 value) final - { - const PointIndices indices = lookup_point_indices(offsets_, index); - Spline &spline = *splines_[indices.spline_index]; - if (spline.type() == CURVE_TYPE_BEZIER) { - BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); - if (is_right_) { - bezier_spline.handle_positions_right()[indices.point_index] = value; - } - else { - bezier_spline.handle_positions_left()[indices.point_index] = value; - } - bezier_spline.mark_cache_invalid(); - } - } - - void set_all(Span<float3> src) final - { - for (const int spline_index : splines_.index_range()) { - Spline &spline = *splines_[spline_index]; - if (spline.type() == CURVE_TYPE_BEZIER) { - const int offset = offsets_[spline_index]; - - BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); - if (is_right_) { - for (const int i : IndexRange(bezier_spline.size())) { - bezier_spline.handle_positions_right()[i] = src[offset + i]; - } - } - else { - for (const int i : IndexRange(bezier_spline.size())) { - bezier_spline.handle_positions_left()[i] = src[offset + i]; - } - } - bezier_spline.mark_cache_invalid(); - } - } - } - - void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final - { - Array<Span<float3>> spans = get_handle_spans(splines_, is_right_); - point_attribute_materialize(spans.as_span(), offsets_, mask, r_span); - } - - void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final - { - Array<Span<float3>> spans = get_handle_spans(splines_, is_right_); - point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span); - } - - /** - * Utility so we can pass handle positions to the materialize functions above. - * - * \note This relies on the ability of the materialize implementations to - * handle empty spans, since only Bezier splines have handles. - */ - static Array<Span<float3>> get_handle_spans(Span<SplinePtr> splines, const bool is_right) - { - Array<Span<float3>> spans(splines.size()); - for (const int i : spans.index_range()) { - if (splines[i]->type() == CURVE_TYPE_BEZIER) { - BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]); - spans[i] = is_right ? bezier_spline.handle_positions_right() : - bezier_spline.handle_positions_left(); - } - else { - spans[i] = {}; - } - } - return spans; - } -}; - -/** - * Provider for any builtin control point attribute that doesn't need - * special handling like access to other arrays in the spline. - */ -template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider { - protected: - using GetSpan = Span<T> (*)(const Spline &spline); - using GetMutableSpan = MutableSpan<T> (*)(Spline &spline); - using UpdateOnWrite = void (*)(Spline &spline); - const GetSpan get_span_; - const GetMutableSpan get_mutable_span_; - const UpdateOnWrite update_on_write_; - bool stored_in_custom_data_; - - public: - BuiltinPointAttributeProvider(std::string attribute_name, - const CreatableEnum creatable, - const DeletableEnum deletable, - const GetSpan get_span, - const GetMutableSpan get_mutable_span, - const UpdateOnWrite update_on_write, - const bool stored_in_custom_data) - : BuiltinAttributeProvider(std::move(attribute_name), - ATTR_DOMAIN_POINT, - bke::cpp_type_to_custom_data_type(CPPType::get<T>()), - creatable, - WritableEnum::Writable, - deletable), - get_span_(get_span), - get_mutable_span_(get_mutable_span), - update_on_write_(update_on_write), - stored_in_custom_data_(stored_in_custom_data) - { - } - - GVArray try_get_for_read(const void *owner) const override - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - - if (!this->exists(owner)) { - return {}; - } - - Span<SplinePtr> splines = curve->splines(); - if (splines.size() == 1) { - return GVArray::ForSpan(get_span_(*splines.first())); - } - - Array<int> offsets = curve->control_point_offsets(); - Array<MutableSpan<T>> spans(splines.size()); - for (const int i : splines.index_range()) { - Span<T> span = get_span_(*splines[i]); - /* Use const-cast because the underlying virtual array implementation is shared between const - * and non const data. */ - spans[i] = MutableSpan<T>(const_cast<T *>(span.data()), span.size()); - } - - return point_data_varray(spans, offsets); - } - - GAttributeWriter try_get_for_write(void *owner) const override - { - CurveEval *curve = static_cast<CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - - if (!this->exists(owner)) { - return {}; - } - - std::function<void()> tag_modified_fn; - if (update_on_write_ != nullptr) { - tag_modified_fn = [curve, update = update_on_write_]() { - for (SplinePtr &spline : curve->splines()) { - update(*spline); - } - }; - } - - MutableSpan<SplinePtr> splines = curve->splines(); - if (splines.size() == 1) { - return {GVMutableArray::ForSpan(get_mutable_span_(*splines.first())), - domain_, - std::move(tag_modified_fn)}; - } - - Array<int> offsets = curve->control_point_offsets(); - Array<MutableSpan<T>> spans(splines.size()); - for (const int i : splines.index_range()) { - spans[i] = get_mutable_span_(*splines[i]); - } - - return {point_data_varray_mutable(spans, offsets), domain_, tag_modified_fn}; - } - - bool try_delete(void *owner) const final - { - if (deletable_ == DeletableEnum::NonDeletable) { - return false; - } - CurveEval *curve = static_cast<CurveEval *>(owner); - return remove_point_attribute(curve, name_); - } - - bool try_create(void *owner, const AttributeInit &initializer) const final - { - if (createable_ == CreatableEnum::NonCreatable) { - return false; - } - CurveEval *curve = static_cast<CurveEval *>(owner); - return create_point_attribute(curve, name_, initializer, CD_PROP_INT32); - } - - bool exists(const void *owner) const final - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr) { - return false; - } - - Span<SplinePtr> splines = curve->splines(); - if (splines.size() == 0) { - return false; - } - - if (stored_in_custom_data_) { - if (!curve->splines().first()->attributes.get_for_read(name_)) { - return false; - } - } - - bool has_point = false; - for (const SplinePtr &spline : curve->splines()) { - if (spline->size() != 0) { - has_point = true; - break; - } - } - - if (!has_point) { - return false; - } - - return true; - } -}; - -/** - * Special attribute provider for the position attribute. Keeping this separate means we don't - * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the - * positions is more clear. - */ -class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> { - public: - PositionAttributeProvider() - : BuiltinPointAttributeProvider( - "position", - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::NonDeletable, - [](const Spline &spline) { return spline.positions(); }, - [](Spline &spline) { return spline.positions(); }, - [](Spline &spline) { spline.mark_cache_invalid(); }, - false) - { - } - - GAttributeWriter try_get_for_write(void *owner) const final - { - CurveEval *curve = static_cast<CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - - /* Use the regular position virtual array when there aren't any Bezier splines - * to avoid the overhead of checking the spline type for every point. */ - if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { - return BuiltinPointAttributeProvider<float3>::try_get_for_write(owner); - } - - auto tag_modified_fn = [curve]() { - /* Changing the positions requires recalculation of cached evaluated data in many cases. - * This could set more specific flags in the future to avoid unnecessary recomputation. */ - curve->mark_cache_invalid(); - }; - - Array<int> offsets = curve->control_point_offsets(); - return {VMutableArray<float3>::For<VArrayImpl_For_SplinePosition>(curve->splines(), - std::move(offsets)), - domain_, - tag_modified_fn}; - } -}; - -class BezierHandleAttributeProvider : public BuiltinAttributeProvider { - private: - bool is_right_; - - public: - BezierHandleAttributeProvider(const bool is_right) - : BuiltinAttributeProvider(is_right ? "handle_right" : "handle_left", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT3, - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::NonDeletable), - is_right_(is_right) - { - } - - GVArray try_get_for_read(const void *owner) const override - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - - if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { - return {}; - } - - Array<int> offsets = curve->control_point_offsets(); - /* Use const-cast because the underlying virtual array implementation is shared between const - * and non const data. */ - return VArray<float3>::For<VArrayImpl_For_BezierHandles>( - const_cast<CurveEval *>(curve)->splines(), std::move(offsets), is_right_); - } - - GAttributeWriter try_get_for_write(void *owner) const override - { - CurveEval *curve = static_cast<CurveEval *>(owner); - if (curve == nullptr) { - return {}; - } - - if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { - return {}; - } - - auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); }; - - Array<int> offsets = curve->control_point_offsets(); - return {VMutableArray<float3>::For<VArrayImpl_For_BezierHandles>( - curve->splines(), std::move(offsets), is_right_), - domain_, - tag_modified_fn}; - } - - bool try_delete(void *UNUSED(owner)) const final - { - return false; - } - - bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final - { - return false; - } - - bool exists(const void *owner) const final - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr) { - return false; - } - - CurveComponentLegacy component; - component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly); - - return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && !curve->splines().is_empty(); - } -}; - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Dynamic Control Point Attributes - * - * The dynamic control point attribute implementation is very similar to the builtin attribute - * implementation-- it uses the same virtual array types. In order to work, this code depends on - * the fact that all a curve's splines will have the same attributes and they all have the same - * type. - * \{ */ - -class DynamicPointAttributeProvider final : public DynamicAttributesProvider { - private: - static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | - CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | - CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL | - CD_MASK_PROP_INT8; - - public: - GAttributeReader try_get_for_read(const void *owner, - const AttributeIDRef &attribute_id) const final - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr || curve->splines().size() == 0) { - return {}; - } - - Span<SplinePtr> splines = curve->splines(); - Vector<GSpan> spans; /* GSpan has no default constructor. */ - spans.reserve(splines.size()); - std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_id); - if (!first_span) { - return {}; - } - spans.append(*first_span); - for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_id); - if (!span) { - /* All splines should have the same set of data layers. It would be possible to recover - * here and return partial data instead, but that would add a lot of complexity for a - * situation we don't even expect to encounter. */ - BLI_assert_unreachable(); - return {}; - } - if (span->type() != spans.last().type()) { - /* Data layer types on separate splines do not match. */ - BLI_assert_unreachable(); - return {}; - } - spans.append(*span); - } - - /* First check for the simpler situation when we can return a simpler span virtual array. */ - if (spans.size() == 1) { - return {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT}; - } - - GAttributeReader attribute = {}; - Array<int> offsets = curve->control_point_offsets(); - attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { - using T = decltype(dummy); - Array<MutableSpan<T>> data(splines.size()); - for (const int i : splines.index_range()) { - Span<T> span = spans[i].typed<T>(); - /* Use const-cast because the underlying virtual array implementation is shared between - * const and non const data. */ - data[i] = MutableSpan<T>(const_cast<T *>(span.data()), span.size()); - BLI_assert(data[i].data() != nullptr); - } - attribute = {point_data_varray(data, offsets), ATTR_DOMAIN_POINT}; - }); - return attribute; - } - - /* This function is almost the same as #try_get_for_read, but without const. */ - GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final - { - CurveEval *curve = static_cast<CurveEval *>(owner); - if (curve == nullptr || curve->splines().size() == 0) { - return {}; - } - - MutableSpan<SplinePtr> splines = curve->splines(); - Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */ - spans.reserve(splines.size()); - std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_id); - if (!first_span) { - return {}; - } - spans.append(*first_span); - for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_id); - if (!span) { - /* All splines should have the same set of data layers. It would be possible to recover - * here and return partial data instead, but that would add a lot of complexity for a - * situation we don't even expect to encounter. */ - BLI_assert_unreachable(); - return {}; - } - if (span->type() != spans.last().type()) { - /* Data layer types on separate splines do not match. */ - BLI_assert_unreachable(); - return {}; - } - spans.append(*span); - } - - /* First check for the simpler situation when we can return a simpler span virtual array. */ - if (spans.size() == 1) { - return {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT}; - } - - GAttributeWriter attribute = {}; - Array<int> offsets = curve->control_point_offsets(); - attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { - using T = decltype(dummy); - Array<MutableSpan<T>> data(splines.size()); - for (const int i : splines.index_range()) { - data[i] = spans[i].typed<T>(); - BLI_assert(data[i].data() != nullptr); - } - attribute = {point_data_varray_mutable(data, offsets), ATTR_DOMAIN_POINT}; - }); - return attribute; - } - - bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final - { - CurveEval *curve = static_cast<CurveEval *>(owner); - return remove_point_attribute(curve, attribute_id); - } - - bool try_create(void *owner, - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const AttributeInit &initializer) const final - { - BLI_assert(this->type_is_supported(data_type)); - if (domain != ATTR_DOMAIN_POINT) { - return false; - } - CurveEval *curve = static_cast<CurveEval *>(owner); - return create_point_attribute(curve, attribute_id, initializer, data_type); - } - - bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final - { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - if (curve == nullptr || curve->splines().size() == 0) { - return false; - } - - Span<SplinePtr> splines = curve->splines(); - - /* In a debug build, check that all corresponding custom data layers have the same type. */ - curve->assert_valid_point_attributes(); - - /* Use the first spline as a representative for all the others. */ - splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT); - - return true; - } - - void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final - { - callback(ATTR_DOMAIN_POINT); - } - - bool type_is_supported(eCustomDataType data_type) const - { - return ((1ULL << data_type) & supported_types_mask) != 0; - } -}; - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Attribute Provider Declaration - * \{ */ - -/** - * In this function all the attribute providers for a curve component are created. - * Most data in this function is statically allocated, because it does not change over time. - */ -static ComponentAttributeProviders create_attribute_providers_for_curve() -{ - static BuiltinSplineAttributeProvider resolution("resolution", - CD_PROP_INT32, - BuiltinAttributeProvider::Writable, - make_resolution_read_attribute, - make_resolution_write_attribute); - - static BuiltinSplineAttributeProvider cyclic("cyclic", - CD_PROP_BOOL, - BuiltinAttributeProvider::Writable, - make_cyclic_read_attribute, - make_cyclic_write_attribute); - - static CustomDataAccessInfo spline_custom_data_access = { - [](void *owner) -> CustomData * { - CurveEval *curve = static_cast<CurveEval *>(owner); - return curve ? &curve->attributes.data : nullptr; - }, - [](const void *owner) -> const CustomData * { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - return curve ? &curve->attributes.data : nullptr; - }, - [](const void *owner) -> int { - const CurveEval *curve = static_cast<const CurveEval *>(owner); - return curve->splines().size(); - }}; - - static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE, - spline_custom_data_access); - - static PositionAttributeProvider position; - static BezierHandleAttributeProvider handles_start(false); - static BezierHandleAttributeProvider handles_end(true); - - static BuiltinPointAttributeProvider<int> id( - "id", - BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Deletable, - [](const Spline &spline) { - std::optional<GSpan> span = spline.attributes.get_for_read("id"); - return span ? span->typed<int>() : Span<int>(); - }, - [](Spline &spline) { - std::optional<GMutableSpan> span = spline.attributes.get_for_write("id"); - return span ? span->typed<int>() : MutableSpan<int>(); - }, - {}, - true); - - static BuiltinPointAttributeProvider<float> radius( - "radius", - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::NonDeletable, - [](const Spline &spline) { return spline.radii(); }, - [](Spline &spline) { return spline.radii(); }, - nullptr, - false); - - static BuiltinPointAttributeProvider<float> tilt( - "tilt", - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::NonDeletable, - [](const Spline &spline) { return spline.tilts(); }, - [](Spline &spline) { return spline.tilts(); }, - [](Spline &spline) { spline.mark_cache_invalid(); }, - false); - - static DynamicPointAttributeProvider point_custom_data; - - return ComponentAttributeProviders( - {&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, - {&spline_custom_data, &point_custom_data}); -} - -/** \} */ - -static AttributeAccessorFunctions get_curve_accessor_functions() -{ - static const ComponentAttributeProviders providers = create_attribute_providers_for_curve(); - AttributeAccessorFunctions fn = - attribute_accessor_functions::accessor_functions_for_providers<providers>(); - fn.domain_size = [](const void *owner, const eAttrDomain domain) -> int { - if (owner == nullptr) { - return 0; - } - const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner); - switch (domain) { - case ATTR_DOMAIN_POINT: - return curve_eval.total_control_point_num(); - case ATTR_DOMAIN_CURVE: - return curve_eval.splines().size(); - default: - return 0; - } - }; - fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { - return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); - }; - fn.adapt_domain = [](const void *owner, - const blender::GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) -> GVArray { - if (owner == nullptr) { - return {}; - } - const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner); - return adapt_curve_attribute_domain(curve_eval, varray, from_domain, to_domain); - }; - return fn; -} - -static const AttributeAccessorFunctions &get_curve_accessor_functions_ref() -{ - static const AttributeAccessorFunctions fn = get_curve_accessor_functions(); - return fn; -} - -} // namespace blender::bke - -std::optional<blender::bke::AttributeAccessor> CurveComponentLegacy::attributes() const -{ - return blender::bke::AttributeAccessor(curve_, blender::bke::get_curve_accessor_functions_ref()); -} - -std::optional<blender::bke::MutableAttributeAccessor> CurveComponentLegacy::attributes_for_write() -{ - CurveEval *curve = this->get_for_write(); - return blender::bke::MutableAttributeAccessor(curve, - blender::bke::get_curve_accessor_functions_ref()); -} - -blender::bke::MutableAttributeAccessor CurveEval::attributes_for_write() -{ - return blender::bke::MutableAttributeAccessor(this, - blender::bke::get_curve_accessor_functions_ref()); -} diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 83a35534c01..4ace68546ac 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -12,6 +12,8 @@ #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" +#include "FN_multi_function_builder.hh" + #include "attribute_access_intern.hh" using blender::GVArray; @@ -426,6 +428,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_array_write_attribute<float3>, tag_component_positions_changed); + static const fn::CustomMF_SI_SO<int8_t, int8_t> handle_type_clamp{ + "Handle Type Validate", + [](int8_t value) { + return std::clamp<int8_t>(value, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN); + }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right", ATTR_DOMAIN_POINT, CD_PROP_INT8, @@ -436,7 +444,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() point_access, make_array_read_attribute<int8_t>, make_array_write_attribute<int8_t>, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&handle_type_clamp}); static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left", ATTR_DOMAIN_POINT, @@ -448,7 +457,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() point_access, make_array_read_attribute<int8_t>, make_array_write_attribute<int8_t>, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&handle_type_clamp}); static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight", ATTR_DOMAIN_POINT, @@ -462,6 +472,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_array_write_attribute<float>, tag_component_positions_changed); + static const fn::CustomMF_SI_SO<int8_t, int8_t> nurbs_order_clamp{ + "NURBS Order Validate", + [](int8_t value) { return std::max<int8_t>(value, 0); }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order", ATTR_DOMAIN_CURVE, CD_PROP_INT8, @@ -472,8 +486,15 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() curve_access, make_array_read_attribute<int8_t>, make_array_write_attribute<int8_t>, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&nurbs_order_clamp}); + static const fn::CustomMF_SI_SO<int8_t, int8_t> normal_mode_clamp{ + "Normal Mode Validate", + [](int8_t value) { + return std::clamp<int8_t>(value, NORMAL_MODE_MINIMUM_TWIST, NORMAL_MODE_Z_UP); + }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider normal_mode("normal_mode", ATTR_DOMAIN_CURVE, CD_PROP_INT8, @@ -484,8 +505,15 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() curve_access, make_array_read_attribute<int8_t>, make_array_write_attribute<int8_t>, - tag_component_normals_changed); + tag_component_normals_changed, + AttributeValidator{&normal_mode_clamp}); + static const fn::CustomMF_SI_SO<int8_t, int8_t> knots_mode_clamp{ + "Knots Mode Validate", + [](int8_t value) { + return std::clamp<int8_t>(value, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT_BEZIER); + }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode", ATTR_DOMAIN_CURVE, CD_PROP_INT8, @@ -496,8 +524,15 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() curve_access, make_array_read_attribute<int8_t>, make_array_write_attribute<int8_t>, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&knots_mode_clamp}); + static const fn::CustomMF_SI_SO<int8_t, int8_t> curve_type_clamp{ + "Curve Type Validate", + [](int8_t value) { + return std::clamp<int8_t>(value, CURVE_TYPE_CATMULL_ROM, CURVE_TYPES_NUM); + }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider curve_type("curve_type", ATTR_DOMAIN_CURVE, CD_PROP_INT8, @@ -508,8 +543,13 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() curve_access, make_array_read_attribute<int8_t>, make_array_write_attribute<int8_t>, - tag_component_curve_types_changed); + tag_component_curve_types_changed, + AttributeValidator{&curve_type_clamp}); + static const fn::CustomMF_SI_SO<int, int> resolution_clamp{ + "Resolution Validate", + [](int value) { return std::max<int>(value, 1); }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider resolution("resolution", ATTR_DOMAIN_CURVE, CD_PROP_INT32, @@ -520,7 +560,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() curve_access, make_array_read_attribute<int>, make_array_write_attribute<int>, - tag_component_topology_changed); + tag_component_topology_changed, + AttributeValidator{&resolution_clamp}); static BuiltinCustomDataLayerProvider cyclic("cyclic", ATTR_DOMAIN_CURVE, diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 715c7d6c743..bf1dc1453c2 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -14,6 +14,8 @@ #include "BKE_lib_id.h" #include "BKE_mesh.h" +#include "FN_multi_function_builder.hh" + #include "attribute_access_intern.hh" extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id); @@ -902,14 +904,14 @@ static void set_loop_uv(MLoopUV &uv, float2 co) copy_v2_v2(uv.uv, co); } -static float get_crease(const MEdge &edge) +static float get_crease(const float &crease) { - return edge.crease / 255.0f; + return crease; } -static void set_crease(MEdge &edge, float value) +static void set_crease(float &crease, const float value) { - edge.crease = round_fl_to_uchar_clamp(value * 255.0f); + crease = std::clamp(value, 0.0f, 1.0f); } class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> { @@ -1217,6 +1219,13 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() make_array_write_attribute<int>, nullptr); + static const fn::CustomMF_SI_SO<int, int> material_index_clamp{ + "Material Index Validate", + [](int value) { + /* Use #short for the maximum since many areas still use that type for indices. */ + return std::clamp<int>(value, 0, std::numeric_limits<short>::max()); + }, + fn::CustomMF_presets::AllSpanOrSingle()}; static BuiltinCustomDataLayerProvider material_index("material_index", ATTR_DOMAIN_FACE, CD_PROP_INT32, @@ -1227,7 +1236,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() face_access, make_array_read_attribute<int>, make_array_write_attribute<int>, - nullptr); + nullptr, + AttributeValidator{&material_index_clamp}); static BuiltinCustomDataLayerProvider shade_smooth( "shade_smooth", @@ -1246,13 +1256,13 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() "crease", ATTR_DOMAIN_EDGE, CD_PROP_FLOAT, - CD_MEDGE, - BuiltinAttributeProvider::NonCreatable, + CD_CREASE, + BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::NonDeletable, + BuiltinAttributeProvider::Deletable, edge_access, - make_derived_read_attribute<MEdge, float, get_crease>, - make_derived_write_attribute<MEdge, float, get_crease, set_crease>, + make_array_read_attribute<float>, + make_derived_write_attribute<float, float, get_crease, set_crease>, nullptr); static NamedLegacyCustomDataProvider uvs( diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 33f84aff545..8361d8e1849 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -677,7 +677,7 @@ static void copy_frame_to_eval_cb(bGPDlayer *gpl, * - When the frame is the layer's active frame (already handled in * gpencil_copy_visible_frames_to_eval). */ - if (gpf == NULL || gpf == gpl->actframe) { + if (ELEM(gpf, NULL, gpl->actframe)) { return; } diff --git a/source/blender/blenkernel/intern/idprop_create.cc b/source/blender/blenkernel/intern/idprop_create.cc index 24f59d5e49e..8a6e5cdcc50 100644 --- a/source/blender/blenkernel/intern/idprop_create.cc +++ b/source/blender/blenkernel/intern/idprop_create.cc @@ -79,7 +79,7 @@ static void array_values_set(IDProperty *property, template< /** C-Primitive type of the array. Can be int32_t, float, double. */ typename PrimitiveType, - /** Subtype of the ID_ARRAY. Must match PrimitiveType. */ + /** Sub-type of the #ID_ARRAY. Must match #PrimitiveType. */ eIDPropertyType id_property_subtype> std::unique_ptr<IDProperty, IDPropertyDeleter> create_array(StringRefNull prop_name, Span<PrimitiveType> values) diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 75cf10f88aa..3cc0727a94a 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -1999,7 +1999,7 @@ void BKE_image_stamp_buf(Scene *scene, } /* set before return */ - BLF_size(mono, scene->r.stamp_font_id, 72); + BLF_size(mono, scene->r.stamp_font_id); BLF_wordwrap(mono, width - (BUFF_MARGIN_X * 2)); BLF_buffer(mono, rectf, rect, width, height, channels, display); @@ -2662,7 +2662,7 @@ Image *BKE_image_ensure_viewer(Main *bmain, int type, const char *name) ima = image_alloc(bmain, name, IMA_SRC_VIEWER, type); } - /* Happens on reload, imagewindow cannot be image user when hidden. */ + /* Happens on reload, image-window cannot be image user when hidden. */ if (ima->id.us == 0) { id_us_ensure_real(&ima->id); } @@ -5148,7 +5148,7 @@ bool BKE_image_has_alpha(Image *image) const int planes = (ibuf ? ibuf->planes : 0); BKE_image_release_ibuf(image, ibuf, lock); - if (planes == 32 || planes == 16) { + if (ELEM(planes, 32, 16)) { return true; } @@ -5567,15 +5567,14 @@ bool BKE_image_remove_renderslot(Image *ima, ImageUser *iuser, int slot) next_last_slot = current_slot; } - if (!iuser) { + if (iuser == nullptr || iuser->scene == nullptr) { return false; } Render *re = RE_GetSceneRender(iuser->scene); - if (!re) { - return false; + if (re != nullptr) { + RE_SwapResult(re, ¤t_last_slot->render); + RE_SwapResult(re, &next_last_slot->render); } - RE_SwapResult(re, ¤t_last_slot->render); - RE_SwapResult(re, &next_last_slot->render); current_last_slot = next_last_slot; } diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index 5a299582890..32795baaa37 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -355,7 +355,7 @@ static void checker_board_text( char text[3] = {'A', '1', '\0'}; const int mono = blf_mono_font_render; - BLF_size(mono, 54.0f, 72); /* hard coded size! */ + BLF_size(mono, 54.0f); /* hard coded size! */ /* OCIO_TODO: using NULL as display will assume using sRGB display * this is correct since currently generated images are assumed to be in sRGB space, diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 22c0007cbc0..40a5c08a068 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -2142,7 +2142,7 @@ void do_versions_ipos_to_animato(Main *bmain) if (ob->action) { action_to_animdata(id, ob->action); - /* only decrease usercount if this Action isn't now being used by AnimData */ + /* Only decrease user-count if this Action isn't now being used by AnimData. */ if (ob->action != adt->action) { id_us_min(&ob->action->id); ob->action = NULL; @@ -2246,7 +2246,7 @@ void do_versions_ipos_to_animato(Main *bmain) /* Add AnimData block */ AnimData *adt = BKE_animdata_ensure_id(id); - /* Convert Shapekey data... */ + /* Convert Shape-key data... */ ipo_to_animdata(bmain, id, key->ipo, NULL, NULL, NULL); if (adt->action) { diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 8faa260ab8b..158aaa961ce 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -416,7 +416,7 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) /* Can happen that we get un-linkable ID here, e.g. with shape-key referring to itself * (through drivers)... * Just skip it, shape key can only be either indirectly linked, or fully local, period. - * And let's curse one more time that stupid useless shapekey ID type! */ + * And let's curse one more time that stupid useless shape-key ID type! */ if (*id_pointer && *id_pointer != id_self && BKE_idtype_idcode_is_linkable(GS((*id_pointer)->name))) { id_lib_extern(*id_pointer); @@ -566,7 +566,7 @@ struct IDCopyLibManagementData { int flag; }; -/* Increases usercount as required, and remap self ID pointers. */ +/** Increases user-count as required, and remap self ID pointers. */ static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data) { ID **id_pointer = cb_data->id_pointer; @@ -1289,7 +1289,7 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori new_id->flag = (new_id->flag & ~copy_idflag_mask) | (id->flag & copy_idflag_mask); - /* We do not want any handling of usercount in code duplicating the data here, we do that all + /* We do not want any handling of user-count in code duplicating the data here, we do that all * at once in id_copy_libmanagement_cb() at the end. */ const int copy_data_flag = orig_flag | LIB_ID_CREATE_NO_USER_REFCOUNT; @@ -1566,7 +1566,7 @@ void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_onl } FOREACH_MAIN_ID_END; - /* Go over whole Main database to re-generate proper usercounts... */ + /* Go over whole Main database to re-generate proper user-counts. */ FOREACH_MAIN_ID_BEGIN (bmain, id) { BKE_library_foreach_ID_link(bmain, id, diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 8d5699d7d49..f4f5ca7a1d7 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -280,8 +280,8 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) } /* Now we can safely mark that ID as not being in Main database anymore. */ - /* NOTE: This needs to be done in a separate loop than above, otherwise some usercounts of - * deleted IDs may not be properly decreased by the remappings (since `NO_MAIN` ID usercounts + /* NOTE: This needs to be done in a separate loop than above, otherwise some user-counts of + * deleted IDs may not be properly decreased by the remappings (since `NO_MAIN` ID user-counts * is never affected). */ for (ID *id = tagged_deleted_ids.first; id; id = id->next) { id->tag |= LIB_TAG_NO_MAIN; diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index b05ae775be6..3f1b80014ac 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -1846,8 +1846,8 @@ static bool lib_override_library_resync(Main *bmain, } } if (reference_id == nullptr) { - /* Can happen e.g. when there is a local override of a shapekey, but the matching linked - * obdata (mesh etc.) does not have any shapekey anymore. */ + /* Can happen e.g. when there is a local override of a shape-key, but the matching linked + * obdata (mesh etc.) does not have any shape-key anymore. */ continue; } BLI_assert(GS(reference_id->name) == GS(id->name)); @@ -3951,7 +3951,7 @@ void BKE_lib_override_library_operations_store_end( void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage) { /* We cannot just call BKE_main_free(override_storage), not until we have option to make - * 'ghost' copies of IDs without increasing usercount of used data-blocks. */ + * 'ghost' copies of IDs without increasing user-count of used data-blocks. */ ID *id; FOREACH_MAIN_ID_BEGIN (override_storage, id) { diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 28b0337d9a2..2ebdc6788d9 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -817,7 +817,7 @@ void BKE_libblock_relink_ex( Main *bmain, void *idv, void *old_idv, void *new_idv, const short remap_flags) { - /* Should be able to replace all _relink() funcs (constraints, rigidbody, etc.) ? */ + /* Should be able to replace all _relink() functions (constraints, rigidbody, etc.) ? */ ID *id = idv; ID *old_id = old_idv; diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 25268785d42..0b8351efdf4 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1579,7 +1579,7 @@ void BKE_mask_parent_init(MaskParent *parent) parent->id_type = ID_MC; } -/* *** own animation/shapekey implementation *** +/* *** own animation/shape-key implementation *** * BKE_mask_layer_shape_XXX */ int BKE_mask_layer_shape_totvert(MaskLayer *masklay) diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 49963c333ec..48fadd2e9b8 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -1447,7 +1447,6 @@ Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob) MVert *mvert = CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CONSTRUCT, NULL, mesh->totvert); for (int i = 0; i < mesh->totvert; i++) { copy_v3_v3(mvert[i].co, process.co[i]); - mvert->flag = 0; } MEM_freeN(process.co); diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 7da9acc3cf6..6df6cd31cf4 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -68,6 +68,7 @@ using blender::BitVector; using blender::float3; using blender::MutableSpan; using blender::Span; +using blender::StringRef; using blender::VArray; using blender::Vector; @@ -148,8 +149,6 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int mesh_tessface_clear_intern(mesh_dst, false); } - mesh_dst->cd_flag = mesh_src->cd_flag; - mesh_dst->edit_mesh = nullptr; mesh_dst->mselect = (MSelect *)MEM_dupallocN(mesh_dst->mselect); @@ -250,10 +249,19 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address Set<std::string> names_to_skip; if (!BLO_write_is_undo(writer)) { BKE_mesh_legacy_convert_hide_layers_to_flags(mesh); + BKE_mesh_legacy_convert_selection_layers_to_flags(mesh); BKE_mesh_legacy_convert_material_indices_to_mpoly(mesh); BKE_mesh_legacy_bevel_weight_from_layers(mesh); + BKE_mesh_legacy_face_set_from_generic(mesh); + BKE_mesh_legacy_edge_crease_from_layers(mesh); /* When converting to the old mesh format, don't save redundant attributes. */ - names_to_skip.add_multiple_new({".hide_vert", ".hide_edge", ".hide_poly", "material_index"}); + names_to_skip.add_multiple_new({".hide_vert", + ".hide_edge", + ".hide_poly", + "material_index", + ".select_vert", + ".select_edge", + ".select_poly"}); /* Set deprecated mesh data pointers for forward compatibility. */ mesh->mvert = const_cast<MVert *>(mesh->verts().data()); @@ -689,7 +697,6 @@ static int customdata_compare( case CD_PROP_BOOL: { const bool *l1_data = (bool *)l1->data; const bool *l2_data = (bool *)l2->data; - for (int i = 0; i < total_length; i++) { if (l1_data[i] != l2_data[i]) { return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; @@ -1017,7 +1024,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, me_dst->totloop = loops_len; me_dst->totpoly = polys_len; - me_dst->cd_flag = me_src->cd_flag; BKE_mesh_copy_parameters_for_eval(me_dst, me_src); CustomData_copy(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_SET_DEFAULT, verts_len); @@ -1619,7 +1625,7 @@ void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh) break; } } - if (edge.crease != 0) { + if (edge.crease_legacy != 0) { mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { break; @@ -1639,39 +1645,46 @@ void BKE_mesh_mselect_clear(Mesh *me) void BKE_mesh_mselect_validate(Mesh *me) { + using namespace blender; + using namespace blender::bke; MSelect *mselect_src, *mselect_dst; int i_src, i_dst; if (me->totselect == 0) { return; } - const Span<MVert> verts = me->verts(); - const Span<MEdge> edges = me->edges(); - const Span<MPoly> polys = me->polys(); mselect_src = me->mselect; mselect_dst = (MSelect *)MEM_malloc_arrayN( (me->totselect), sizeof(MSelect), "Mesh selection history"); + const AttributeAccessor attributes = me->attributes(); + const VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + const VArray<bool> select_edge = attributes.lookup_or_default<bool>( + ".select_edge", ATTR_DOMAIN_EDGE, false); + const VArray<bool> select_poly = attributes.lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + for (i_src = 0, i_dst = 0; i_src < me->totselect; i_src++) { int index = mselect_src[i_src].index; switch (mselect_src[i_src].type) { case ME_VSEL: { - if (verts[index].flag & SELECT) { + if (select_vert[index]) { mselect_dst[i_dst] = mselect_src[i_src]; i_dst++; } break; } case ME_ESEL: { - if (edges[index].flag & SELECT) { + if (select_edge[index]) { mselect_dst[i_dst] = mselect_src[i_src]; i_dst++; } break; } case ME_FSEL: { - if (polys[index].flag & SELECT) { + if (select_poly[index]) { mselect_dst[i_dst] = mselect_src[i_src]; i_dst++; } diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index 7a04e45fe00..be6c27ee6f9 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -375,14 +375,10 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes, * `mv` is in `dest_mesh` with index `mv_index`. * The `orig_mv` vertex came from Mesh `orig_me` and had index `index_in_orig_me` there. */ static void copy_vert_attributes(Mesh *dest_mesh, - MVert *mv, - const MVert *orig_mv, const Mesh *orig_me, int mv_index, int index_in_orig_me) { - mv->flag = orig_mv->flag; - /* For all layers in the orig mesh, copy the layer information. */ CustomData *target_cd = &dest_mesh->vdata; const CustomData *source_cd = &orig_me->vdata; @@ -449,7 +445,6 @@ static void copy_edge_attributes(Mesh *dest_mesh, int medge_index, int index_in_orig_me) { - medge->crease = orig_medge->crease; medge->flag = orig_medge->flag; CustomData *target_cd = &dest_mesh->edata; const CustomData *source_cd = &orig_me->edata; @@ -724,14 +719,14 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim) MutableSpan<MVert> verts = result->verts_for_write(); for (int vi : im->vert_index_range()) { const Vert *v = im->vert(vi); - MVert *mv = &verts[vi]; - copy_v3fl_v3db(mv->co, v->co); if (v->orig != NO_INDEX) { const Mesh *orig_me; int index_in_orig_me; - const MVert *orig_mv = mim.input_mvert_for_orig_index(v->orig, &orig_me, &index_in_orig_me); - copy_vert_attributes(result, mv, orig_mv, orig_me, vi, index_in_orig_me); + mim.input_mvert_for_orig_index(v->orig, &orig_me, &index_in_orig_me); + copy_vert_attributes(result, orig_me, vi, index_in_orig_me); } + MVert *mv = &verts[vi]; + copy_v3fl_v3db(mv->co, v->co); } /* Set the loopstart and totloop for each output poly, diff --git a/source/blender/blenkernel/intern/mesh_calc_edges.cc b/source/blender/blenkernel/intern/mesh_calc_edges.cc index cc315130ad1..038133c33ae 100644 --- a/source/blender/blenkernel/intern/mesh_calc_edges.cc +++ b/source/blender/blenkernel/intern/mesh_calc_edges.cc @@ -13,6 +13,7 @@ #include "BLI_threads.h" #include "BLI_timeit.hh" +#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" @@ -120,8 +121,7 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh, } static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edge_maps, - MutableSpan<MEdge> new_edges, - short new_edge_flag) + MutableSpan<MEdge> new_edges) { /* All edges are distributed in the hash tables now. They have to be serialized into a single * array below. To be able to parallelize this, we have to compute edge index offsets for each @@ -147,7 +147,7 @@ static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edg /* Initialize new edge. */ new_edge.v1 = item.key.v_low; new_edge.v2 = item.key.v_high; - new_edge.flag = new_edge_flag; + new_edge.flag = ME_EDGEDRAW | ME_EDGERENDER; } item.value.index = new_edge_index; new_edge_index++; @@ -236,8 +236,7 @@ void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select /* Create new edges. */ MutableSpan<MEdge> new_edges{ static_cast<MEdge *>(MEM_calloc_arrayN(new_totedge, sizeof(MEdge), __func__)), new_totedge}; - const short new_edge_flag = (ME_EDGEDRAW | ME_EDGERENDER) | (select_new_edges ? SELECT : 0); - calc_edges::serialize_and_initialize_deduplicated_edges(edge_maps, new_edges, new_edge_flag); + calc_edges::serialize_and_initialize_deduplicated_edges(edge_maps, new_edges); calc_edges::update_edge_indices_in_poly_loops(mesh, edge_maps, parallel_mask); /* Free old CustomData and assign new one. */ @@ -246,6 +245,24 @@ void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_ASSIGN, new_edges.data(), new_totedge); mesh->totedge = new_totedge; + if (select_new_edges) { + MutableAttributeAccessor attributes = mesh->attributes_for_write(); + SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_span<bool>( + ".select_edge", ATTR_DOMAIN_EDGE); + if (select_edge) { + int new_edge_index = 0; + for (const EdgeMap &edge_map : edge_maps) { + for (EdgeMap::Item item : edge_map.items()) { + if (item.value.original_edge == nullptr) { + select_edge.span[new_edge_index] = true; + } + new_edge_index++; + } + } + select_edge.finish(); + } + } + /* Explicitly clear edge maps, because that way it can be parallelized. */ clear_hash_tables(edge_maps); } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index e56df0e3fe3..1ad74ef693a 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -119,7 +119,6 @@ static void make_edges_mdata_extend(Mesh &mesh) BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2); BLI_edgehashIterator_setValue(ehi, POINTER_FROM_UINT(e_index)); - medge->crease = 0; medge->flag = ME_EDGEDRAW | ME_EDGERENDER; } BLI_edgehashIterator_free(ehi); @@ -1328,7 +1327,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob) BLI_listbase_clear(&mesh_src->vertex_group_names); BKE_mesh_copy_parameters(mesh_dst, mesh_src); - mesh_dst->cd_flag = mesh_src->cd_flag; /* For original meshes, shape key data is stored in the #Key data-block, so it * must be moved from the storage in #CustomData layers used for evaluation. */ diff --git a/source/blender/blenkernel/intern/mesh_debug.cc b/source/blender/blenkernel/intern/mesh_debug.cc index 8a9ce901923..ba4f25c74ee 100644 --- a/source/blender/blenkernel/intern/mesh_debug.cc +++ b/source/blender/blenkernel/intern/mesh_debug.cc @@ -27,21 +27,11 @@ # include "BLI_dynstr.h" -static void mesh_debug_info_from_cd_flag(const Mesh *me, DynStr *dynstr) -{ - BLI_dynstr_append(dynstr, "'cd_flag': {"); - if (me->cd_flag & ME_CDFLAG_EDGE_CREASE) { - BLI_dynstr_append(dynstr, "'EDGE_CREASE', "); - } - BLI_dynstr_append(dynstr, "},\n"); -} - char *BKE_mesh_debug_info(const Mesh *me) { DynStr *dynstr = BLI_dynstr_new(); char *ret; - const char *indent4 = " "; const char *indent8 = " "; BLI_dynstr_append(dynstr, "{\n"); @@ -75,9 +65,6 @@ char *BKE_mesh_debug_info(const Mesh *me) CustomData_debug_info_from_layers(&me->fdata, indent8, dynstr); BLI_dynstr_append(dynstr, " ),\n"); - BLI_dynstr_append(dynstr, indent4); - mesh_debug_info_from_cd_flag(me, dynstr); - BLI_dynstr_append(dynstr, "}\n"); ret = BLI_dynstr_get_cstring(dynstr); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index 938d7e42aa3..4f8391263a1 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -818,103 +818,88 @@ void BKE_mesh_flush_hidden_from_polys(Mesh *me) hide_edge.finish(); } -void BKE_mesh_flush_select_from_polys_ex(MVert *mvert, - const int totvert, - const MLoop *mloop, - MEdge *medge, - const int totedge, - const MPoly *mpoly, - const int totpoly) +void BKE_mesh_flush_select_from_polys(Mesh *me) { - MVert *mv; - MEdge *med; - const MPoly *mp; - - int i = totvert; - for (mv = mvert; i--; mv++) { - mv->flag &= (char)~SELECT; + using namespace blender::bke; + MutableAttributeAccessor attributes = me->attributes_for_write(); + const VArray<bool> select_poly = attributes.lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + if (select_poly.is_single() && !select_poly.get_internal_single()) { + attributes.remove(".select_vert"); + attributes.remove(".select_edge"); + return; } + SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_vert", ATTR_DOMAIN_POINT); + SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_edge", ATTR_DOMAIN_EDGE); - i = totedge; - for (med = medge; i--; med++) { - med->flag &= ~SELECT; - } + /* Use generic domain interpolation to read the polygon attribute on the other domains. + * Assume selected faces are not hidden and none of their vertices/edges are hidden. */ + attributes.lookup_or_default<bool>(".select_poly", ATTR_DOMAIN_POINT, false) + .materialize(select_vert.span); + attributes.lookup_or_default<bool>(".select_poly", ATTR_DOMAIN_EDGE, false) + .materialize(select_edge.span); - i = totpoly; - for (mp = mpoly; i--; mp++) { - /* Assume if its selected its not hidden and none of its verts/edges are hidden - * (a common assumption). */ - if (mp->flag & ME_FACE_SEL) { - const MLoop *ml; - int j; - j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - mvert[ml->v].flag |= SELECT; - medge[ml->e].flag |= SELECT; - } - } - } -} -void BKE_mesh_flush_select_from_polys(Mesh *me) -{ - BKE_mesh_flush_select_from_polys_ex(me->verts_for_write().data(), - me->totvert, - me->loops().data(), - me->edges_for_write().data(), - me->totedge, - me->polys().data(), - me->totpoly); + select_vert.finish(); + select_edge.finish(); } -static void mesh_flush_select_from_verts(const Span<MVert> verts, +static void mesh_flush_select_from_verts(const Span<MEdge> edges, + const Span<MPoly> polys, const Span<MLoop> loops, const VArray<bool> &hide_edge, const VArray<bool> &hide_poly, - MutableSpan<MEdge> edges, - MutableSpan<MPoly> polys) + const VArray<bool> &select_vert, + MutableSpan<bool> select_edge, + MutableSpan<bool> select_poly) { + /* Select visible edges that have both of their vertices selected. */ for (const int i : edges.index_range()) { if (!hide_edge[i]) { - MEdge &edge = edges[i]; - if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) { - edge.flag |= SELECT; - } - else { - edge.flag &= ~SELECT; - } + const MEdge &edge = edges[i]; + select_edge[i] = select_vert[edge.v1] && select_vert[edge.v2]; } } + /* Select visible faces that have all of their vertices selected. */ for (const int i : polys.index_range()) { - if (hide_poly[i]) { - continue; - } - MPoly &poly = polys[i]; - bool all_verts_selected = true; - for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { - if (!(verts[loop.v].flag & SELECT)) { - all_verts_selected = false; - } - } - if (all_verts_selected) { - poly.flag |= ME_FACE_SEL; - } - else { - poly.flag &= (char)~ME_FACE_SEL; + if (!hide_poly[i]) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + select_poly[i] = std::all_of(poly_loops.begin(), poly_loops.end(), [&](const MLoop &loop) { + return select_vert[loop.v]; + }); } } } void BKE_mesh_flush_select_from_verts(Mesh *me) { - const blender::bke::AttributeAccessor attributes = me->attributes(); + using namespace blender::bke; + MutableAttributeAccessor attributes = me->attributes_for_write(); + const VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + if (select_vert.is_single() && !select_vert.get_internal_single()) { + attributes.remove(".select_edge"); + attributes.remove(".select_poly"); + return; + } + SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_edge", ATTR_DOMAIN_EDGE); + SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_poly", ATTR_DOMAIN_FACE); mesh_flush_select_from_verts( - me->verts(), + me->edges(), + me->polys(), me->loops(), attributes.lookup_or_default<bool>(".hide_edge", ATTR_DOMAIN_EDGE, false), attributes.lookup_or_default<bool>(".hide_poly", ATTR_DOMAIN_FACE, false), - me->edges_for_write(), - me->polys_for_write()); + select_vert, + select_edge.span, + select_poly.span); + select_edge.finish(); + select_poly.finish(); } /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 627c0057a28..2c75c477b9a 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -13,6 +13,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_types.h" #include "BLI_edgehash.h" #include "BLI_math.h" @@ -918,6 +919,35 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Face Set Conversion + * \{ */ + +void BKE_mesh_legacy_face_set_from_generic(Mesh *mesh) +{ + using namespace blender; + for (CustomDataLayer &layer : MutableSpan(mesh->pdata.layers, mesh->pdata.totlayer)) { + if (StringRef(layer.name) == ".sculpt_face_set") { + layer.type = CD_SCULPT_FACE_SETS; + } + } + CustomData_update_typemap(&mesh->pdata); +} + +void BKE_mesh_legacy_face_set_to_generic(Mesh *mesh) +{ + using namespace blender; + for (CustomDataLayer &layer : MutableSpan(mesh->pdata.layers, mesh->pdata.totlayer)) { + if (layer.type == CD_SCULPT_FACE_SETS) { + BLI_strncpy(layer.name, ".sculpt_face_set", sizeof(layer.name)); + layer.type = CD_PROP_INT32; + } + } + CustomData_update_typemap(&mesh->pdata); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Bevel Weight Conversion * \{ */ @@ -979,6 +1009,44 @@ void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Edge Crease Conversion + * \{ */ + +void BKE_mesh_legacy_edge_crease_from_layers(Mesh *mesh) +{ + using namespace blender; + MutableSpan<MEdge> edges = mesh->edges_for_write(); + if (const float *creases = static_cast<const float *>( + CustomData_get_layer(&mesh->edata, CD_CREASE))) { + mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; + for (const int i : edges.index_range()) { + edges[i].crease_legacy = std::clamp(creases[i], 0.0f, 1.0f) * 255.0f; + } + } + else { + mesh->cd_flag &= ~ME_CDFLAG_EDGE_CREASE; + for (const int i : edges.index_range()) { + edges[i].crease_legacy = 0; + } + } +} + +void BKE_mesh_legacy_edge_crease_to_layers(Mesh *mesh) +{ + using namespace blender; + const Span<MEdge> edges = mesh->edges(); + if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) { + float *creases = static_cast<float *>( + CustomData_add_layer(&mesh->edata, CD_CREASE, CD_CONSTRUCT, nullptr, edges.size())); + for (const int i : edges.index_range()) { + creases[i] = edges[i].crease_legacy / 255.0f; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Hide Attribute and Legacy Flag Conversion * \{ */ @@ -993,7 +1061,7 @@ void BKE_mesh_legacy_convert_hide_layers_to_flags(Mesh *mesh) ".hide_vert", ATTR_DOMAIN_POINT, false); threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { - SET_FLAG_FROM_TEST(verts[i].flag, hide_vert[i], ME_HIDE); + SET_FLAG_FROM_TEST(verts[i].flag_legacy, hide_vert[i], ME_HIDE); } }); @@ -1023,13 +1091,14 @@ void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh) MutableAttributeAccessor attributes = mesh->attributes_for_write(); const Span<MVert> verts = mesh->verts(); - if (std::any_of( - verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag & ME_HIDE; })) { + if (std::any_of(verts.begin(), verts.end(), [](const MVert &vert) { + return vert.flag_legacy & ME_HIDE; + })) { SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>( ".hide_vert", ATTR_DOMAIN_POINT); threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { - hide_vert.span[i] = verts[i].flag & ME_HIDE; + hide_vert.span[i] = verts[i].flag_legacy & ME_HIDE; } }); hide_vert.finish(); @@ -1063,6 +1132,7 @@ void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh) } /** \} */ + /* -------------------------------------------------------------------- */ /** \name Material Index Conversion * \{ */ @@ -1102,3 +1172,90 @@ void BKE_mesh_legacy_convert_mpoly_to_material_indices(Mesh *mesh) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Selection Attribute and Legacy Flag Conversion + * \{ */ + +void BKE_mesh_legacy_convert_selection_layers_to_flags(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + const AttributeAccessor attributes = mesh->attributes(); + + MutableSpan<MVert> verts = mesh->verts_for_write(); + const VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(verts[i].flag_legacy, select_vert[i], SELECT); + } + }); + + MutableSpan<MEdge> edges = mesh->edges_for_write(); + const VArray<bool> select_edge = attributes.lookup_or_default<bool>( + ".select_edge", ATTR_DOMAIN_EDGE, false); + threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(edges[i].flag, select_edge[i], SELECT); + } + }); + + MutableSpan<MPoly> polys = mesh->polys_for_write(); + const VArray<bool> select_poly = attributes.lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(polys[i].flag, select_poly[i], ME_FACE_SEL); + } + }); +} + +void BKE_mesh_legacy_convert_flags_to_selection_layers(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh->attributes_for_write(); + + const Span<MVert> verts = mesh->verts(); + if (std::any_of(verts.begin(), verts.end(), [](const MVert &vert) { + return vert.flag_legacy & SELECT; + })) { + SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_vert", ATTR_DOMAIN_POINT); + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + select_vert.span[i] = (verts[i].flag_legacy & SELECT) != 0; + } + }); + select_vert.finish(); + } + + const Span<MEdge> edges = mesh->edges(); + if (std::any_of( + edges.begin(), edges.end(), [](const MEdge &edge) { return edge.flag & SELECT; })) { + SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_edge", ATTR_DOMAIN_EDGE); + threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + select_edge.span[i] = (edges[i].flag & SELECT) != 0; + } + }); + select_edge.finish(); + } + + const Span<MPoly> polys = mesh->polys(); + if (std::any_of( + polys.begin(), polys.end(), [](const MPoly &poly) { return poly.flag & ME_FACE_SEL; })) { + SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_only_span<bool>( + ".select_poly", ATTR_DOMAIN_FACE); + threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + select_poly.span[i] = (polys[i].flag & ME_FACE_SEL) != 0; + } + }); + select_poly.finish(); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.cc index f57effd49f4..b612564ef09 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.cc @@ -12,6 +12,7 @@ #include "DNA_meshdata_types.h" #include "DNA_vec_types.h" +#include "BLI_array.hh" #include "BLI_bitmap.h" #include "BLI_buffer.h" #include "BLI_math.h" @@ -30,6 +31,7 @@ /* ngon version wip, based on BM_uv_vert_map_create */ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, const bool *hide_poly, + const bool *select_poly, const MLoop *mloop, const MLoopUV *mloopuv, uint totpoly, @@ -52,7 +54,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, /* generate UvMapVert array */ mp = mpoly; for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { + if (!selected || (!(hide_poly && hide_poly[a]) && (select_poly && select_poly[a]))) { totuv += mp->totloop; } } @@ -65,7 +67,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, buf = vmap->buf = (UvMapVert *)MEM_callocN(sizeof(*vmap->buf) * (size_t)totuv, "UvMapVert"); vmap->vert = (UvMapVert **)MEM_callocN(sizeof(*vmap->vert) * totvert, "UvMapVert*"); if (use_winding) { - winding = MEM_callocN(sizeof(*winding) * totpoly, "winding"); + winding = static_cast<bool *>(MEM_callocN(sizeof(*winding) * totpoly, "winding")); } if (!vmap->vert || !vmap->buf) { @@ -75,7 +77,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, mp = mpoly; for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { + if (!selected || (!(hide_poly && hide_poly[a]) && (select_poly && select_poly[a]))) { float(*tf_uv)[2] = NULL; if (use_winding) { @@ -194,11 +196,12 @@ static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map, int totloop, const bool do_loops) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__); int *indices, *index_iter; int i, j; - indices = index_iter = MEM_mallocN(sizeof(int) * (size_t)totloop, __func__); + indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totloop, __func__)); + index_iter = indices; /* Count number of polys for each vertex */ for (i = 0; i < totpoly; i++) { @@ -265,8 +268,8 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, const MLoop *mloop, const int UNUSED(totloop)) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__); - int *indices = MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__)); int *index_step; const MLoopTri *mlt; int i; @@ -303,8 +306,8 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, void BKE_mesh_vert_edge_map_create( MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, "vert-edge map"); - int *indices = MEM_mallocN(sizeof(int[2]) * (size_t)totedge, "vert-edge map mem"); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int[2]) * (size_t)totedge, __func__)); int *i_pt = indices; int i; @@ -342,8 +345,8 @@ void BKE_mesh_vert_edge_map_create( void BKE_mesh_vert_edge_vert_map_create( MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, "vert-edge map"); - int *indices = MEM_mallocN(sizeof(int[2]) * (size_t)totedge, "vert-edge map mem"); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int[2]) * (size_t)totedge, __func__)); int *i_pt = indices; int i; @@ -387,8 +390,8 @@ void BKE_mesh_edge_loop_map_create(MeshElemMap **r_map, const MLoop *mloop, const int totloop) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totedge, "edge-poly map"); - int *indices = MEM_mallocN(sizeof(int) * (size_t)totloop * 2, "edge-poly map mem"); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totedge, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totloop * 2, __func__)); int *index_step; const MPoly *mp; int i; @@ -440,8 +443,8 @@ void BKE_mesh_edge_poly_map_create(MeshElemMap **r_map, const MLoop *mloop, const int totloop) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totedge, "edge-poly map"); - int *indices = MEM_mallocN(sizeof(int) * (size_t)totloop, "edge-poly map mem"); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totedge, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totloop, __func__)); int *index_step; const MPoly *mp; int i; @@ -485,8 +488,8 @@ void BKE_mesh_origindex_map_create(MeshElemMap **r_map, const int *final_origindex, const int totfinal) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totsource, "poly-tessface map"); - int *indices = MEM_mallocN(sizeof(int) * (size_t)totfinal, "poly-tessface map mem"); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totsource, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totfinal, __func__)); int *index_step; int i; @@ -527,8 +530,8 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map, const MLoopTri *looptri, const int looptri_num) { - MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)mpoly_num, "poly-tessface map"); - int *indices = MEM_mallocN(sizeof(int) * (size_t)looptri_num, "poly-tessface map mem"); + MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)mpoly_num, __func__); + int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)looptri_num, __func__)); int *index_step; int i; @@ -559,13 +562,13 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map, /** * Callback deciding whether the given poly/loop/edge define an island boundary or not. */ -typedef bool (*MeshRemap_CheckIslandBoundary)(const struct MPoly *mpoly, - const struct MLoop *mloop, - const struct MEdge *medge, - const int edge_user_count, - const struct MPoly *mpoly_array, - const struct MeshElemMap *edge_poly_map, - void *user_data); +using MeshRemap_CheckIslandBoundary = bool (*)(const MPoly *mpoly, + const MLoop *mloop, + const MEdge *medge, + const int edge_user_count, + const MPoly *mpoly_array, + const MeshElemMap *edge_poly_map, + void *user_data); static void poly_edge_loop_islands_calc(const MEdge *medge, const int totedge, @@ -620,8 +623,8 @@ static void poly_edge_loop_islands_calc(const MEdge *medge, &edge_poly_map, &edge_poly_mem, medge, totedge, mpoly, totpoly, mloop, totloop); } - poly_groups = MEM_callocN(sizeof(int) * (size_t)totpoly, __func__); - poly_stack = MEM_mallocN(sizeof(int) * (size_t)totpoly, __func__); + poly_groups = static_cast<int *>(MEM_callocN(sizeof(int) * (size_t)totpoly, __func__)); + poly_stack = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totpoly, __func__)); while (true) { int poly; @@ -834,17 +837,17 @@ void BKE_mesh_loop_islands_init(MeshIslandStore *island_store, island_store->item_type = item_type; island_store->items_to_islands_num = items_num; - island_store->items_to_islands = BLI_memarena_alloc( - mem, sizeof(*island_store->items_to_islands) * (size_t)items_num); + island_store->items_to_islands = static_cast<int *>( + BLI_memarena_alloc(mem, sizeof(*island_store->items_to_islands) * (size_t)items_num)); island_store->island_type = island_type; island_store->islands_num_alloc = MISLAND_DEFAULT_BUFSIZE; - island_store->islands = BLI_memarena_alloc( - mem, sizeof(*island_store->islands) * island_store->islands_num_alloc); + island_store->islands = static_cast<MeshElemMap **>( + BLI_memarena_alloc(mem, sizeof(*island_store->islands) * island_store->islands_num_alloc)); island_store->innercut_type = innercut_type; - island_store->innercuts = BLI_memarena_alloc( - mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc); + island_store->innercuts = static_cast<MeshElemMap **>( + BLI_memarena_alloc(mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc)); } void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store) @@ -898,24 +901,29 @@ void BKE_mesh_loop_islands_add(MeshIslandStore *island_store, MeshElemMap **islds, **innrcuts; island_store->islands_num_alloc *= 2; - islds = BLI_memarena_alloc(mem, sizeof(*islds) * island_store->islands_num_alloc); + islds = static_cast<MeshElemMap **>( + BLI_memarena_alloc(mem, sizeof(*islds) * island_store->islands_num_alloc)); memcpy(islds, island_store->islands, sizeof(*islds) * (curr_num_islands - 1)); island_store->islands = islds; - innrcuts = BLI_memarena_alloc(mem, sizeof(*innrcuts) * island_store->islands_num_alloc); + innrcuts = static_cast<MeshElemMap **>( + BLI_memarena_alloc(mem, sizeof(*innrcuts) * island_store->islands_num_alloc)); memcpy(innrcuts, island_store->innercuts, sizeof(*innrcuts) * (curr_num_islands - 1)); island_store->innercuts = innrcuts; } - island_store->islands[curr_island_idx] = isld = BLI_memarena_alloc(mem, sizeof(*isld)); + island_store->islands[curr_island_idx] = isld = static_cast<MeshElemMap *>( + BLI_memarena_alloc(mem, sizeof(*isld))); isld->count = num_island_items; - isld->indices = BLI_memarena_alloc(mem, sizeof(*isld->indices) * (size_t)num_island_items); + isld->indices = static_cast<int *>( + BLI_memarena_alloc(mem, sizeof(*isld->indices) * (size_t)num_island_items)); memcpy(isld->indices, island_item_indices, sizeof(*isld->indices) * (size_t)num_island_items); - island_store->innercuts[curr_island_idx] = innrcut = BLI_memarena_alloc(mem, sizeof(*innrcut)); + island_store->innercuts[curr_island_idx] = innrcut = static_cast<MeshElemMap *>( + BLI_memarena_alloc(mem, sizeof(*innrcut))); innrcut->count = num_innercut_items; - innrcut->indices = BLI_memarena_alloc(mem, - sizeof(*innrcut->indices) * (size_t)num_innercut_items); + innrcut->indices = static_cast<int *>( + BLI_memarena_alloc(mem, sizeof(*innrcut->indices) * (size_t)num_innercut_items)); memcpy(innrcut->indices, innercut_item_indices, sizeof(*innrcut->indices) * (size_t)num_innercut_items); @@ -927,11 +935,11 @@ void BKE_mesh_loop_islands_add(MeshIslandStore *island_store, * Would make things much more complex though, * and each UVMap would then need its own mesh mapping, not sure we want that at all! */ -typedef struct MeshCheckIslandBoundaryUv { +struct MeshCheckIslandBoundaryUv { const MLoop *loops; const MLoopUV *luvs; const MeshElemMap *edge_loop_map; -} MeshCheckIslandBoundaryUv; +}; static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp), const MLoop *ml, @@ -942,7 +950,8 @@ static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp), void *user_data) { if (user_data) { - const MeshCheckIslandBoundaryUv *data = user_data; + const MeshCheckIslandBoundaryUv *data = static_cast<const MeshCheckIslandBoundaryUv *>( + user_data); const MLoop *loops = data->loops; const MLoopUV *luvs = data->luvs; const MeshElemMap *edge_to_loops = &data->edge_loop_map[ml->e]; @@ -1056,13 +1065,16 @@ static bool mesh_calc_islands_loop_poly_uv(const MVert *UNUSED(verts), } if (num_edge_borders) { - edge_border_count = MEM_mallocN(sizeof(*edge_border_count) * (size_t)totedge, __func__); - edge_innercut_indices = MEM_mallocN(sizeof(*edge_innercut_indices) * (size_t)num_edge_borders, - __func__); + edge_border_count = static_cast<char *>( + MEM_mallocN(sizeof(*edge_border_count) * (size_t)totedge, __func__)); + edge_innercut_indices = static_cast<int *>( + MEM_mallocN(sizeof(*edge_innercut_indices) * (size_t)num_edge_borders, __func__)); } - poly_indices = MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__); - loop_indices = MEM_mallocN(sizeof(*loop_indices) * (size_t)totloop, __func__); + poly_indices = static_cast<int *>( + MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__)); + loop_indices = static_cast<int *>( + MEM_mallocN(sizeof(*loop_indices) * (size_t)totloop, __func__)); /* NOTE: here we ignore '0' invalid group - this should *never* happen in this case anyway? */ for (grp_idx = 1; grp_idx <= num_poly_groups; grp_idx++) { diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index a77879fb573..3c492e2e167 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -24,6 +24,7 @@ #include "DNA_meshdata_types.h" #include "BKE_attribute.h" +#include "BKE_attribute.hh" #include "BKE_bvhutils.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" @@ -193,8 +194,7 @@ static openvdb::FloatGrid::Ptr remesh_voxel_level_set_create(const Mesh *mesh, { const Span<MVert> verts = mesh->verts(); const Span<MLoop> loops = mesh->loops(); - Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh), - BKE_mesh_runtime_looptri_len(mesh)}; + const Span<MLoopTri> looptris = mesh->looptris(); std::vector<openvdb::Vec3s> points(mesh->totvert); std::vector<openvdb::Vec3I> triangles(looptris.size()); @@ -318,24 +318,28 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, const Mesh *source) void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, const Mesh *source) { + using namespace blender; + using namespace blender::bke; + const AttributeAccessor src_attributes = source->attributes(); + MutableAttributeAccessor dst_attributes = target->attributes_for_write(); const MPoly *target_polys = (const MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY); const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT); const MLoop *target_loops = (const MLoop *)CustomData_get_layer(&target->ldata, CD_MLOOP); - const int *source_face_sets = (const int *)CustomData_get_layer(&source->pdata, - CD_SCULPT_FACE_SETS); - if (source_face_sets == nullptr) { - return; - } - int *target_face_sets; - if (CustomData_has_layer(&target->pdata, CD_SCULPT_FACE_SETS)) { - target_face_sets = (int *)CustomData_get_layer(&target->pdata, CD_SCULPT_FACE_SETS); + const VArray<int> src_face_sets = src_attributes.lookup<int>(".sculpt_face_set", + ATTR_DOMAIN_FACE); + if (!src_face_sets) { + return; } - else { - target_face_sets = (int *)CustomData_add_layer( - &target->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, target->totpoly); + SpanAttributeWriter<int> dst_face_sets = dst_attributes.lookup_or_add_for_write_only_span<int>( + ".sculpt_face_set", ATTR_DOMAIN_FACE); + if (!dst_face_sets) { + return; } + const VArraySpan<int> src(src_face_sets); + MutableSpan<int> dst = dst_face_sets.span; + const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(source); BVHTreeFromMesh bvhtree = {nullptr}; BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_LOOPTRI, 2); @@ -349,13 +353,14 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, const Mesh *source) BKE_mesh_calc_poly_center(mpoly, &target_loops[mpoly->loopstart], target_verts, from_co); BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree); if (nearest.index != -1) { - target_face_sets[i] = source_face_sets[looptri[nearest.index].poly]; + dst[i] = src[looptri[nearest.index].poly]; } else { - target_face_sets[i] = 1; + dst[i] = 1; } } free_bvhtree_from_mesh(&bvhtree); + dst_face_sets.finish(); } void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc index a66f2a714e7..062d6cdb952 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.cc +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -112,6 +112,13 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) BKE_mesh_clear_derived_normals(mesh); } +blender::Span<MLoopTri> Mesh::looptris() const +{ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(this); + const int num_looptris = BKE_mesh_runtime_looptri_len(this); + return {looptris, num_looptris}; +} + /** * Ensure the array is large enough * @@ -154,12 +161,23 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh) const Span<MPoly> polys = mesh->polys(); const Span<MLoop> loops = mesh->loops(); - BKE_mesh_recalc_looptri(loops.data(), - polys.data(), - verts.data(), - mesh->totloop, - mesh->totpoly, - mesh->runtime.looptris.array_wip); + if (!BKE_mesh_poly_normals_are_dirty(mesh)) { + BKE_mesh_recalc_looptri_with_normals(loops.data(), + polys.data(), + verts.data(), + mesh->totloop, + mesh->totpoly, + mesh->runtime.looptris.array_wip, + BKE_mesh_poly_normals_ensure(mesh)); + } + else { + BKE_mesh_recalc_looptri(loops.data(), + polys.data(), + verts.data(), + mesh->totloop, + mesh->totpoly, + mesh->runtime.looptris.array_wip); + } BLI_assert(mesh->runtime.looptris.array == nullptr); atomic_cas_ptr((void **)&mesh->runtime.looptris.array, @@ -280,10 +298,10 @@ void BKE_mesh_tag_coords_changed_uniformly(Mesh *mesh) BKE_mesh_tag_coords_changed(mesh); /* The normals didn't change, since all verts moved by the same amount. */ if (!vert_normals_were_dirty) { - BKE_mesh_poly_normals_clear_dirty(mesh); + BKE_mesh_vertex_normals_clear_dirty(mesh); } if (!poly_normals_were_dirty) { - BKE_mesh_vertex_normals_clear_dirty(mesh); + BKE_mesh_poly_normals_clear_dirty(mesh); } } diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index 1ddac19304d..ed7ae8113a7 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -22,8 +22,7 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const MutableSpan<T> dst) { const Span<MLoop> loops = mesh.loops(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MLoopTri> looptris = mesh.looptris(); for (const int i : mask) { const int looptri_index = looptri_indices[i]; @@ -69,8 +68,7 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const IndexMask mask, const MutableSpan<T> dst) { - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MLoopTri> looptris = mesh.looptris(); for (const int i : mask) { const int looptri_index = looptri_indices[i]; @@ -115,8 +113,7 @@ void sample_face_attribute(const Mesh &mesh, const IndexMask mask, const MutableSpan<T> dst) { - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MLoopTri> looptris = mesh.looptris(); for (const int i : mask) { const int looptri_index = looptri_indices[i]; @@ -161,8 +158,7 @@ Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords() const Span<MVert> verts = mesh_->verts(); const Span<MLoop> loops = mesh_->loops(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_), - BKE_mesh_runtime_looptri_len(mesh_)}; + const Span<MLoopTri> looptris = mesh_->looptris(); for (const int i : mask_) { const int looptri_index = looptri_indices_[i]; @@ -191,8 +187,7 @@ Span<float3> MeshAttributeInterpolator::ensure_nearest_weights() const Span<MVert> verts = mesh_->verts(); const Span<MLoop> loops = mesh_->loops(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_), - BKE_mesh_runtime_looptri_len(mesh_)}; + const Span<MLoopTri> looptris = mesh_->looptris(); for (const int i : mask_) { const int looptri_index = looptri_indices_[i]; @@ -265,8 +260,7 @@ int sample_surface_points_spherical(RandomNumberGenerator &rng, { const Span<MVert> verts = mesh.verts(); const Span<MLoop> loops = mesh.loops(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MLoopTri> looptris = mesh.looptris(); const float sample_radius_sq = pow2f(sample_radius); const float sample_plane_area = M_PI * sample_radius_sq; @@ -363,8 +357,7 @@ int sample_surface_points_projected( { const Span<MVert> verts = mesh.verts(); const Span<MLoop> loops = mesh.loops(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MLoopTri> looptris = mesh.looptris(); int point_count = 0; for ([[maybe_unused]] const int _ : IndexRange(tries_num)) { diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 7dc7e6009d9..ce61ca483e9 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -62,7 +62,7 @@ typedef enum { static void multiresModifier_disp_run( DerivedMesh *dm, Mesh *me, DerivedMesh *dm2, DispOp op, CCGElem **oldGridData, int totlvl); -/** Customdata */ +/** Custom-data. */ void multires_customdata_delete(Mesh *me) { diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h index 5e2822ad5df..f27618b2145 100644 --- a/source/blender/blenkernel/intern/multires_reshape.h +++ b/source/blender/blenkernel/intern/multires_reshape.h @@ -101,6 +101,8 @@ typedef struct MultiresReshapeContext { /* Vertex crease custom data layer, null if none is present. */ const float *cd_vertex_crease; + /* Edge crease custom data layer, null if none is present. */ + const float *cd_edge_crease; } MultiresReshapeContext; /** diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index e887f543dc5..1463404069f 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -482,13 +482,14 @@ static bool is_crease_supported(const MultiresReshapeSmoothContext *reshape_smoo /* Get crease which will be used for communication to OpenSubdiv topology. * Note that simple subdivision treats all base edges as infinitely sharp. */ -static char get_effective_crease_char(const MultiresReshapeSmoothContext *reshape_smooth_context, - const MEdge *base_edge) +static float get_effective_crease(const MultiresReshapeSmoothContext *reshape_smooth_context, + const int base_edge_index) { if (!is_crease_supported(reshape_smooth_context)) { return 255; } - return base_edge->crease; + const float *creases = reshape_smooth_context->reshape_context->cd_vertex_crease; + return creases ? creases[base_edge_index] : 0.0f; } static float get_effective_crease_float(const MultiresReshapeSmoothContext *reshape_smooth_context, @@ -812,7 +813,6 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context, const int subdiv_v2) { MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; - const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) { if (!is_loose) { @@ -832,8 +832,7 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context, return; } /* Edges without crease are to be ignored as well. */ - const MEdge *base_edge = &reshape_context->base_edges[coarse_edge_index]; - const char crease = get_effective_crease_char(reshape_smooth_context, base_edge); + const char crease = get_effective_crease(reshape_smooth_context, coarse_edge_index); if (crease == 0) { return; } @@ -846,7 +845,6 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap const Mesh *base_mesh = reshape_context->base_mesh; const MPoly *base_mpoly = reshape_context->base_polys; const MLoop *base_mloop = reshape_context->base_loops; - const MEdge *base_edge = reshape_context->base_edges; reshape_smooth_context->non_loose_base_edge_map = BLI_BITMAP_NEW(base_mesh->totedge, "non_loose_base_edge_map"); @@ -859,8 +857,8 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, loop->e)) { BLI_BITMAP_ENABLE(reshape_smooth_context->non_loose_base_edge_map, loop->e); - const char crease = get_effective_crease_char(reshape_smooth_context, &base_edge[loop->e]); - if (crease != 0) { + const float crease = get_effective_crease(reshape_smooth_context, loop->e); + if (crease > 0.0f) { ++num_used_edges; } } diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index 5b60394feda..4fc1217158c 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -206,6 +206,7 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level); reshape_context->cd_vertex_crease = CustomData_get_layer(&base_mesh->vdata, CD_CREASE); + reshape_context->cd_edge_crease = CustomData_get_layer(&base_mesh->edata, CD_CREASE); context_init_commoon(reshape_context); @@ -271,6 +272,8 @@ bool multires_reshape_context_create_from_subdiv(MultiresReshapeContext *reshape reshape_context->base_edges = BKE_mesh_edges(base_mesh); reshape_context->base_polys = BKE_mesh_polys(base_mesh); reshape_context->base_loops = BKE_mesh_loops(base_mesh); + reshape_context->cd_vertex_crease = (const float *)CustomData_get_layer(&base_mesh->edata, + CD_CREASE); reshape_context->subdiv = subdiv; reshape_context->need_free_subdiv = false; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index da508ff865c..c0315dcc848 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -2072,7 +2072,7 @@ bool BKE_nla_tweakmode_enter(AnimData *adt) /* handle AnimData level changes: * - 'real' active action to temp storage (no need to change user-counts). - * - Action of active strip set to be the 'active action', and have its usercount incremented. + * - Action of active strip set to be the 'active action', and have its user-count incremented. * - Editing-flag for this AnimData block should also get turned on * (for more efficient restoring). * - Take note of the active strip for mapping-correction of keyframes @@ -2136,7 +2136,8 @@ void BKE_nla_tweakmode_exit(AnimData *adt) } /* handle AnimData level changes: - * - 'temporary' active action needs its usercount decreased, since we're removing this reference + * - 'temporary' active action needs its user-count decreased, + * since we're removing this reference * - 'real' active action is restored from storage * - storage pointer gets cleared (to avoid having bad notes hanging around) * - editing-flag for this AnimData block should also get turned off diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index b82cf30416a..1c7b1b9fa3e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -47,6 +47,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" +#include "BKE_asset.h" #include "BKE_bpath.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -54,6 +55,7 @@ #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_idprop.h" +#include "BKE_idprop.hh" #include "BKE_idtype.h" #include "BKE_image_format.h" #include "BKE_lib_id.h" @@ -132,7 +134,7 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c bNodeTree *ntree_dst = (bNodeTree *)id_dst; const bNodeTree *ntree_src = (const bNodeTree *)id_src; - /* We never handle usercount here for own data. */ + /* We never handle user-count here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; ntree_dst->runtime = MEM_new<bNodeTreeRuntime>(__func__); @@ -1026,6 +1028,33 @@ static void ntree_blend_read_expand(BlendExpander *expander, ID *id) ntreeBlendReadExpand(expander, ntree); } +namespace blender::bke { + +static void node_tree_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data) +{ + bNodeTree &node_tree = *static_cast<bNodeTree *>(asset_ptr); + + BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release()); + auto inputs = idprop::create_group("inputs"); + auto outputs = idprop::create_group("outputs"); + LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.inputs) { + auto property = idprop::create(socket->name, socket->typeinfo->idname); + IDP_AddToGroup(inputs.get(), property.release()); + } + LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.outputs) { + auto property = idprop::create(socket->name, socket->typeinfo->idname); + IDP_AddToGroup(outputs.get(), property.release()); + } + BKE_asset_metadata_idprop_ensure(asset_data, inputs.release()); + BKE_asset_metadata_idprop_ensure(asset_data, outputs.release()); +} + +} // namespace blender::bke + +static AssetTypeInfo AssetType_NT = { + /* pre_save_fn */ blender::bke::node_tree_asset_pre_save, +}; + IDTypeInfo IDType_ID_NT = { /* id_code */ ID_NT, /* id_filter */ FILTER_ID_NT, @@ -1035,7 +1064,7 @@ IDTypeInfo IDType_ID_NT = { /* name_plural */ "node_groups", /* translation_context */ BLT_I18NCONTEXT_ID_NODETREE, /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, - /* asset_type_info */ nullptr, + /* asset_type_info */ &AssetType_NT, /* init_data */ ntree_init_data, /* copy_data */ ntree_copy_data, @@ -2149,7 +2178,7 @@ bNode *nodeAddNode(const struct bContext *C, bNodeTree *ntree, const char *idnam BKE_ntree_update_tag_node_new(ntree, node); - if (node->type == GEO_NODE_INPUT_SCENE_TIME) { + if (ELEM(node->type, GEO_NODE_INPUT_SCENE_TIME, GEO_NODE_SELF_OBJECT)) { DEG_relations_tag_update(CTX_data_main(C)); } @@ -2992,7 +3021,7 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) /* Also update relations for the scene time node, which causes a dependency * on time that users expect to be removed when the node is removed. */ - if (node_has_id || node->type == GEO_NODE_INPUT_SCENE_TIME) { + if (node_has_id || ELEM(node->type, GEO_NODE_INPUT_SCENE_TIME, GEO_NODE_SELF_OBJECT)) { if (bmain != nullptr) { DEG_relations_tag_update(bmain); } @@ -4702,6 +4731,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_trim(); register_node_type_geo_deform_curves_on_surface(); register_node_type_geo_delete_geometry(); + register_node_type_geo_distribute_points_in_volume(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_dual_mesh(); register_node_type_geo_duplicate_elements(); @@ -4748,6 +4778,7 @@ static void registerGeometryNodes() register_node_type_geo_material_replace(); register_node_type_geo_material_selection(); register_node_type_geo_merge_by_distance(); + register_node_type_geo_mesh_face_set_boundaries(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); register_node_type_geo_mesh_primitive_cube(); @@ -4769,10 +4800,14 @@ static void registerGeometryNodes() register_node_type_geo_realize_instances(); register_node_type_geo_remove_attribute(); register_node_type_geo_rotate_instances(); + register_node_type_geo_sample_index(); + register_node_type_geo_sample_nearest_surface(); + register_node_type_geo_sample_nearest(); register_node_type_geo_scale_elements(); register_node_type_geo_scale_instances(); register_node_type_geo_separate_components(); register_node_type_geo_separate_geometry(); + register_node_type_geo_self_object(); register_node_type_geo_set_curve_handles(); register_node_type_geo_set_curve_radius(); register_node_type_geo_set_curve_tilt(); @@ -4789,7 +4824,6 @@ static void registerGeometryNodes() register_node_type_geo_string_to_curves(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); - register_node_type_geo_transfer_attribute(); register_node_type_geo_transform(); register_node_type_geo_translate_instances(); register_node_type_geo_triangulate(); diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 00b78284791..4fb0e423a33 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -14,16 +14,12 @@ namespace blender::bke::node_tree_runtime { -void handle_node_tree_output_changed(bNodeTree &tree_cow) +void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow) { - if (tree_cow.type == NTREE_GEOMETRY) { - /* Rebuild geometry nodes lazy function graph. */ - { - std::lock_guard lock{tree_cow.runtime->geometry_nodes_lazy_function_graph_info_mutex}; - tree_cow.runtime->geometry_nodes_lazy_function_graph_info.reset(); - } - blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow); - } + BLI_assert(tree_cow.type == NTREE_GEOMETRY); + /* Rebuild geometry nodes lazy function graph. */ + tree_cow.runtime->geometry_nodes_lazy_function_graph_info.reset(); + blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow); } static void double_checked_lock(std::mutex &mutex, bool &data_is_dirty, FunctionRef<void()> fn) @@ -127,15 +123,17 @@ static void update_directly_linked_links_and_sockets(const bNodeTree &ntree) socket->runtime->directly_linked_links.clear(); socket->runtime->directly_linked_sockets.clear(); } - node->runtime->has_linked_inputs = false; - node->runtime->has_linked_outputs = false; + node->runtime->has_available_linked_inputs = false; + node->runtime->has_available_linked_outputs = false; } for (bNodeLink *link : tree_runtime.links) { link->fromsock->runtime->directly_linked_links.append(link); link->fromsock->runtime->directly_linked_sockets.append(link->tosock); link->tosock->runtime->directly_linked_links.append(link); - link->fromnode->runtime->has_linked_outputs = true; - link->tonode->runtime->has_linked_inputs = true; + if (link->is_available()) { + link->fromnode->runtime->has_available_linked_outputs = true; + link->tonode->runtime->has_available_linked_inputs = true; + } } for (bNodeSocket *socket : tree_runtime.input_sockets) { if (socket->flag & SOCK_MULTI_INPUT) { @@ -172,7 +170,10 @@ static void find_logical_origins_for_socket_recursive( links_to_check = links_to_check.take_front(1); } for (bNodeLink *link : links_to_check) { - if (link->flag & NODE_LINK_MUTED) { + if (link->is_muted()) { + continue; + } + if (!link->is_available()) { continue; } bNodeSocket &origin_socket = *link->fromsock; @@ -289,14 +290,20 @@ static void toposort_from_start_node(const ToposortDirection direction, break; } bNodeSocket &socket = *sockets[item.socket_index]; - const Span<bNodeSocket *> linked_sockets = socket.runtime->directly_linked_sockets; - if (item.link_index == linked_sockets.size()) { + const Span<bNodeLink *> linked_links = socket.runtime->directly_linked_links; + if (item.link_index == linked_links.size()) { /* All links connected to this socket have already been visited. */ item.socket_index++; item.link_index = 0; continue; } - bNodeSocket &linked_socket = *linked_sockets[item.link_index]; + bNodeLink &link = *linked_links[item.link_index]; + if (!link.is_available()) { + /* Ignore unavailable links. */ + item.link_index++; + continue; + } + bNodeSocket &linked_socket = *socket.runtime->directly_linked_sockets[item.link_index]; bNode &linked_node = *linked_socket.runtime->owner_node; ToposortNodeState &linked_node_state = node_states[linked_node.runtime->index_in_tree]; if (linked_node_state.is_done) { @@ -341,8 +348,9 @@ static void update_toposort(const bNodeTree &ntree, /* Ignore nodes that are done already. */ continue; } - if ((direction == ToposortDirection::LeftToRight) ? node->runtime->has_linked_outputs : - node->runtime->has_linked_inputs) { + if ((direction == ToposortDirection::LeftToRight) ? + node->runtime->has_available_linked_outputs : + node->runtime->has_available_linked_inputs) { /* Ignore non-start nodes. */ continue; } @@ -402,7 +410,7 @@ static void ensure_topology_cache(const bNodeTree &ntree) update_toposort(ntree, ToposortDirection::LeftToRight, tree_runtime.toposort_left_to_right, - tree_runtime.has_link_cycle); + tree_runtime.has_available_link_cycle); }, [&]() { bool dummy; diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index b2caaa912d7..f9bab0959c9 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -1047,6 +1047,7 @@ class NodeTreeMainUpdater { void update_individual_nodes(bNodeTree &ntree) { + Vector<bNode *> group_inout_nodes; LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { nodeDeclarationEnsure(&ntree, node); if (this->should_update_individual_node(ntree, *node)) { @@ -1058,6 +1059,18 @@ class NodeTreeMainUpdater { ntype.updatefunc(&ntree, node); } } + if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { + group_inout_nodes.append(node); + } + } + /* The update function of group input/output nodes may add new interface sockets. When that + * happens, all the input/output nodes have to be updated again. In the future it would be + * better to move this functionality out of the node update function into the operator that's + * supposed to create the new interface socket. */ + if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) { + for (bNode *node : group_inout_nodes) { + node->typeinfo->updatefunc(&ntree, node); + } } } @@ -1375,7 +1388,7 @@ class NodeTreeMainUpdater { uint32_t get_combined_socket_topology_hash(const bNodeTree &tree, Span<const bNodeSocket *> sockets) { - if (tree.has_link_cycle()) { + if (tree.has_available_link_cycle()) { /* Return dummy value when the link has any cycles. The algorithm below could be improved to * handle cycles more gracefully. */ return 0; @@ -1391,7 +1404,7 @@ class NodeTreeMainUpdater { Array<uint32_t> get_socket_topology_hashes(const bNodeTree &tree, Span<const bNodeSocket *> sockets) { - BLI_assert(!tree.has_link_cycle()); + BLI_assert(!tree.has_available_link_cycle()); Array<std::optional<uint32_t>> hash_by_socket_id(tree.all_sockets().size()); Stack<const bNodeSocket *> sockets_to_check = sockets; diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index cbe5ea425fb..7ea6a4c597e 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -188,7 +188,7 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in /* Do not copy runtime data. */ BKE_object_runtime_reset_on_copy(ob_dst, flag); - /* We never handle usercount here for own data. */ + /* We never handle user-count here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; if (ob_src->totcol) { @@ -245,7 +245,7 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in BLI_listbase_clear(&ob_dst->modifiers); BLI_listbase_clear(&ob_dst->greasepencil_modifiers); - /* NOTE: Also takes care of softbody and particle systems copying. */ + /* NOTE: Also takes care of soft-body and particle systems copying. */ BKE_object_modifier_stack_copy(ob_dst, ob_src, true, flag_subdata); BLI_listbase_clear((ListBase *)&ob_dst->drawdata); @@ -396,7 +396,7 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], IDWALK_CB_USER); } - /* Note that ob->gpd is deprecated, so no need to handle it here. */ + /* Note that `ob->gpd` is deprecated, so no need to handle it here. */ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER); if (object->pd) { @@ -736,9 +736,9 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &sb->shared); if (sb->shared == nullptr) { /* Link deprecated caches if they exist, so we can use them for versioning. - * We should only do this when sb->shared == nullptr, because those pointers + * We should only do this when `sb->shared == nullptr`, because those pointers * are always set (for compatibility with older Blenders). We mustn't link - * the same pointcache twice. */ + * the same point-cache twice. */ BKE_ptcache_blend_read_data(reader, &sb->ptcaches, &sb->pointcache, false); } else { @@ -1141,7 +1141,7 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) * This code is a workaround this to check all point-caches from both source and destination * objects in parallel, and transfer those flags when it makes sense. * - * This allows to keep baked caches across liboverrides applies. + * This allows to keep baked caches across lib-overrides applies. * * NOTE: This is fairly hackish and weak, but so is the point-cache system as its whole. A more * robust solution would be e.g. to have a specific RNA entry point to deal with such cases @@ -1170,7 +1170,7 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) point_cache_dst != nullptr; point_cache_dst = point_cache_dst->next, point_cache_src = (point_cache_src != nullptr) ? point_cache_src->next : nullptr) { - /* Always force updating info about caches of applied liboverrides. */ + /* Always force updating info about caches of applied lib-overrides. */ point_cache_dst->flag |= PTCACHE_FLAG_INFO_DIRTY; if (point_cache_src == nullptr || !STREQ(point_cache_dst->name, point_cache_src->name)) { continue; @@ -1300,10 +1300,10 @@ void BKE_object_free_modifiers(Object *ob, const int flag) while ((gp_md = (GpencilModifierData *)BLI_pophead(&ob->greasepencil_modifiers))) { BKE_gpencil_modifier_free_ex(gp_md, flag); } - /* particle modifiers were freed, so free the particlesystems as well */ + /* Particle modifiers were freed, so free the particle-systems as well. */ BKE_object_free_particlesystems(ob); - /* same for softbody */ + /* Same for soft-body */ BKE_object_free_softbody(ob); /* modifiers may have stored data in the DM cache */ @@ -1448,11 +1448,11 @@ static bool object_modifier_type_copy_check(ModifierType md_type) } /** - * Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings ID), - * or add one, and return valid `psys` from `ob_dst`. + * Find a `psys` matching given `psys_src` in `ob_dst` + * (i.e. sharing the same #ParticleSettings ID), or add one, and return valid `psys` from `ob_dst`. * * \note Order handling is fairly weak here. This code assumes that it is called **before** the - * modifier using the psys is actually copied, and that this copied modifier will be added at the + * modifier using the `psys` is actually copied, and that this copied modifier will be added at the * end of the stack. That way we can be sure that the particle modifier will be before the one * using its particle system in the stack. */ @@ -2484,8 +2484,8 @@ void BKE_object_copy_particlesystems(Object *ob_dst, const Object *ob_src, const static void copy_object_pose(Object *obn, const Object *ob, const int flag) { - /* NOTE: need to clear obn->pose pointer first, - * so that BKE_pose_copy_data works (otherwise there's a crash) */ + /* NOTE: need to clear `obn->pose` pointer first, + * so that #BKE_pose_copy_data works (otherwise there's a crash) */ obn->pose = nullptr; BKE_pose_copy_data_ex(&obn->pose, ob->pose, flag, true); /* true = copy constraints */ @@ -2916,7 +2916,7 @@ void BKE_object_rot_to_mat3(const Object *ob, float mat[3][3], bool use_drot) axis_angle_to_mat3(dmat, ob->drotAxis, ob->drotAngle); } else { - /* quats are normalized before use to eliminate scaling issues */ + /* Quaternions are normalized before use to eliminate scaling issues. */ float tquat[4]; normalize_qt_qt(tquat, ob->quat); @@ -2952,7 +2952,7 @@ void BKE_object_mat3_to_rot(Object *ob, float mat[3][3], bool use_compat) float quat[4]; float dquat[4]; - /* without drot we could apply 'mat' directly */ + /* Without `drot` we could apply 'mat' directly. */ mat3_normalized_to_quat(quat, mat); axis_angle_to_quat(dquat, ob->drotAxis, ob->drotAngle); invert_qt_normalized(dquat); @@ -2965,12 +2965,12 @@ void BKE_object_mat3_to_rot(Object *ob, float mat[3][3], bool use_compat) float quat[4]; float dquat[4]; - /* without drot we could apply 'mat' directly */ + /* Without `drot` we could apply 'mat' directly. */ mat3_normalized_to_quat(quat, mat); eulO_to_quat(dquat, ob->drot, ob->rotmode); invert_qt_normalized(dquat); mul_qt_qtqt(quat, dquat, quat); - /* end drot correction */ + /* End `drot` correction. */ if (use_compat) { quat_to_compatible_eulO(ob->rot, ob->rot, ob->rotmode, quat); @@ -3131,12 +3131,11 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) return false; } - /* ctime is now a proper var setting of Curve which gets set by Animato like any other var + /* `ctime` is now a proper var setting of Curve which gets set by Animato like any other var * that's animated, but this will only work if it actually is animated. * * We divide the curve-time calculated in the previous step by the length of the path, - * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. - */ + * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */ if (cu->pathlen) { ctime = cu->ctime / cu->pathlen; } @@ -3415,7 +3414,7 @@ static void solve_parenting( mul_m4_m4m4(r_obmat, tmat, locmat); if (r_originmat) { - /* usable originmat */ + /* Usable `r_originmat`. */ copy_m3_m4(r_originmat, tmat); } @@ -4096,7 +4095,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, } else { Object temp_ob = blender::dna::shallow_copy(*dob->ob); - /* Do not modify the original boundbox. */ + /* Do not modify the original bounding-box. */ temp_ob.runtime.bb = nullptr; BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data); const BoundBox *bb = BKE_object_boundbox_get(&temp_ob); @@ -4149,7 +4148,7 @@ void BKE_object_foreach_display_point(Object *ob, void (*func_cb)(const float[3], void *), void *user_data) { - /* TODO: pointcloud and curves object support */ + /* TODO: point-cloud and curves object support. */ const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); float3 co; @@ -4188,10 +4187,11 @@ void BKE_scene_foreach_display_point(Depsgraph *depsgraph, void (*func_cb)(const float[3], void *), void *user_data) { - DEG_OBJECT_ITER_BEGIN (depsgraph, - ob, - DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | - DEG_ITER_OBJECT_FLAG_DUPLI) { + DEGObjectIterSettings deg_iter_settings{}; + deg_iter_settings.depsgraph = depsgraph; + deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | + DEG_ITER_OBJECT_FLAG_DUPLI; + DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) { if ((ob->base_flag & BASE_SELECTED) != 0) { BKE_object_foreach_display_point(ob, ob->obmat, func_cb, user_data); } @@ -4298,7 +4298,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, * is evaluated on the rebuilt pose, otherwise we get incorrect poses * on file load */ if (ob->pose == nullptr || (ob->pose->flag & POSE_RECALC)) { - /* No need to pass bmain here, we assume we do not need to rebuild DEG from here... */ + /* No need to pass `bmain` here, we assume we do not need to rebuild DEG from here. */ BKE_pose_rebuild(nullptr, ob, (bArmature *)ob->data, true); } } @@ -5449,8 +5449,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, depsgraph, scene, ob->track, false, recursion, frame, type); } - /* skip subframe if object is parented - * to vertex of a dynamic paint canvas */ + /* Skip sub-frame if object is parented to vertex of a dynamic paint canvas. */ if (no_update && (ELEM(ob->partype, PARVERT1, PARVERT3))) { return false; } @@ -5481,8 +5480,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, if (update_mesh) { BKE_animsys_evaluate_animdata( &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); - /* ignore cache clear during subframe updates - * to not mess up cache validity */ + /* Ignore cache clear during sub-frame updates to not mess up cache validity. */ object_cacheIgnoreClear(ob, 1); BKE_object_handle_update(depsgraph, scene, ob); object_cacheIgnoreClear(ob, 0); diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index 1fe5d7aa0e7..a72d68710ed 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -163,14 +163,14 @@ bool BKE_object_defgroup_clear(Object *ob, bDeformGroup *dg, const bool use_sele } else { if (BKE_mesh_deform_verts(me)) { - const MVert *mv; + const bool *select_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".select_vert"); int i; - mv = BKE_mesh_verts(me); dv = BKE_mesh_deform_verts_for_write(me); - for (i = 0; i < me->totvert; i++, mv++, dv++) { - if (dv->dw && (!use_selection || (mv->flag & SELECT))) { + for (i = 0; i < me->totvert; i++, dv++) { + if (dv->dw && (!use_selection || (select_vert && select_vert[i]))) { MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr); BKE_defvert_remove_group(dv, dw); /* dw can be NULL */ changed = true; diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 6db1c864918..dde3130a5b0 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -311,12 +311,13 @@ static void make_child_duplis(const DupliContext *ctx, /* FIXME: using a mere counter to generate a 'persistent' dupli id is very weak. One possible * better solution could be to use `session_uuid` of ID's instead? */ int persistent_dupli_id = 0; + DEGObjectIterSettings deg_iter_settings{}; + deg_iter_settings.depsgraph = ctx->depsgraph; /* NOTE: this set of flags ensure we only iterate over objects that have a base in either the * current scene, or the set (background) scene. */ - int deg_objects_visibility_flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | - DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET; - - DEG_OBJECT_ITER_BEGIN (ctx->depsgraph, ob, deg_objects_visibility_flags) { + deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET; + DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) { if ((ob != ctx->obedit) && is_child(ob, parent)) { DupliContext pctx; if (copy_dupli_context(&pctx, ctx, ctx->object, nullptr, persistent_dupli_id)) { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index ea1bd3c1cc3..171a7b41373 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -25,6 +25,7 @@ #include "BLI_hash.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_string_utf8.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -65,6 +66,16 @@ #include "bmesh.h" +static void sculpt_attribute_update_refs(Object *ob); +static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + const SculptAttributeParams *params, + PBVHType pbvhtype, + bool flat_array_for_bmesh); +void sculptsession_bmesh_add_layers(Object *ob); + using blender::MutableSpan; using blender::Span; @@ -1304,13 +1315,22 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, f } } +static bool paint_rake_rotation_active(const MTex &mtex) +{ + return mtex.tex && mtex.brush_angle_mode & MTEX_ANGLE_RAKE; +} + +static bool paint_rake_rotation_active(const Brush &brush) +{ + return paint_rake_rotation_active(brush.mtex) || paint_rake_rotation_active(brush.mask_mtex); +} + bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, const float mouse_pos[2]) { bool ok = false; - if ((brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || - (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { + if (paint_rake_rotation_active(*brush)) { const float r = RAKE_THRESHHOLD; float rotation; @@ -1430,13 +1450,9 @@ static void sculptsession_free_pbvh(Object *object) MEM_SAFE_FREE(ss->vemap); MEM_SAFE_FREE(ss->vemap_mem); - MEM_SAFE_FREE(ss->persistent_base); - MEM_SAFE_FREE(ss->preview_vert_list); ss->preview_vert_count = 0; - MEM_SAFE_FREE(ss->preview_vert_list); - MEM_SAFE_FREE(ss->vertex_info.connected_component); MEM_SAFE_FREE(ss->vertex_info.boundary); @@ -1470,6 +1486,8 @@ void BKE_sculptsession_free(Object *ob) if (ob && ob->sculpt) { SculptSession *ss = ob->sculpt; + BKE_sculpt_attribute_destroy_temporary_all(ob); + if (ss->bm) { BKE_sculptsession_bm_to_me(ob, true); BM_mesh_free(ss->bm); @@ -1628,6 +1646,21 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) return false; } +/* Helper function to keep persistent base attribute references up to + * date. This is a bit more tricky since they persist across strokes. + */ +static void sculpt_update_persistent_base(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + ss->attrs.persistent_co = BKE_sculpt_attribute_get( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)); + ss->attrs.persistent_no = BKE_sculpt_attribute_get( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no)); + ss->attrs.persistent_disp = BKE_sculpt_attribute_get( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp)); +} + static void sculpt_update_object( Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool need_pmap, bool is_paint_tool) { @@ -1706,7 +1739,8 @@ static void sculpt_update_object( /* Sculpt Face Sets. */ if (use_face_sets) { - ss->face_sets = static_cast<int *>(CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS)); + ss->face_sets = static_cast<int *>( + CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, ".sculpt_face_set")); } else { ss->face_sets = nullptr; @@ -1726,6 +1760,9 @@ static void sculpt_update_object( BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default); + sculpt_attribute_update_refs(ob); + sculpt_update_persistent_base(ob); + if (need_pmap && ob->type == OB_MESH && !ss->pmap) { BKE_mesh_vert_poly_map_create(&ss->pmap, &ss->pmap_mem, @@ -1930,22 +1967,17 @@ int *BKE_sculpt_face_sets_ensure(Mesh *mesh) { using namespace blender; using namespace blender::bke; - if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) { - return static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); + if (!attributes.contains(".sculpt_face_set")) { + SpanAttributeWriter<int> face_sets = attributes.lookup_or_add_for_write_only_span<int>( + ".sculpt_face_set", ATTR_DOMAIN_FACE); + face_sets.span.fill(1); + mesh->face_sets_color_default = 1; + face_sets.finish(); } - const AttributeAccessor attributes = mesh->attributes_for_write(); - const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( - ".hide_poly", ATTR_DOMAIN_FACE, false); - - MutableSpan<int> face_sets = { - static_cast<int *>(CustomData_add_layer( - &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)), - mesh->totpoly}; - - face_sets.fill(1); - mesh->face_sets_color_default = 1; - return face_sets.data(); + return static_cast<int *>( + CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, ".sculpt_face_set")); } bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh) @@ -2015,6 +2047,8 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) } } } + /* The evaluated multires CCG must be updated to contain the new data. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); ret |= SCULPT_MASK_LAYER_CALC_LOOP; } @@ -2022,6 +2056,8 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) /* Create vertex paint mask layer if there isn't one already. */ if (!paint_mask) { CustomData_add_layer(&me->vdata, CD_PAINT_MASK, CD_SET_DEFAULT, nullptr, me->totvert); + /* The evaluated mesh must be updated to contain the new data. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); ret |= SCULPT_MASK_LAYER_CALC_VERT; } @@ -2119,13 +2155,16 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) static PBVH *build_pbvh_for_dynamic_topology(Object *ob) { - PBVH *pbvh = BKE_pbvh_new(); + PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); + + sculptsession_bmesh_add_layers(ob); + BKE_pbvh_build_bmesh(pbvh, ob->sculpt->bm, ob->sculpt->bm_smooth_shading, ob->sculpt->bm_log, - ob->sculpt->cd_vert_node_offset, - ob->sculpt->cd_face_node_offset); + ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, + ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset); pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); pbvh_show_face_sets_set(pbvh, false); return pbvh; @@ -2135,7 +2174,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool { Mesh *me = BKE_object_get_original_mesh(ob); const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop); - PBVH *pbvh = BKE_pbvh_new(); + PBVH *pbvh = BKE_pbvh_new(PBVH_FACES); BKE_pbvh_respect_hide_set(pbvh, respect_hide); MutableSpan<MVert> verts = me->verts_for_write(); @@ -2178,7 +2217,7 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect { CCGKey key; BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); - PBVH *pbvh = BKE_pbvh_new(); + PBVH *pbvh = BKE_pbvh_new(PBVH_GRIDS); BKE_pbvh_respect_hide_set(pbvh, respect_hide); Mesh *base_mesh = BKE_mesh_from_object(ob); @@ -2288,3 +2327,558 @@ void BKE_paint_face_set_overlay_color_get(const int face_set, const int seed, uc &rgba[2]); rgba_float_to_uchar(r_color, rgba); } + +int BKE_sculptsession_vertex_count(const SculptSession *ss) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return ss->totvert; + case PBVH_BMESH: + return BM_mesh_elem_count(ss->bm, BM_VERT); + case PBVH_GRIDS: + return BKE_pbvh_get_grid_num_verts(ss->pbvh); + } + + return 0; +} + +/** Returns pointer to a CustomData associated with a given domain, if + * one exists. If not nullptr is returned (this may happen with e.g. + * multires and ATTR_DOMAIN_POINT). + */ +static CustomData *sculpt_get_cdata(Object *ob, eAttrDomain domain) +{ + SculptSession *ss = ob->sculpt; + + if (ss->bm) { + switch (domain) { + case ATTR_DOMAIN_POINT: + return &ss->bm->vdata; + case ATTR_DOMAIN_FACE: + return &ss->bm->pdata; + default: + BLI_assert_unreachable(); + return NULL; + } + } + else { + Mesh *me = BKE_object_get_original_mesh(ob); + + switch (domain) { + case ATTR_DOMAIN_POINT: + /* Cannot get vertex domain for multires grids. */ + if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return nullptr; + } + + return &me->vdata; + case ATTR_DOMAIN_FACE: + return &me->pdata; + default: + BLI_assert_unreachable(); + return NULL; + } + } +} + +static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) +{ + SculptSession *ss = ob->sculpt; + + switch (domain) { + case ATTR_DOMAIN_POINT: + return BKE_sculptsession_vertex_count(ss); + break; + case ATTR_DOMAIN_FACE: + return ss->totfaces; + break; + default: + BLI_assert_unreachable(); + return 0; + } +} + +static bool sculpt_attribute_create(SculptSession *ss, + Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + SculptAttribute *out, + const SculptAttributeParams *params, + PBVHType pbvhtype, + bool flat_array_for_bmesh) +{ + Mesh *me = BKE_object_get_original_mesh(ob); + + bool simple_array = params->simple_array; + bool permanent = params->permanent; + + out->params = *params; + out->proptype = proptype; + out->domain = domain; + BLI_strncpy_utf8(out->name, name, sizeof(out->name)); + + /* Force non-CustomData simple_array mode if not PBVH_FACES. */ + if (pbvhtype == PBVH_GRIDS || (pbvhtype == PBVH_BMESH && flat_array_for_bmesh)) { + if (permanent) { + printf( + "%s: error: tried to make permanent customdata in multires or bmesh mode; will make " + "local " + "array " + "instead.\n", + __func__); + permanent = (out->params.permanent = false); + } + + simple_array = (out->params.simple_array = true); + } + + BLI_assert(!(simple_array && permanent)); + + int totelem = sculpt_attr_elem_count_get(ob, domain); + + if (simple_array) { + int elemsize = CustomData_sizeof(proptype); + + out->data = MEM_calloc_arrayN(totelem, elemsize, __func__); + + out->data_for_bmesh = ss->bm != NULL; + out->bmesh_cd_offset = -1; + out->layer = NULL; + out->elem_size = elemsize; + out->used = true; + out->elem_num = totelem; + + return true; + } + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + CustomData *cdata = NULL; + out->data_for_bmesh = true; + + switch (domain) { + case ATTR_DOMAIN_POINT: + cdata = &ss->bm->vdata; + break; + case ATTR_DOMAIN_FACE: + cdata = &ss->bm->pdata; + break; + default: + out->used = false; + return false; + } + + BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1); + + BM_data_layer_add_named(ss->bm, cdata, proptype, name); + int index = CustomData_get_named_layer_index(cdata, proptype, name); + + if (!permanent) { + cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; + } + + out->data = NULL; + out->layer = cdata->layers + index; + out->bmesh_cd_offset = out->layer->offset; + out->elem_size = CustomData_sizeof(proptype); + break; + } + case PBVH_FACES: { + CustomData *cdata = NULL; + + out->data_for_bmesh = false; + + switch (domain) { + case ATTR_DOMAIN_POINT: + cdata = &me->vdata; + break; + case ATTR_DOMAIN_FACE: + cdata = &me->pdata; + break; + default: + out->used = false; + return false; + } + + BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1); + + CustomData_add_layer_named(cdata, proptype, CD_SET_DEFAULT, NULL, totelem, name); + int index = CustomData_get_named_layer_index(cdata, proptype, name); + + if (!permanent) { + cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; + } + + out->data = NULL; + out->layer = cdata->layers + index; + out->bmesh_cd_offset = -1; + out->data = out->layer->data; + out->elem_size = CustomData_get_elem_size(out->layer); + + break; + } + case PBVH_GRIDS: { + /* GRIDS should have been handled as simple arrays. */ + BLI_assert_unreachable(); + break; + } + default: + BLI_assert_unreachable(); + break; + } + + out->used = true; + out->elem_num = totelem; + + return true; +} + +static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) +{ + SculptSession *ss = ob->sculpt; + int elem_num = sculpt_attr_elem_count_get(ob, attr->domain); + + bool bad = false; + + if (attr->params.simple_array) { + bad = attr->elem_num != elem_num; + + if (bad) { + MEM_SAFE_FREE(attr->data); + } + } + else { + CustomData *cdata = sculpt_get_cdata(ob, attr->domain); + + if (cdata) { + int layer_index = CustomData_get_named_layer_index(cdata, attr->proptype, attr->name); + bad = layer_index == -1; + + if (ss->bm) { + attr->bmesh_cd_offset = cdata->layers[layer_index].offset; + } + } + } + + if (bad) { + sculpt_attribute_create(ss, + ob, + attr->domain, + attr->proptype, + attr->name, + attr, + &attr->params, + BKE_pbvh_type(ss->pbvh), + true); + } + + return bad; +} + +static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss, + eAttrDomain domain, + eCustomDataType proptype, + const char *name) +{ + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + SculptAttribute *attr = ss->temp_attributes + i; + + if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype && + attr->domain == domain) { + + return attr; + } + } + + return NULL; +} + +bool BKE_sculpt_attribute_exists(Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name) +{ + SculptSession *ss = ob->sculpt; + SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name); + + if (attr) { + return true; + } + + CustomData *cdata = sculpt_get_cdata(ob, domain); + return CustomData_get_named_layer_index(cdata, proptype, name) != -1; + + return false; +} + +static SculptAttribute *sculpt_alloc_attr(SculptSession *ss) +{ + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + if (!ss->temp_attributes[i].used) { + memset((void *)(ss->temp_attributes + i), 0, sizeof(SculptAttribute)); + ss->temp_attributes[i].used = true; + + return ss->temp_attributes + i; + } + } + + BLI_assert_unreachable(); + return NULL; +} + +SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name) +{ + SculptSession *ss = ob->sculpt; + + /* See if attribute is cached in ss->temp_attributes. */ + SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name); + + if (attr) { + if (sculpt_attr_update(ob, attr)) { + sculpt_attribute_update_refs(ob); + } + + return attr; + } + + /* Does attribute exist in CustomData layout? */ + CustomData *cdata = sculpt_get_cdata(ob, domain); + if (cdata) { + int index = CustomData_get_named_layer_index(cdata, proptype, name); + + if (index != -1) { + int totelem = 0; + + switch (domain) { + case ATTR_DOMAIN_POINT: + totelem = BKE_sculptsession_vertex_count(ss); + break; + case ATTR_DOMAIN_FACE: + totelem = ss->totfaces; + break; + default: + BLI_assert_unreachable(); + break; + } + + attr = sculpt_alloc_attr(ss); + + attr->used = true; + attr->proptype = proptype; + attr->data = cdata->layers[index].data; + attr->bmesh_cd_offset = cdata->layers[index].offset; + attr->elem_num = totelem; + attr->layer = cdata->layers + index; + attr->elem_size = CustomData_get_elem_size(attr->layer); + + BLI_strncpy_utf8(attr->name, name, sizeof(attr->name)); + return attr; + } + } + + return NULL; +} + +static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + const SculptAttributeParams *params, + PBVHType pbvhtype, + bool flat_array_for_bmesh) +{ + SculptSession *ss = ob->sculpt; + SculptAttribute *attr = BKE_sculpt_attribute_get(ob, domain, proptype, name); + + if (attr) { + return attr; + } + + attr = sculpt_alloc_attr(ss); + + /* Create attribute. */ + sculpt_attribute_create( + ss, ob, domain, proptype, name, attr, params, pbvhtype, flat_array_for_bmesh); + sculpt_attribute_update_refs(ob); + + return attr; +} + +SculptAttribute *BKE_sculpt_attribute_ensure(Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + const SculptAttributeParams *params) +{ + SculptAttributeParams temp_params = *params; + + return sculpt_attribute_ensure_ex( + ob, domain, proptype, name, &temp_params, BKE_pbvh_type(ob->sculpt->pbvh), true); +} + +static void sculptsession_bmesh_attr_update_internal(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + sculptsession_bmesh_add_layers(ob); + + if (ss->pbvh) { + BKE_pbvh_update_bmesh_offsets(ss->pbvh, + ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, + ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset); + } +} + +void sculptsession_bmesh_add_layers(Object *ob) +{ + SculptSession *ss = ob->sculpt; + SculptAttributeParams params = {0}; + + ss->attrs.dyntopo_node_id_vertex = sculpt_attribute_ensure_ex( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), + ¶ms, + PBVH_BMESH, + false); + + ss->attrs.dyntopo_node_id_face = sculpt_attribute_ensure_ex( + ob, + ATTR_DOMAIN_FACE, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face), + ¶ms, + PBVH_BMESH, + false); +} + +void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + SculptAttribute *attr = ss->temp_attributes + i; + + if (attr->params.stroke_only) { + BKE_sculpt_attribute_destroy(ob, attr); + } + } +} + +static void sculpt_attribute_update_refs(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + /* run twice, in case sculpt_attr_update had to recreate a layer and + messed up the bmesh offsets. */ + for (int i = 0; i < 2; i++) { + for (int j = 0; j < SCULPT_MAX_ATTRIBUTES; j++) { + SculptAttribute *attr = ss->temp_attributes + j; + + if (attr->used) { + sculpt_attr_update(ob, attr); + } + } + + if (ss->bm) { + sculptsession_bmesh_attr_update_internal(ob); + } + } + + Mesh *me = BKE_object_get_original_mesh(ob); + + if (ss->pbvh) { + BKE_pbvh_update_active_vcol(ss->pbvh, me); + } +} + +void BKE_sculpt_attribute_destroy_temporary_all(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + SculptAttribute *attr = ss->temp_attributes + i; + + if (attr->used && !attr->params.permanent) { + BKE_sculpt_attribute_destroy(ob, attr); + } + } +} + +bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) +{ + SculptSession *ss = ob->sculpt; + eAttrDomain domain = attr->domain; + + BLI_assert(attr->used); + + /* Remove from convenience pointer struct. */ + SculptAttribute **ptrs = (SculptAttribute **)&ss->attrs; + int ptrs_num = sizeof(ss->attrs) / sizeof(void *); + + for (int i = 0; i < ptrs_num; i++) { + if (ptrs[i] == attr) { + ptrs[i] = NULL; + } + } + + /* Remove from internal temp_attributes array. */ + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + SculptAttribute *attr2 = ss->temp_attributes + i; + + if (STREQ(attr2->name, attr->name) && attr2->domain == attr->domain && + attr2->proptype == attr->proptype) { + + attr2->used = false; + } + } + + Mesh *me = BKE_object_get_original_mesh(ob); + + if (attr->params.simple_array) { + MEM_SAFE_FREE(attr->data); + } + else if (ss->bm) { + CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->pdata; + + BM_data_layer_free_named(ss->bm, cdata, attr->name); + } + else { + CustomData *cdata = NULL; + int totelem = 0; + + switch (domain) { + case ATTR_DOMAIN_POINT: + cdata = ss->bm ? &ss->bm->vdata : &me->vdata; + totelem = ss->totvert; + break; + case ATTR_DOMAIN_FACE: + cdata = ss->bm ? &ss->bm->pdata : &me->pdata; + totelem = ss->totfaces; + break; + default: + BLI_assert_unreachable(); + return false; + } + + /* We may have been called after destroying ss->bm in which case attr->layer + * might be invalid. + */ + int layer_i = CustomData_get_named_layer_index(cdata, attr->proptype, attr->name); + if (layer_i != 0) { + CustomData_free_layer(cdata, attr->proptype, totelem, layer_i); + } + + sculpt_attribute_update_refs(ob); + } + + attr->data = NULL; + attr->used = false; + + return true; +} diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index ed1d93647ce..561043b553e 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -453,7 +453,7 @@ static int distribute_binary_search(const float *sum, int n, float value) return low; } -/* the max number if calls to rng_* funcs within psys_thread_distribute_particle +/* the max number if calls to rng_* functions within psys_thread_distribute_particle * be sure to keep up to date if this changes */ #define PSYS_RND_DIST_SKIP 3 diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 991a0863235..b6f792812a8 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -705,11 +705,12 @@ void BKE_pbvh_build_grids(PBVH *pbvh, MEM_freeN(prim_bbc); } -PBVH *BKE_pbvh_new(void) +PBVH *BKE_pbvh_new(PBVHType type) { PBVH *pbvh = MEM_callocN(sizeof(PBVH), "pbvh"); pbvh->respect_hide = true; pbvh->draw_cache_invalid = true; + pbvh->header.type = type; return pbvh; } @@ -3104,9 +3105,9 @@ bool pbvh_has_face_sets(PBVH *pbvh) { switch (pbvh->header.type) { case PBVH_GRIDS: - return (pbvh->pdata && CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS)); case PBVH_FACES: - return (pbvh->pdata && CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS)); + return pbvh->pdata && + CustomData_get_layer_named(pbvh->pdata, CD_PROP_INT32, ".sculpt_face_set") != NULL; case PBVH_BMESH: return false; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 9240f8cb80f..502a71a9719 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -1863,6 +1863,12 @@ static void pbvh_bmesh_create_nodes_fast_recursive( /***************************** Public API *****************************/ +void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset) +{ + pbvh->cd_vert_node_offset = cd_vert_node_offset; + pbvh->cd_face_node_offset = cd_face_node_offset; +} + void BKE_pbvh_build_bmesh(PBVH *pbvh, BMesh *bm, bool smooth_shading, @@ -1870,8 +1876,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_vert_node_offset, const int cd_face_node_offset) { - pbvh->cd_vert_node_offset = cd_vert_node_offset; - pbvh->cd_face_node_offset = cd_face_node_offset; pbvh->header.bm = bm; BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75); @@ -1882,6 +1886,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, /* TODO: choose leaf limit better */ pbvh->leaf_limit = 100; + BKE_pbvh_update_bmesh_offsets(pbvh, cd_vert_node_offset, cd_face_node_offset); + if (smooth_shading) { pbvh->flags |= PBVH_DYNTOPO_SMOOTH_SHADING; } @@ -1999,7 +2005,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, BLI_mempool_destroy(queue_pool); } - /* Unmark nodes */ + /* Unmark nodes. */ for (int n = 0; n < pbvh->totnode; n++) { PBVHNode *node = &pbvh->nodes[n]; diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 6adb728b515..2705241425b 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1545,7 +1545,7 @@ void BKE_rigidbody_remove_object(Main *bmain, Scene *scene, Object *ob, const bo FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } - /* Relying on usercount of the object should be OK, and it is much cheaper than looping in all + /* Relying on user-count of the object should be OK, and it is much cheaper than looping in all * collections to check whether the object is already in another one... */ if (ID_REAL_USERS(&ob->id) == 1) { /* Some users seems to find it funny to use a view-layer instancing collection diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index db950492f69..bf1b1586db8 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -235,7 +235,7 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int { Scene *scene_dst = (Scene *)id_dst; const Scene *scene_src = (const Scene *)id_src; - /* We never handle usercount here for own data. */ + /* We never handle user-count here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; /* We always need allocation of our private ID data. */ const int flag_private_id_data = flag & ~LIB_ID_CREATE_NO_ALLOCATE; @@ -1228,7 +1228,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) } /* Active channels root pointer. */ - if (ed->displayed_channels == old_displayed_channels || ed->displayed_channels == nullptr) { + if (ELEM(ed->displayed_channels, old_displayed_channels, nullptr)) { ed->displayed_channels = &ed->channels; } else { @@ -1263,7 +1263,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) } } - if (ms->old_channels == old_displayed_channels || ms->old_channels == nullptr) { + if (ELEM(ms->old_channels, old_displayed_channels, nullptr)) { ms->old_channels = &ed->channels; } else { diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 40348824a13..03f0c3ff1e9 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -305,7 +305,7 @@ IDTypeInfo IDType_ID_SCR = { .lib_override_apply_post = NULL, }; -/* ************ Spacetype/regiontype handling ************** */ +/* ************ Space-type/region-type handling ************** */ /* keep global; this has to be accessible outside of windowmanager */ static ListBase spacetypes = {NULL, NULL}; @@ -1662,7 +1662,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) LISTBASE_FOREACH_MUTABLE (ConsoleLine *, cl, &sconsole->history) { BLO_read_data_address(reader, &cl->line); if (cl->line) { - /* the allocted length is not written, so reset here */ + /* The allocated length is not written, so reset here. */ cl->len_alloc = cl->len + 1; } else { diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index de1d0d3c30e..6e23ca0e89f 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -1155,11 +1155,12 @@ void BKE_sound_update_scene(Depsgraph *depsgraph, Scene *scene) /* cheap test to skip looping over all objects (no speakers is a common case) */ if (DEG_id_type_any_exists(depsgraph, ID_SPK)) { - DEG_OBJECT_ITER_BEGIN (depsgraph, - object, - (DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | - DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY | - DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET)) { + DEGObjectIterSettings deg_iter_settings = {0}; + deg_iter_settings.depsgraph = depsgraph; + deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET; + DEG_OBJECT_ITER_BEGIN (°_iter_settings, object) { sound_update_base(scene, object, new_set); } DEG_OBJECT_ITER_END; diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc deleted file mode 100644 index a674bf7800a..00000000000 --- a/source/blender/blenkernel/intern/spline_base.cc +++ /dev/null @@ -1,526 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_array.hh" -#include "BLI_generic_virtual_array.hh" -#include "BLI_span.hh" -#include "BLI_task.hh" -#include "BLI_timeit.hh" - -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" - -using blender::Array; -using blender::float3; -using blender::GMutableSpan; -using blender::GSpan; -using blender::GVArray; -using blender::IndexRange; -using blender::MutableSpan; -using blender::Span; -using blender::VArray; -using blender::attribute_math::convert_to_static_type; -using blender::bke::AttributeIDRef; -using blender::bke::AttributeMetaData; - -CurveType Spline::type() const -{ - return type_; -} - -void Spline::copy_base_settings(const Spline &src, Spline &dst) -{ - dst.normal_mode = src.normal_mode; - dst.is_cyclic_ = src.is_cyclic_; -} - -static SplinePtr create_spline(const CurveType type) -{ - switch (type) { - case CURVE_TYPE_POLY: - return std::make_unique<PolySpline>(); - case CURVE_TYPE_BEZIER: - return std::make_unique<BezierSpline>(); - case CURVE_TYPE_NURBS: - return std::make_unique<NURBSpline>(); - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - return {}; - } - BLI_assert_unreachable(); - return {}; -} - -SplinePtr Spline::copy() const -{ - SplinePtr dst = this->copy_without_attributes(); - dst->attributes = this->attributes; - return dst; -} - -SplinePtr Spline::copy_only_settings() const -{ - SplinePtr dst = create_spline(type_); - this->copy_base_settings(*this, *dst); - this->copy_settings(*dst); - return dst; -} - -SplinePtr Spline::copy_without_attributes() const -{ - SplinePtr dst = this->copy_only_settings(); - this->copy_data(*dst); - - /* Though the attributes storage is empty, it still needs to know the correct size. */ - dst->attributes.reallocate(dst->size()); - return dst; -} - -void Spline::translate(const blender::float3 &translation) -{ - for (float3 &position : this->positions()) { - position += translation; - } - this->mark_cache_invalid(); -} - -void Spline::transform(const blender::float4x4 &matrix) -{ - for (float3 &position : this->positions()) { - position = matrix * position; - } - this->mark_cache_invalid(); -} - -void Spline::reverse() -{ - this->positions().reverse(); - this->radii().reverse(); - this->tilts().reverse(); - - this->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - std::optional<blender::GMutableSpan> attribute = this->attributes.get_for_write(id); - if (!attribute) { - BLI_assert_unreachable(); - return false; - } - convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - attribute->typed<T>().reverse(); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - this->reverse_impl(); - this->mark_cache_invalid(); -} - -int Spline::evaluated_edges_num() const -{ - const int eval_num = this->evaluated_points_num(); - if (eval_num < 2) { - /* Two points are required for an edge. */ - return 0; - } - - return this->is_cyclic_ ? eval_num : eval_num - 1; -} - -float Spline::length() const -{ - Span<float> lengths = this->evaluated_lengths(); - return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last(); -} - -int Spline::segments_num() const -{ - const int num = this->size(); - - return is_cyclic_ ? num : num - 1; -} - -bool Spline::is_cyclic() const -{ - return is_cyclic_; -} - -void Spline::set_cyclic(const bool value) -{ - is_cyclic_ = value; -} - -static void accumulate_lengths(Span<float3> positions, - const bool is_cyclic, - MutableSpan<float> lengths) -{ - using namespace blender::math; - - float length = 0.0f; - for (const int i : IndexRange(positions.size() - 1)) { - length += distance(positions[i], positions[i + 1]); - lengths[i] = length; - } - if (is_cyclic) { - lengths.last() = length + distance(positions.last(), positions.first()); - } -} - -Span<float> Spline::evaluated_lengths() const -{ - if (!length_cache_dirty_) { - return evaluated_lengths_cache_; - } - - std::lock_guard lock{length_cache_mutex_}; - if (!length_cache_dirty_) { - return evaluated_lengths_cache_; - } - - const int total = evaluated_edges_num(); - evaluated_lengths_cache_.resize(total); - if (total != 0) { - Span<float3> positions = this->evaluated_positions(); - accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); - } - - length_cache_dirty_ = false; - return evaluated_lengths_cache_; -} - -static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next) -{ - using namespace blender::math; - - const float3 dir_prev = normalize(middle - prev); - const float3 dir_next = normalize(next - middle); - - const float3 result = normalize(dir_prev + dir_next); - if (UNLIKELY(is_zero(result))) { - return float3(0.0f, 0.0f, 1.0f); - } - return result; -} - -static void calculate_tangents(Span<float3> positions, - const bool is_cyclic, - MutableSpan<float3> tangents) -{ - using namespace blender::math; - - if (positions.size() == 1) { - tangents.first() = float3(0.0f, 0.0f, 1.0f); - return; - } - - for (const int i : IndexRange(1, positions.size() - 2)) { - tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]); - } - - if (is_cyclic) { - const float3 &second_to_last = positions[positions.size() - 2]; - const float3 &last = positions.last(); - const float3 &first = positions.first(); - const float3 &second = positions[1]; - tangents.first() = direction_bisect(last, first, second); - tangents.last() = direction_bisect(second_to_last, last, first); - } - else { - tangents.first() = normalize(positions[1] - positions[0]); - tangents.last() = normalize(positions.last() - positions[positions.size() - 2]); - } -} - -Span<float3> Spline::evaluated_tangents() const -{ - if (!tangent_cache_dirty_) { - return evaluated_tangents_cache_; - } - - std::lock_guard lock{tangent_cache_mutex_}; - if (!tangent_cache_dirty_) { - return evaluated_tangents_cache_; - } - - const int eval_num = this->evaluated_points_num(); - evaluated_tangents_cache_.resize(eval_num); - - Span<float3> positions = this->evaluated_positions(); - - calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); - this->correct_end_tangents(); - - tangent_cache_dirty_ = false; - return evaluated_tangents_cache_; -} - -static float3 rotate_direction_around_axis(const float3 &direction, - const float3 &axis, - const float angle) -{ - using namespace blender::math; - - BLI_ASSERT_UNIT_V3(direction); - BLI_ASSERT_UNIT_V3(axis); - - const float3 axis_scaled = axis * dot(direction, axis); - const float3 diff = direction - axis_scaled; - const float3 cross = blender::math::cross(axis, diff); - - return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); -} - -static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> r_normals) -{ - using namespace blender::math; - - BLI_assert(r_normals.size() == tangents.size()); - - /* Same as in `vec_to_quat`. */ - const float epsilon = 1e-4f; - for (const int i : r_normals.index_range()) { - const float3 &tangent = tangents[i]; - if (fabsf(tangent.x) + fabsf(tangent.y) < epsilon) { - r_normals[i] = {1.0f, 0.0f, 0.0f}; - } - else { - r_normals[i] = normalize(float3(tangent.y, -tangent.x, 0.0f)); - } - } -} - -/** - * Rotate the last normal in the same way the tangent has been rotated. - */ -static float3 calculate_next_normal(const float3 &last_normal, - const float3 &last_tangent, - const float3 ¤t_tangent) -{ - using namespace blender::math; - - if (is_zero(last_tangent) || is_zero(current_tangent)) { - return last_normal; - } - const float angle = angle_normalized_v3v3(last_tangent, current_tangent); - if (angle != 0.0) { - const float3 axis = normalize(cross(last_tangent, current_tangent)); - return rotate_direction_around_axis(last_normal, axis, angle); - } - return last_normal; -} - -static void calculate_normals_minimum(Span<float3> tangents, - const bool cyclic, - MutableSpan<float3> r_normals) -{ - using namespace blender::math; - BLI_assert(r_normals.size() == tangents.size()); - - if (r_normals.is_empty()) { - return; - } - - const float epsilon = 1e-4f; - - /* Set initial normal. */ - const float3 &first_tangent = tangents[0]; - if (fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon) { - r_normals[0] = {1.0f, 0.0f, 0.0f}; - } - else { - r_normals[0] = normalize(float3(first_tangent.y, -first_tangent.x, 0.0f)); - } - - /* Forward normal with minimum twist along the entire spline. */ - for (const int i : IndexRange(1, r_normals.size() - 1)) { - r_normals[i] = calculate_next_normal(r_normals[i - 1], tangents[i - 1], tangents[i]); - } - - if (!cyclic) { - return; - } - - /* Compute how much the first normal deviates from the normal that has been forwarded along the - * entire cyclic spline. */ - const float3 uncorrected_last_normal = calculate_next_normal( - r_normals.last(), tangents.last(), tangents[0]); - float correction_angle = angle_signed_on_axis_v3v3_v3( - r_normals[0], uncorrected_last_normal, tangents[0]); - if (correction_angle > M_PI) { - correction_angle = correction_angle - 2 * M_PI; - } - - /* Gradually apply correction by rotating all normals slightly. */ - const float angle_step = correction_angle / r_normals.size(); - for (const int i : r_normals.index_range()) { - const float angle = angle_step * i; - r_normals[i] = rotate_direction_around_axis(r_normals[i], tangents[i], angle); - } -} - -Span<float3> Spline::evaluated_normals() const -{ - if (!normal_cache_dirty_) { - return evaluated_normals_cache_; - } - - std::lock_guard lock{normal_cache_mutex_}; - if (!normal_cache_dirty_) { - return evaluated_normals_cache_; - } - - const int eval_num = this->evaluated_points_num(); - evaluated_normals_cache_.resize(eval_num); - - Span<float3> tangents = this->evaluated_tangents(); - MutableSpan<float3> normals = evaluated_normals_cache_; - - /* Only Z up normals are supported at the moment. */ - switch (this->normal_mode) { - case NORMAL_MODE_Z_UP: { - calculate_normals_z_up(tangents, normals); - break; - } - case NORMAL_MODE_MINIMUM_TWIST: { - calculate_normals_minimum(tangents, is_cyclic_, normals); - break; - } - } - - /* Rotate the generated normals with the interpolated tilt data. */ - VArray<float> tilts = this->interpolate_to_evaluated(this->tilts()); - for (const int i : normals.index_range()) { - normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]); - } - - normal_cache_dirty_ = false; - return evaluated_normals_cache_; -} - -Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const -{ - return this->lookup_evaluated_length(this->length() * factor); -} - -Spline::LookupResult Spline::lookup_evaluated_length(const float length) const -{ - BLI_assert(length >= 0.0f && length <= this->length()); - - Span<float> lengths = this->evaluated_lengths(); - - const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); - const int index = offset - lengths.begin(); - const int next_index = (index == this->evaluated_points_num() - 1) ? 0 : index + 1; - - const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; - const float length_in_segment = length - previous_length; - const float segment_length = lengths[index] - previous_length; - const float factor = segment_length == 0.0f ? 0.0f : length_in_segment / segment_length; - - return LookupResult{index, next_index, factor}; -} - -Array<float> Spline::sample_uniform_index_factors(const int samples_num) const -{ - const Span<float> lengths = this->evaluated_lengths(); - - BLI_assert(samples_num > 0); - Array<float> samples(samples_num); - - samples[0] = 0.0f; - if (samples_num == 1) { - return samples; - } - - const float total_length = this->length(); - const float sample_length = total_length / (samples_num - (is_cyclic_ ? 0 : 1)); - - /* Store the length at the previous evaluated point in a variable so it can - * start out at zero (the lengths array doesn't contain 0 for the first point). */ - float prev_length = 0.0f; - int i_sample = 1; - for (const int i_evaluated : IndexRange(this->evaluated_edges_num())) { - const float length = lengths[i_evaluated]; - - /* Add every sample that fits in this evaluated edge. */ - while ((sample_length * i_sample) < length && i_sample < samples_num) { - const float factor = (sample_length * i_sample - prev_length) / (length - prev_length); - samples[i_sample] = i_evaluated + factor; - i_sample++; - } - - prev_length = length; - } - - /* Zero lengths or float inaccuracies can cause invalid values, or simply - * skip some, so set the values that weren't completed in the main loop. */ - for (const int i : IndexRange(i_sample, samples_num - i_sample)) { - samples[i] = float(samples_num); - } - - if (!is_cyclic_) { - /* In rare cases this can prevent overflow of the stored index. */ - samples.last() = lengths.size(); - } - - return samples; -} - -Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const -{ - const int eval_num = this->evaluated_points_num(); - - if (is_cyclic_) { - if (index_factor < eval_num) { - const int index = std::floor(index_factor); - const int next_index = (index < eval_num - 1) ? index + 1 : 0; - return LookupResult{index, next_index, index_factor - index}; - } - return LookupResult{eval_num - 1, 0, 1.0f}; - } - - if (index_factor < eval_num - 1) { - const int index = std::floor(index_factor); - const int next_index = index + 1; - return LookupResult{index, next_index, index_factor - index}; - } - return LookupResult{eval_num - 2, eval_num - 1, 1.0f}; -} - -void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const -{ - Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions(); - for (const float3 &position : positions) { - minmax_v3v3_v3(min, max, position); - } -} - -GVArray Spline::interpolate_to_evaluated(GSpan data) const -{ - return this->interpolate_to_evaluated(GVArray::ForSpan(data)); -} - -void Spline::sample_with_index_factors(const GVArray &src, - Span<float> index_factors, - GMutableSpan dst) const -{ - BLI_assert(src.size() == this->evaluated_points_num()); - - blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - const VArray<T> src_typed = src.typed<T>(); - MutableSpan<T> dst_typed = dst.typed<T>(); - if (src.size() == 1) { - dst_typed.fill(src_typed[0]); - return; - } - blender::threading::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - const LookupResult interp = this->lookup_data_from_index_factor(index_factors[i]); - dst_typed[i] = blender::attribute_math::mix2(interp.factor, - src_typed[interp.evaluated_index], - src_typed[interp.next_evaluated_index]); - } - }); - }); -} diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc deleted file mode 100644 index 80515d0ef37..00000000000 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ /dev/null @@ -1,646 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_array.hh" -#include "BLI_span.hh" -#include "BLI_task.hh" - -#include "BKE_spline.hh" - -using blender::Array; -using blender::float3; -using blender::GVArray; -using blender::IndexRange; -using blender::MutableSpan; -using blender::Span; -using blender::VArray; - -void BezierSpline::copy_settings(Spline &dst) const -{ - BezierSpline &bezier = static_cast<BezierSpline &>(dst); - bezier.resolution_ = resolution_; -} - -void BezierSpline::copy_data(Spline &dst) const -{ - BezierSpline &bezier = static_cast<BezierSpline &>(dst); - bezier.positions_ = positions_; - bezier.handle_types_left_ = handle_types_left_; - bezier.handle_positions_left_ = handle_positions_left_; - bezier.handle_types_right_ = handle_types_right_; - bezier.handle_positions_right_ = handle_positions_right_; - bezier.radii_ = radii_; - bezier.tilts_ = tilts_; -} - -int BezierSpline::size() const -{ - const int size = positions_.size(); - BLI_assert(size == handle_types_left_.size()); - BLI_assert(size == handle_positions_left_.size()); - BLI_assert(size == handle_types_right_.size()); - BLI_assert(size == handle_positions_right_.size()); - BLI_assert(size == radii_.size()); - BLI_assert(size == tilts_.size()); - return size; -} - -int BezierSpline::resolution() const -{ - return resolution_; -} - -void BezierSpline::set_resolution(const int value) -{ - BLI_assert(value > 0); - resolution_ = value; - this->mark_cache_invalid(); -} - -void BezierSpline::resize(const int size) -{ - handle_types_left_.resize(size); - handle_positions_left_.resize(size); - positions_.resize(size); - handle_types_right_.resize(size); - handle_positions_right_.resize(size); - radii_.resize(size); - tilts_.resize(size); - this->mark_cache_invalid(); - attributes.reallocate(size); -} - -MutableSpan<float3> BezierSpline::positions() -{ - return positions_; -} -Span<float3> BezierSpline::positions() const -{ - return positions_; -} -MutableSpan<float> BezierSpline::radii() -{ - return radii_; -} -Span<float> BezierSpline::radii() const -{ - return radii_; -} -MutableSpan<float> BezierSpline::tilts() -{ - return tilts_; -} -Span<float> BezierSpline::tilts() const -{ - return tilts_; -} -Span<int8_t> BezierSpline::handle_types_left() const -{ - return handle_types_left_; -} -MutableSpan<int8_t> BezierSpline::handle_types_left() -{ - return handle_types_left_; -} -Span<float3> BezierSpline::handle_positions_left() const -{ - this->ensure_auto_handles(); - return handle_positions_left_; -} -MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only) -{ - if (!write_only) { - this->ensure_auto_handles(); - } - return handle_positions_left_; -} - -Span<int8_t> BezierSpline::handle_types_right() const -{ - return handle_types_right_; -} -MutableSpan<int8_t> BezierSpline::handle_types_right() -{ - return handle_types_right_; -} -Span<float3> BezierSpline::handle_positions_right() const -{ - this->ensure_auto_handles(); - return handle_positions_right_; -} -MutableSpan<float3> BezierSpline::handle_positions_right(const bool write_only) -{ - if (!write_only) { - this->ensure_auto_handles(); - } - return handle_positions_right_; -} - -void BezierSpline::reverse_impl() -{ - this->handle_positions_left().reverse(); - this->handle_positions_right().reverse(); - std::swap(this->handle_positions_left_, this->handle_positions_right_); - - this->handle_types_left().reverse(); - this->handle_types_right().reverse(); - std::swap(this->handle_types_left_, this->handle_types_right_); -} - -static float3 previous_position(Span<float3> positions, const bool cyclic, const int i) -{ - if (i == 0) { - if (cyclic) { - return positions[positions.size() - 1]; - } - return 2.0f * positions[i] - positions[i + 1]; - } - return positions[i - 1]; -} - -static float3 next_position(Span<float3> positions, const bool cyclic, const int i) -{ - if (i == positions.size() - 1) { - if (cyclic) { - return positions[0]; - } - return 2.0f * positions[i] - positions[i - 1]; - } - return positions[i + 1]; -} - -void BezierSpline::ensure_auto_handles() const -{ - if (!auto_handles_dirty_) { - return; - } - - std::lock_guard lock{auto_handle_mutex_}; - if (!auto_handles_dirty_) { - return; - } - - if (this->size() == 1) { - auto_handles_dirty_ = false; - return; - } - - for (const int i : IndexRange(this->size())) { - using namespace blender; - - if (ELEM(BEZIER_HANDLE_AUTO, handle_types_left_[i], handle_types_right_[i])) { - const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i); - const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i]; - float prev_len = math::length(prev_diff); - float next_len = math::length(next_diff); - if (prev_len == 0.0f) { - prev_len = 1.0f; - } - if (next_len == 0.0f) { - next_len = 1.0f; - } - const float3 dir = next_diff / next_len + prev_diff / prev_len; - - /* This magic number is unfortunate, but comes from elsewhere in Blender. */ - const float len = math::length(dir) * 2.5614f; - if (len != 0.0f) { - if (handle_types_left_[i] == BEZIER_HANDLE_AUTO) { - const float prev_len_clamped = std::min(prev_len, next_len * 5.0f); - handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len); - } - if (handle_types_right_[i] == BEZIER_HANDLE_AUTO) { - const float next_len_clamped = std::min(next_len, prev_len * 5.0f); - handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len); - } - } - } - - if (handle_types_left_[i] == BEZIER_HANDLE_VECTOR) { - const float3 prev = previous_position(positions_, is_cyclic_, i); - handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f); - } - - if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) { - const float3 next = next_position(positions_, is_cyclic_, i); - handle_positions_right_[i] = math::interpolate(positions_[i], next, 1.0f / 3.0f); - } - } - - auto_handles_dirty_ = false; -} - -void BezierSpline::translate(const blender::float3 &translation) -{ - for (float3 &position : this->positions()) { - position += translation; - } - for (float3 &handle_position : this->handle_positions_left()) { - handle_position += translation; - } - for (float3 &handle_position : this->handle_positions_right()) { - handle_position += translation; - } - this->mark_cache_invalid(); -} - -void BezierSpline::transform(const blender::float4x4 &matrix) -{ - for (float3 &position : this->positions()) { - position = matrix * position; - } - for (float3 &handle_position : this->handle_positions_left()) { - handle_position = matrix * handle_position; - } - for (float3 &handle_position : this->handle_positions_right()) { - handle_position = matrix * handle_position; - } - this->mark_cache_invalid(); -} - -static void set_handle_position(const float3 &position, - const HandleType type, - const HandleType type_other, - const float3 &new_value, - float3 &handle, - float3 &handle_other) -{ - using namespace blender::math; - - /* Don't bother when the handle positions are calculated automatically anyway. */ - if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) { - return; - } - - handle = new_value; - if (type_other == BEZIER_HANDLE_ALIGN) { - /* Keep track of the old length of the opposite handle. */ - const float length = distance(handle_other, position); - /* Set the other handle to directly opposite from the current handle. */ - const float3 dir = normalize(handle - position); - handle_other = position - dir * length; - } -} - -void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value) -{ - set_handle_position(positions_[index], - static_cast<HandleType>(handle_types_right_[index]), - static_cast<HandleType>(handle_types_left_[index]), - value, - handle_positions_right_[index], - handle_positions_left_[index]); -} - -void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value) -{ - set_handle_position(positions_[index], - static_cast<HandleType>(handle_types_right_[index]), - static_cast<HandleType>(handle_types_left_[index]), - value, - handle_positions_left_[index], - handle_positions_right_[index]); -} - -bool BezierSpline::point_is_sharp(const int index) const -{ - return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) || - ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE); -} - -bool BezierSpline::segment_is_vector(const int index) const -{ - /* Two control points are necessary to form a segment, that should be checked by the caller. */ - BLI_assert(this->size() > 1); - - if (index == this->size() - 1) { - if (is_cyclic_) { - return handle_types_right_.last() == BEZIER_HANDLE_VECTOR && - handle_types_left_.first() == BEZIER_HANDLE_VECTOR; - } - /* There is actually no segment in this case, but it's nice to avoid - * having a special case for the last segment in calling code. */ - return true; - } - return handle_types_right_[index] == BEZIER_HANDLE_VECTOR && - handle_types_left_[index + 1] == BEZIER_HANDLE_VECTOR; -} - -void BezierSpline::mark_cache_invalid() -{ - offset_cache_dirty_ = true; - position_cache_dirty_ = true; - mapping_cache_dirty_ = true; - tangent_cache_dirty_ = true; - normal_cache_dirty_ = true; - length_cache_dirty_ = true; - auto_handles_dirty_ = true; -} - -int BezierSpline::evaluated_points_num() const -{ - BLI_assert(this->size() > 0); - return this->control_point_offsets().last(); -} - -void BezierSpline::correct_end_tangents() const -{ - using namespace blender::math; - if (is_cyclic_) { - return; - } - - MutableSpan<float3> tangents(evaluated_tangents_cache_); - - if (handle_positions_right_.first() != positions_.first()) { - tangents.first() = normalize(handle_positions_right_.first() - positions_.first()); - } - if (handle_positions_left_.last() != positions_.last()) { - tangents.last() = normalize(positions_.last() - handle_positions_left_.last()); - } -} - -BezierSpline::InsertResult BezierSpline::calculate_segment_insertion(const int index, - const int next_index, - const float parameter) -{ - using namespace blender::math; - - BLI_assert(parameter <= 1.0f && parameter >= 0.0f); - BLI_assert(ELEM(next_index, 0, index + 1)); - const float3 &point_prev = positions_[index]; - const float3 &handle_prev = handle_positions_right_[index]; - const float3 &handle_next = handle_positions_left_[next_index]; - const float3 &point_next = positions_[next_index]; - const float3 center_point = interpolate(handle_prev, handle_next, parameter); - - BezierSpline::InsertResult result; - result.handle_prev = interpolate(point_prev, handle_prev, parameter); - result.handle_next = interpolate(handle_next, point_next, parameter); - result.left_handle = interpolate(result.handle_prev, center_point, parameter); - result.right_handle = interpolate(center_point, result.handle_next, parameter); - result.position = interpolate(result.left_handle, result.right_handle, parameter); - return result; -} - -static void bezier_forward_difference_3d(const float3 &point_0, - const float3 &point_1, - const float3 &point_2, - const float3 &point_3, - MutableSpan<float3> result) -{ - BLI_assert(result.size() > 0); - const float inv_len = 1.0f / static_cast<float>(result.size()); - const float inv_len_squared = inv_len * inv_len; - const float inv_len_cubed = inv_len_squared * inv_len; - - const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len; - const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared; - const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed; - - float3 q0 = point_0; - float3 q1 = rt1 + rt2 + rt3; - float3 q2 = 2.0f * rt2 + 6.0f * rt3; - float3 q3 = 6.0f * rt3; - for (const int i : result.index_range()) { - result[i] = q0; - q0 += q1; - q1 += q2; - q2 += q3; - } -} - -void BezierSpline::evaluate_segment(const int index, - const int next_index, - MutableSpan<float3> positions) const -{ - if (this->segment_is_vector(index)) { - BLI_assert(positions.size() == 1); - positions.first() = positions_[index]; - } - else { - bezier_forward_difference_3d(positions_[index], - handle_positions_right_[index], - handle_positions_left_[next_index], - positions_[next_index], - positions); - } -} - -Span<int> BezierSpline::control_point_offsets() const -{ - if (!offset_cache_dirty_) { - return offset_cache_; - } - - std::lock_guard lock{offset_cache_mutex_}; - if (!offset_cache_dirty_) { - return offset_cache_; - } - - const int size = this->size(); - offset_cache_.resize(size + 1); - - MutableSpan<int> offsets = offset_cache_; - if (size == 1) { - offsets.first() = 0; - offsets.last() = 1; - } - else { - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - offset += this->segment_is_vector(i) ? 1 : resolution_; - } - offsets.last() = offset; - } - - offset_cache_dirty_ = false; - return offsets; -} - -static void calculate_mappings_linear_resolution(Span<int> offsets, - const int size, - const int resolution, - const bool is_cyclic, - MutableSpan<float> r_mappings) -{ - const float first_segment_len_inv = 1.0f / offsets[1]; - for (const int i : IndexRange(0, offsets[1])) { - r_mappings[i] = i * first_segment_len_inv; - } - - const int grain_size = std::max(2048 / resolution, 1); - blender::threading::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { - for (const int i_control_point : range) { - const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point]; - const float segment_len_inv = 1.0f / segment_len; - for (const int i : IndexRange(segment_len)) { - r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv; - } - } - }); - - if (is_cyclic) { - const int last_segment_len = offsets[size] - offsets[size - 1]; - const float last_segment_len_inv = 1.0f / last_segment_len; - for (const int i : IndexRange(last_segment_len)) { - r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv; - } - } - else { - r_mappings.last() = size - 1; - } -} - -Span<float> BezierSpline::evaluated_mappings() const -{ - if (!mapping_cache_dirty_) { - return evaluated_mapping_cache_; - } - - std::lock_guard lock{mapping_cache_mutex_}; - if (!mapping_cache_dirty_) { - return evaluated_mapping_cache_; - } - - const int num = this->size(); - const int eval_num = this->evaluated_points_num(); - evaluated_mapping_cache_.resize(eval_num); - MutableSpan<float> mappings = evaluated_mapping_cache_; - - if (eval_num == 1) { - mappings.first() = 0.0f; - mapping_cache_dirty_ = false; - return mappings; - } - - Span<int> offsets = this->control_point_offsets(); - - blender::threading::isolate_task([&]() { - /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - calculate_mappings_linear_resolution(offsets, num, resolution_, is_cyclic_, mappings); - }); - - mapping_cache_dirty_ = false; - return mappings; -} - -Span<float3> BezierSpline::evaluated_positions() const -{ - if (!position_cache_dirty_) { - return evaluated_position_cache_; - } - - std::lock_guard lock{position_cache_mutex_}; - if (!position_cache_dirty_) { - return evaluated_position_cache_; - } - - const int num = this->size(); - const int eval_num = this->evaluated_points_num(); - evaluated_position_cache_.resize(eval_num); - - MutableSpan<float3> positions = evaluated_position_cache_; - - if (num == 1) { - /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ - BLI_assert(eval_num == 1); - positions.first() = positions_.first(); - position_cache_dirty_ = false; - return positions; - } - - this->ensure_auto_handles(); - - Span<int> offsets = this->control_point_offsets(); - - const int grain_size = std::max(512 / resolution_, 1); - blender::threading::isolate_task([&]() { - /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) { - for (const int i : range) { - this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); - } - }); - }); - if (is_cyclic_) { - this->evaluate_segment( - num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 1])); - } - else { - /* Since evaluating the bezier segment doesn't add the final point, - * it must be added manually in the non-cyclic case. */ - positions.last() = positions_.last(); - } - - position_cache_dirty_ = false; - return positions; -} - -BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( - const float index_factor) const -{ - const int num = this->size(); - - if (is_cyclic_) { - if (index_factor < num) { - const int index = std::floor(index_factor); - const int next_index = (index < num - 1) ? index + 1 : 0; - return InterpolationData{index, next_index, index_factor - index}; - } - return InterpolationData{num - 1, 0, 1.0f}; - } - - if (index_factor < num - 1) { - const int index = std::floor(index_factor); - const int next_index = index + 1; - return InterpolationData{index, next_index, index_factor - index}; - } - return InterpolationData{num - 2, num - 1, 1.0f}; -} - -/* Use a spline argument to avoid adding this to the header. */ -template<typename T> -static void interpolate_to_evaluated_impl(const BezierSpline &spline, - const blender::VArray<T> &src, - MutableSpan<T> dst) -{ - BLI_assert(src.size() == spline.size()); - BLI_assert(dst.size() == spline.evaluated_points_num()); - Span<float> mappings = spline.evaluated_mappings(); - - for (const int i : dst.index_range()) { - BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor( - mappings[i]); - - const T &value = src[interp.control_point_index]; - const T &next_value = src[interp.next_control_point_index]; - - dst[i] = blender::attribute_math::mix2(interp.factor, value, next_value); - } -} - -GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const -{ - BLI_assert(src.size() == this->size()); - - if (src.is_single()) { - return src; - } - - const int eval_num = this->evaluated_points_num(); - if (eval_num == 1) { - return src; - } - - GVArray new_varray; - blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(eval_num); - interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values); - new_varray = VArray<T>::ForContainer(std::move(values)); - } - }); - - return new_varray; -} diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc deleted file mode 100644 index a7eeb82d854..00000000000 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ /dev/null @@ -1,395 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_array.hh" -#include "BLI_span.hh" -#include "BLI_virtual_array.hh" - -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" - -using blender::Array; -using blender::float3; -using blender::GVArray; -using blender::IndexRange; -using blender::MutableSpan; -using blender::Span; -using blender::VArray; - -void NURBSpline::copy_settings(Spline &dst) const -{ - NURBSpline &nurbs = static_cast<NURBSpline &>(dst); - nurbs.knots_mode = knots_mode; - nurbs.resolution_ = resolution_; - nurbs.order_ = order_; -} - -void NURBSpline::copy_data(Spline &dst) const -{ - NURBSpline &nurbs = static_cast<NURBSpline &>(dst); - nurbs.positions_ = positions_; - nurbs.weights_ = weights_; - nurbs.knots_ = knots_; - nurbs.knots_dirty_ = knots_dirty_; - nurbs.radii_ = radii_; - nurbs.tilts_ = tilts_; -} - -int NURBSpline::size() const -{ - const int size = positions_.size(); - BLI_assert(size == radii_.size()); - BLI_assert(size == tilts_.size()); - BLI_assert(size == weights_.size()); - return size; -} - -int NURBSpline::resolution() const -{ - return resolution_; -} - -void NURBSpline::set_resolution(const int value) -{ - BLI_assert(value > 0); - resolution_ = value; - this->mark_cache_invalid(); -} - -uint8_t NURBSpline::order() const -{ - return order_; -} - -void NURBSpline::set_order(const uint8_t value) -{ - BLI_assert(value >= 2 && value <= 6); - order_ = value; - this->mark_cache_invalid(); -} - -void NURBSpline::resize(const int size) -{ - positions_.resize(size); - radii_.resize(size); - tilts_.resize(size); - weights_.resize(size); - this->mark_cache_invalid(); - attributes.reallocate(size); -} - -MutableSpan<float3> NURBSpline::positions() -{ - return positions_; -} -Span<float3> NURBSpline::positions() const -{ - return positions_; -} -MutableSpan<float> NURBSpline::radii() -{ - return radii_; -} -Span<float> NURBSpline::radii() const -{ - return radii_; -} -MutableSpan<float> NURBSpline::tilts() -{ - return tilts_; -} -Span<float> NURBSpline::tilts() const -{ - return tilts_; -} -MutableSpan<float> NURBSpline::weights() -{ - return weights_; -} -Span<float> NURBSpline::weights() const -{ - return weights_; -} - -void NURBSpline::reverse_impl() -{ - this->weights().reverse(); -} - -void NURBSpline::mark_cache_invalid() -{ - basis_cache_dirty_ = true; - position_cache_dirty_ = true; - tangent_cache_dirty_ = true; - normal_cache_dirty_ = true; - length_cache_dirty_ = true; -} - -int NURBSpline::evaluated_points_num() const -{ - if (!this->check_valid_num_and_order()) { - return 0; - } - return resolution_ * this->segments_num(); -} - -void NURBSpline::correct_end_tangents() const -{ -} - -bool NURBSpline::check_valid_num_and_order() const -{ - if (this->size() < order_) { - return false; - } - - if (ELEM(this->knots_mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER)) { - if (this->knots_mode == NURBS_KNOT_MODE_BEZIER && this->size() <= order_) { - return false; - } - return (!is_cyclic_ || this->size() % (order_ - 1) == 0); - } - - return true; -} - -int NURBSpline::knots_num() const -{ - const int num = this->size() + order_; - return is_cyclic_ ? num + order_ - 1 : num; -} - -void NURBSpline::calculate_knots() const -{ - const KnotsMode mode = this->knots_mode; - const int order = order_; - const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); - const bool is_end_point = ELEM(mode, NURBS_KNOT_MODE_ENDPOINT, NURBS_KNOT_MODE_ENDPOINT_BEZIER); - /* Inner knots are always repeated once except on Bezier case. */ - const int repeat_inner = is_bezier ? order - 1 : 1; - /* How many times to repeat 0.0 at the beginning of knot. */ - const int head = is_end_point ? (order - (is_cyclic_ ? 1 : 0)) : - (is_bezier ? min_ii(2, repeat_inner) : 1); - /* Number of knots replicating widths of the starting knots. - * Covers both Cyclic and EndPoint cases. */ - const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0); - - knots_.resize(this->knots_num()); - MutableSpan<float> knots = knots_; - - int r = head; - float current = 0.0f; - - const int offset = is_end_point && is_cyclic_ ? 1 : 0; - if (offset) { - knots[0] = current; - current += 1.0f; - } - - for (const int i : IndexRange(offset, knots.size() - offset - tail)) { - knots[i] = current; - r--; - if (r == 0) { - current += 1.0; - r = repeat_inner; - } - } - - const int tail_index = knots.size() - tail; - for (const int i : IndexRange(tail)) { - knots[tail_index + i] = current + (knots[i] - knots[0]); - } -} - -Span<float> NURBSpline::knots() const -{ - if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_num()); - return knots_; - } - - std::lock_guard lock{knots_mutex_}; - if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_num()); - return knots_; - } - - this->calculate_knots(); - - knots_dirty_ = false; - - return knots_; -} - -static void calculate_basis_for_point(const float parameter, - const int num, - const int degree, - const Span<float> knots, - MutableSpan<float> r_weights, - int &r_start_index) -{ - const int order = degree + 1; - - int start = 0; - int end = 0; - for (const int i : IndexRange(num + degree)) { - const bool knots_equal = knots[i] == knots[i + 1]; - if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) { - continue; - } - - start = std::max(i - degree, 0); - end = i; - break; - } - - Array<float, 12> buffer(order * 2, 0.0f); - - buffer[end - start] = 1.0f; - - for (const int i_order : IndexRange(2, degree)) { - if (end + i_order >= knots.size()) { - end = num + degree - i_order; - } - for (const int i : IndexRange(end - start + 1)) { - const int knot_index = start + i; - - float new_basis = 0.0f; - if (buffer[i] != 0.0f) { - new_basis += ((parameter - knots[knot_index]) * buffer[i]) / - (knots[knot_index + i_order - 1] - knots[knot_index]); - } - - if (buffer[i + 1] != 0.0f) { - new_basis += ((knots[knot_index + i_order] - parameter) * buffer[i + 1]) / - (knots[knot_index + i_order] - knots[knot_index + 1]); - } - - buffer[i] = new_basis; - } - } - - buffer.as_mutable_span().drop_front(end - start + 1).fill(0.0f); - r_weights.copy_from(buffer.as_span().take_front(order)); - r_start_index = start; -} - -const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const -{ - if (!basis_cache_dirty_) { - return basis_cache_; - } - - std::lock_guard lock{basis_cache_mutex_}; - if (!basis_cache_dirty_) { - return basis_cache_; - } - - const int num = this->size(); - const int eval_num = this->evaluated_points_num(); - - const int order = this->order(); - const int degree = order - 1; - - basis_cache_.weights.resize(eval_num * order); - basis_cache_.start_indices.resize(eval_num); - - if (eval_num == 0) { - return basis_cache_; - } - - MutableSpan<float> basis_weights(basis_cache_.weights); - MutableSpan<int> basis_start_indices(basis_cache_.start_indices); - - const Span<float> control_weights = this->weights(); - const Span<float> knots = this->knots(); - - const int last_control_point_index = is_cyclic_ ? num + degree : num; - - const float start = knots[degree]; - const float end = knots[last_control_point_index]; - const float step = (end - start) / this->evaluated_edges_num(); - for (const int i : IndexRange(eval_num)) { - /* Clamp parameter due to floating point inaccuracy. */ - const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]); - - MutableSpan<float> point_weights = basis_weights.slice(i * order, order); - - calculate_basis_for_point( - parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); - - for (const int j : point_weights.index_range()) { - const int point_index = (basis_start_indices[i] + j) % num; - point_weights[j] *= control_weights[point_index]; - } - } - - basis_cache_dirty_ = false; - return basis_cache_; -} - -template<typename T> -void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, - const int order, - const blender::VArray<T> &src, - MutableSpan<T> dst) -{ - const int num = src.size(); - blender::attribute_math::DefaultMixer<T> mixer(dst); - - for (const int i : dst.index_range()) { - Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); - const int start_index = basis_cache.start_indices[i]; - - for (const int j : point_weights.index_range()) { - const int point_index = (start_index + j) % num; - mixer.mix_in(i, src[point_index], point_weights[j]); - } - } - - mixer.finalize(); -} - -GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const -{ - BLI_assert(src.size() == this->size()); - - if (src.is_single()) { - return src; - } - - const BasisCache &basis_cache = this->calculate_basis_cache(); - - GVArray new_varray; - blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(this->evaluated_points_num()); - interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values); - new_varray = VArray<T>::ForContainer(std::move(values)); - } - }); - - return new_varray; -} - -Span<float3> NURBSpline::evaluated_positions() const -{ - if (!position_cache_dirty_) { - return evaluated_position_cache_; - } - - std::lock_guard lock{position_cache_mutex_}; - if (!position_cache_dirty_) { - return evaluated_position_cache_; - } - - const int eval_num = this->evaluated_points_num(); - evaluated_position_cache_.resize(eval_num); - - /* TODO: Avoid copying the evaluated data from the temporary array. */ - VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span()); - evaluated.materialize(evaluated_position_cache_); - - position_cache_dirty_ = false; - return evaluated_position_cache_; -} diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc deleted file mode 100644 index c3cc268c81c..00000000000 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_span.hh" -#include "BLI_virtual_array.hh" - -#include "BKE_spline.hh" - -using blender::float3; -using blender::GVArray; -using blender::MutableSpan; -using blender::Span; - -void PolySpline::copy_settings(Spline &UNUSED(dst)) const -{ - /* Poly splines have no settings not covered by the base class. */ -} - -void PolySpline::copy_data(Spline &dst) const -{ - PolySpline &poly = static_cast<PolySpline &>(dst); - poly.positions_ = positions_; - poly.radii_ = radii_; - poly.tilts_ = tilts_; -} - -int PolySpline::size() const -{ - const int size = positions_.size(); - BLI_assert(size == radii_.size()); - BLI_assert(size == tilts_.size()); - return size; -} - -void PolySpline::resize(const int size) -{ - positions_.resize(size); - radii_.resize(size); - tilts_.resize(size); - this->mark_cache_invalid(); - attributes.reallocate(size); -} - -MutableSpan<float3> PolySpline::positions() -{ - return positions_; -} -Span<float3> PolySpline::positions() const -{ - return positions_; -} -MutableSpan<float> PolySpline::radii() -{ - return radii_; -} -Span<float> PolySpline::radii() const -{ - return radii_; -} -MutableSpan<float> PolySpline::tilts() -{ - return tilts_; -} -Span<float> PolySpline::tilts() const -{ - return tilts_; -} - -void PolySpline::reverse_impl() -{ -} - -void PolySpline::mark_cache_invalid() -{ - tangent_cache_dirty_ = true; - normal_cache_dirty_ = true; - length_cache_dirty_ = true; -} - -int PolySpline::evaluated_points_num() const -{ - return this->size(); -} - -void PolySpline::correct_end_tangents() const -{ -} - -Span<float3> PolySpline::evaluated_positions() const -{ - return this->positions(); -} - -GVArray PolySpline::interpolate_to_evaluated(const GVArray &src) const -{ - BLI_assert(src.size() == this->size()); - return src; -} diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c index b13aec37c78..aabed2cea28 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -41,6 +41,8 @@ typedef struct ConverterStorage { /* CustomData layer for vertex sharpnesses. */ const float *cd_vertex_crease; + /* CustomData layer for edge sharpness. */ + const float *cd_edge_crease; /* Indexed by loop index, value denotes index of face-varying vertex * which corresponds to the UV coordinate. */ @@ -157,12 +159,11 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, int manif return 10.0f; } #endif - if (!storage->settings.use_creases) { + if (!storage->settings.use_creases || storage->cd_edge_crease == NULL) { return 0.0f; } const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index]; - const MEdge *medge = storage->edges; - return BKE_subdiv_crease_to_sharpness_char(medge[edge_index].crease); + return BKE_subdiv_crease_to_sharpness_f(storage->cd_edge_crease[edge_index]); } static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter, @@ -211,6 +212,7 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( storage->polys, (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"), + (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".select_poly"), storage->loops, mloopuv, num_poly, @@ -398,6 +400,7 @@ static void init_user_data(OpenSubdiv_Converter *converter, user_data->polys = BKE_mesh_polys(mesh); user_data->loops = BKE_mesh_loops(mesh); user_data->cd_vertex_crease = CustomData_get_layer(&mesh->vdata, CD_CREASE); + user_data->cd_edge_crease = CustomData_get_layer(&mesh->edata, CD_CREASE); user_data->loop_uv_indices = NULL; initialize_manifold_indices(user_data); converter->user_data = user_data; diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc index 44bdd6e6d06..6bc188fd1fc 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -518,9 +518,11 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex const int *UNUSED(subdiv_polygon_offset)) { /* Multi-resolution grid data will be applied or become invalid after subdivision, - * so don't try to preserve it and use memory. */ + * so don't try to preserve it and use memory. Crease values should also not be interpolated. */ CustomData_MeshMasks mask = CD_MASK_EVERYTHING; mask.lmask &= ~CD_MASK_MULTIRES_GRIDS; + mask.vmask &= ~CD_MASK_CREASE; + mask.emask &= ~CD_MASK_CREASE; SubdivMeshContext *subdiv_context = static_cast<SubdivMeshContext *>(foreach_context->user_data); subdiv_context->subdiv_mesh = BKE_mesh_new_nomain_from_template_ex( @@ -790,7 +792,7 @@ static void subdiv_copy_edge_data(SubdivMeshContext *ctx, { const int subdiv_edge_index = subdiv_edge - ctx->subdiv_edges; if (coarse_edge == nullptr) { - subdiv_edge->crease = 0; + /* TODO: Ensure crease layer isn't copied to result. */ subdiv_edge->flag = 0; if (!ctx->settings->use_optimal_display) { subdiv_edge->flag |= ME_EDGERENDER; @@ -1135,8 +1137,6 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach is_simple, u, subdiv_vertex->co); - /* Reset flags and such. */ - subdiv_vertex->flag = 0; } /** \} */ diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 0e5f9f30243..c95c43a8099 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -285,7 +285,7 @@ static int ss_sync_from_uv(CCGSubSurf *ss, * Also, initially intention is to treat merged vertices from mirror modifier as seams. * This fixes a very old regression (2.49 was correct here) */ vmap = BKE_mesh_uv_vert_map_create( - mpoly, NULL, mloop, mloopuv, totface, totvert, limit, false, true); + mpoly, NULL, NULL, mloop, mloopuv, totface, totvert, limit, false, true); if (!vmap) { return 0; } @@ -592,11 +592,12 @@ static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss, me = medge; index = (int *)dm->getEdgeDataArray(dm, CD_ORIGINDEX); + const float *creases = (const float *)dm->getEdgeDataArray(dm, CD_CREASE); for (i = 0; i < totedge; i++, me++) { CCGEdge *e; float crease; - crease = useFlatSubdiv ? creaseFactor : me->crease * creaseFactor / 255.0f; + crease = useFlatSubdiv ? creaseFactor : (creases ? creases[i] * creaseFactor : 0.0f); ccgSubSurf_syncEdge( ss, POINTER_FROM_INT(i), POINTER_FROM_UINT(me->v1), POINTER_FROM_UINT(me->v2), crease, &e); @@ -879,7 +880,6 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3]) BLI_INLINE void ccgDM_to_MVert(MVert *mv, const CCGKey *key, CCGElem *elem) { copy_v3_v3(mv->co, CCG_elem_co(key, elem)); - mv->flag = 0; } static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert) @@ -949,7 +949,6 @@ BLI_INLINE void ccgDM_to_MEdge(MEdge *med, const int v1, const int v2, const sho { med->v1 = v1; med->v2 = v2; - med->crease = 0; med->flag = flag; } diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index 288493c9d65..ddc758c6887 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -81,7 +81,7 @@ static void vfont_copy_data(Main *UNUSED(bmain), { VFont *vfont_dst = (VFont *)id_dst; - /* We never handle usercount here for own data. */ + /* We never handle user-count here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; /* Just to be sure, should not have any value actually after reading time. */ diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.cc index f59be7f0111..8008dacc853 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.cc @@ -57,7 +57,7 @@ static void workspace_free_data(ID *id) BLI_freelistN(&workspace->layouts); while (!BLI_listbase_is_empty(&workspace->tools)) { - BKE_workspace_tool_remove(workspace, workspace->tools.first); + BKE_workspace_tool_remove(workspace, static_cast<bToolRef *>(workspace->tools.first)); } MEM_SAFE_FREE(workspace->status_text); @@ -107,12 +107,12 @@ static void workspace_blend_read_data(BlendDataReader *reader, ID *id) } LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { - tref->runtime = NULL; + tref->runtime = nullptr; BLO_read_data_address(reader, &tref->properties); IDP_BlendDataRead(reader, &tref->properties); } - workspace->status_text = NULL; + workspace->status_text = nullptr; id_us_ensure_real(&workspace->id); } @@ -125,15 +125,15 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) /* Do not keep the scene reference when appending a workspace. Setting a scene for a workspace is * a convenience feature, but the workspace should never truly depend on scene data. */ if (ID_IS_LINKED(id)) { - workspace->pin_scene = NULL; + workspace->pin_scene = nullptr; } else { - BLO_read_id_address(reader, NULL, &workspace->pin_scene); + BLO_read_id_address(reader, nullptr, &workspace->pin_scene); } /* Restore proper 'parent' pointers to relevant data, and clean up unused/invalid entries. */ LISTBASE_FOREACH_MUTABLE (WorkSpaceDataRelation *, relation, &workspace->hook_layout_relations) { - relation->parent = NULL; + relation->parent = nullptr; LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { if (win->winid == relation->parentid) { @@ -141,7 +141,7 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) } } } - if (relation->parent == NULL) { + if (relation->parent == nullptr) { BLI_freelinkN(&workspace->hook_layout_relations, relation); } } @@ -176,33 +176,33 @@ static void workspace_blend_read_expand(BlendExpander *expander, ID *id) } IDTypeInfo IDType_ID_WS = { - .id_code = ID_WS, - .id_filter = FILTER_ID_WS, - .main_listbase_index = INDEX_ID_WS, - .struct_size = sizeof(WorkSpace), - .name = "WorkSpace", - .name_plural = "workspaces", - .translation_context = BLT_I18NCONTEXT_ID_WORKSPACE, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA, - .asset_type_info = NULL, - - .init_data = workspace_init_data, - .copy_data = NULL, - .free_data = workspace_free_data, - .make_local = NULL, - .foreach_id = workspace_foreach_id, - .foreach_cache = NULL, - .foreach_path = NULL, - .owner_pointer_get = NULL, - - .blend_write = workspace_blend_write, - .blend_read_data = workspace_blend_read_data, - .blend_read_lib = workspace_blend_read_lib, - .blend_read_expand = workspace_blend_read_expand, - - .blend_read_undo_preserve = NULL, - - .lib_override_apply_post = NULL, + /* id_code */ ID_WS, + /* id_filter */ FILTER_ID_WS, + /* main_listbase_index */ INDEX_ID_WS, + /* struct_size */ sizeof(WorkSpace), + /* name */ "WorkSpace", + /* name_plural */ "workspaces", + /* translation_context */ BLT_I18NCONTEXT_ID_WORKSPACE, + /* flags */ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA, + /* asset_type_info */ nullptr, + + /* init_data */ workspace_init_data, + /* copy_data */ nullptr, + /* free_data */ workspace_free_data, + /* make_local */ nullptr, + /* foreach_id */ workspace_foreach_id, + /* foreach_cache */ nullptr, + /* foreach_path */ nullptr, + /* owner_pointer_get */ nullptr, + + /* blend_write */ workspace_blend_write, + /* blend_read_data */ workspace_blend_read_data, + /* blend_read_lib */ workspace_blend_read_lib, + /* blend_read_expand */ workspace_blend_read_expand, + + /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ nullptr, }; /* -------------------------------------------------------------------- */ @@ -230,7 +230,8 @@ static void workspace_layout_name_set(WorkSpace *workspace, static WorkSpaceLayout *workspace_layout_find_exec(const WorkSpace *workspace, const bScreen *screen) { - return BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen)); + return static_cast<WorkSpaceLayout *>( + BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen))); } static void workspace_relation_add(ListBase *relation_list, @@ -238,7 +239,7 @@ static void workspace_relation_add(ListBase *relation_list, const int parentid, void *data) { - WorkSpaceDataRelation *relation = MEM_callocN(sizeof(*relation), __func__); + WorkSpaceDataRelation *relation = MEM_cnew<WorkSpaceDataRelation>(__func__); relation->parent = parent; relation->parentid = parentid; relation->value = data; @@ -256,9 +257,9 @@ static void workspace_relation_ensure_updated(ListBase *relation_list, const int parentid, void *data) { - WorkSpaceDataRelation *relation = BLI_listbase_bytes_find( - relation_list, &parentid, sizeof(parentid), offsetof(WorkSpaceDataRelation, parentid)); - if (relation != NULL) { + WorkSpaceDataRelation *relation = static_cast<WorkSpaceDataRelation *>(BLI_listbase_bytes_find( + relation_list, &parentid, sizeof(parentid), offsetof(WorkSpaceDataRelation, parentid))); + if (relation != nullptr) { relation->parent = parent; relation->value = data; /* reinsert at the head of the list, so that more commonly used relations are found faster. */ @@ -274,13 +275,13 @@ static void workspace_relation_ensure_updated(ListBase *relation_list, static void *workspace_relation_get_data_matching_parent(const ListBase *relation_list, const void *parent) { - WorkSpaceDataRelation *relation = BLI_findptr( - relation_list, parent, offsetof(WorkSpaceDataRelation, parent)); - if (relation != NULL) { + WorkSpaceDataRelation *relation = static_cast<WorkSpaceDataRelation *>( + BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent))); + if (relation != nullptr) { return relation->value; } - return NULL; + return nullptr; } /** @@ -296,7 +297,8 @@ static bool UNUSED_FUNCTION(workspaces_is_screen_used) #endif (const Main *bmain, bScreen *screen) { - for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace; + workspace = static_cast<WorkSpace *>(workspace->id.next)) { if (workspace_layout_find_exec(workspace, screen)) { return true; } @@ -313,14 +315,16 @@ static bool UNUSED_FUNCTION(workspaces_is_screen_used) WorkSpace *BKE_workspace_add(Main *bmain, const char *name) { - WorkSpace *new_workspace = BKE_id_new(bmain, ID_WS, name); + WorkSpace *new_workspace = static_cast<WorkSpace *>(BKE_id_new(bmain, ID_WS, name)); id_us_ensure_real(&new_workspace->id); return new_workspace; } void BKE_workspace_remove(Main *bmain, WorkSpace *workspace) { - for (WorkSpaceLayout *layout = workspace->layouts.first, *layout_next; layout; + for (WorkSpaceLayout *layout = static_cast<WorkSpaceLayout *>(workspace->layouts.first), + *layout_next; + layout; layout = layout_next) { layout_next = layout->next; BKE_workspace_layout_remove(bmain, workspace, layout); @@ -330,11 +334,13 @@ void BKE_workspace_remove(Main *bmain, WorkSpace *workspace) WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const Main *bmain, const int winid) { - WorkSpaceInstanceHook *hook = MEM_callocN(sizeof(WorkSpaceInstanceHook), __func__); + WorkSpaceInstanceHook *hook = MEM_cnew<WorkSpaceInstanceHook>(__func__); /* set an active screen-layout for each possible window/workspace combination */ - for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { - BKE_workspace_active_layout_set(hook, winid, workspace, workspace->layouts.first); + for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace; + workspace = static_cast<WorkSpace *>(workspace->id.next)) { + BKE_workspace_active_layout_set( + hook, winid, workspace, static_cast<WorkSpaceLayout *>(workspace->layouts.first)); } return hook; @@ -347,8 +353,11 @@ void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook * BLI_assert(!BLI_listbase_is_empty(&bmain->workspaces) || G.background); /* Free relations for this hook */ - for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { - for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first, *relation_next; + for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace; + workspace = static_cast<WorkSpace *>(workspace->id.next)) { + for (WorkSpaceDataRelation *relation = static_cast<WorkSpaceDataRelation *>( + workspace->hook_layout_relations.first), + *relation_next; relation; relation = relation_next) { relation_next = relation->next; @@ -366,7 +375,7 @@ WorkSpaceLayout *BKE_workspace_layout_add(Main *bmain, bScreen *screen, const char *name) { - WorkSpaceLayout *layout = MEM_callocN(sizeof(*layout), __func__); + WorkSpaceLayout *layout = MEM_cnew<WorkSpaceLayout>(__func__); BLI_assert(!workspaces_is_screen_used(bmain, screen)); #ifndef DEBUG @@ -393,7 +402,10 @@ void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLay void BKE_workspace_relations_free(ListBase *relation_list) { - for (WorkSpaceDataRelation *relation = relation_list->first, *relation_next; relation; + for (WorkSpaceDataRelation * + relation = static_cast<WorkSpaceDataRelation *>(relation_list->first), + *relation_next; + relation; relation = relation_next) { relation_next = relation->next; workspace_relation_remove(relation_list, relation); @@ -420,7 +432,7 @@ WorkSpaceLayout *BKE_workspace_layout_find(const WorkSpace *workspace, const bSc workspace->id.name + 2, screen->id.name + 2); - return NULL; + return nullptr; } WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain, @@ -430,10 +442,11 @@ WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain, WorkSpaceLayout *layout; if (r_workspace) { - *r_workspace = NULL; + *r_workspace = nullptr; } - for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace; + workspace = static_cast<WorkSpace *>(workspace->id.next)) { if ((layout = workspace_layout_find_exec(workspace, screen))) { if (r_workspace) { *r_workspace = workspace; @@ -443,7 +456,7 @@ WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain, } } - return NULL; + return nullptr; } WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace, @@ -464,15 +477,15 @@ WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace, LISTBASE_CIRCULAR_BACKWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start); } else { - LISTBASE_CIRCULAR_FORWARD_BEGIN (&workspace->layouts, iter_layout, start) { + LISTBASE_CIRCULAR_FORWARD_BEGIN (WorkSpaceLayout *, &workspace->layouts, iter_layout, start) { if (!callback(iter_layout, arg)) { return iter_layout; } } - LISTBASE_CIRCULAR_FORWARD_END(&workspace->layouts, iter_layout, start); + LISTBASE_CIRCULAR_FORWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start); } - return NULL; + return nullptr; } void BKE_workspace_tool_remove(struct WorkSpace *workspace, struct bToolRef *tref) @@ -494,13 +507,13 @@ bool BKE_workspace_owner_id_check(const WorkSpace *workspace, const char *owner_ } /* We could use hash lookup, for now this list is highly likely under < ~16 items. */ - return BLI_findstring(&workspace->owner_ids, owner_id, offsetof(wmOwnerID, name)) != NULL; + return BLI_findstring(&workspace->owner_ids, owner_id, offsetof(wmOwnerID, name)) != nullptr; } void BKE_workspace_id_tag_all_visible(Main *bmain, int tag) { BKE_main_id_tag_listbase(&bmain->workspaces, tag, false); - wmWindowManager *wm = bmain->wm.first; + wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); workspace->id.tag |= tag; @@ -523,14 +536,14 @@ void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) * that optimization is possible and needed. * This code can be called from places where we might have this equality, but still want to * ensure/update the active layout below. - * Known case where this is buggy and will crash later due to NULL active layout: reading + * Known case where this is buggy and will crash later due to nullptr active layout: reading * a blend file, when the new read workspace ID happens to have the exact same memory address * as when it was saved in the blend file (extremely unlikely, but possible). */ hook->active = workspace; if (workspace) { - WorkSpaceLayout *layout = workspace_relation_get_data_matching_parent( - &workspace->hook_layout_relations, hook); + WorkSpaceLayout *layout = static_cast<WorkSpaceLayout *>( + workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook)); if (layout) { hook->act_layout = layout; } @@ -551,7 +564,8 @@ WorkSpaceLayout *BKE_workspace_active_layout_for_workspace_get(const WorkSpaceIn } /* Inactive workspace */ - return workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook); + return static_cast<WorkSpaceLayout *>( + workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook)); } void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, |