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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern')
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc10
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc100
-rw-r--r--source/blender/blenkernel/intern/constraint.c6
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc63
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc304
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc37
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c146
-rw-r--r--source/blender/blenkernel/intern/lib_id.c76
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc112
-rw-r--r--source/blender/blenkernel/intern/material.c130
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c27
-rw-r--r--source/blender/blenkernel/intern/node.cc3
-rw-r--r--source/blender/blenkernel/intern/object.c10
-rw-r--r--source/blender/blenkernel/intern/ocean.c8
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c4
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc8
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc8
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc4
18 files changed, 929 insertions, 127 deletions
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index ca1d92c001b..d4dd7e248d5 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -712,11 +712,13 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free)
if (!em) {
ClothModifierData *clmd = (ClothModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Cloth);
- KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob),
- clmd->sim_parms->shapekey_rest);
+ if (clmd) {
+ KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob),
+ clmd->sim_parms->shapekey_rest);
- if (kb && kb->data) {
- return (float(*)[3])kb->data;
+ if (kb && kb->data) {
+ return (float(*)[3])kb->data;
+ }
}
}
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index c24630c5666..62833e10438 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -45,6 +45,7 @@ using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
namespace blender::bke {
@@ -590,6 +591,105 @@ void NamedLegacyCustomDataProvider::foreach_domain(
callback(domain_);
}
+CustomDataAttributes::CustomDataAttributes()
+{
+ CustomData_reset(&data);
+ size_ = 0;
+}
+
+CustomDataAttributes::~CustomDataAttributes()
+{
+ CustomData_free(&data, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
+{
+ size_ = other.size_;
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
+{
+ size_ = other.size_;
+ data = other.data;
+ CustomData_reset(&other.data);
+}
+
+std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
+{
+ BLI_assert(size_ != 0);
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
+{
+ BLI_assert(size_ != 0);
+ for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GMutableSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::create_by_move(const blender::StringRef name,
+ const CustomDataType data_type,
+ void *buffer)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::remove(const blender::StringRef name)
+{
+ bool result = false;
+ for (const int i : IndexRange(data.totlayer)) {
+ const CustomDataLayer &layer = data.layers[i];
+ if (layer.name == name) {
+ CustomData_free_layer(&data, layer.type, size_, i);
+ result = true;
+ }
+ }
+ return result;
+}
+
+void CustomDataAttributes::reallocate(const int size)
+{
+ size_ = size;
+ CustomData_realloc(&data, size);
+}
+
+bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 17f36bd0860..9293a2b449a 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1473,8 +1473,12 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* that's animated, but this will only work if it actually is animated...
*
* we divide the curvetime 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. */
curvetime /= cu->pathlen;
+
+ if (cu->flag & CU_PATH_CLAMP) {
+ CLAMP(curvetime, 0.0f, 1.0f);
+ }
}
else {
/* fixed position along curve */
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 19bbd8178b7..9cafe1124b1 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -16,7 +16,9 @@
#include "BLI_array.hh"
#include "BLI_listbase.h"
+#include "BLI_map.hh"
#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
#include "DNA_curve_types.h"
@@ -26,7 +28,9 @@
using blender::Array;
using blender::float3;
using blender::float4x4;
+using blender::Map;
using blender::Span;
+using blender::StringRefNull;
blender::Span<SplinePtr> CurveEval::splines() const
{
@@ -38,6 +42,9 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
return splines_;
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all splines.
+ */
void CurveEval::add_spline(SplinePtr spline)
{
splines_.append(std::move(spline));
@@ -50,17 +57,6 @@ void CurveEval::remove_splines(blender::IndexMask mask)
}
}
-CurveEval *CurveEval::copy()
-{
- CurveEval *new_curve = new CurveEval();
-
- for (SplinePtr &spline : this->splines()) {
- new_curve->add_spline(spline->copy());
- }
-
- return new_curve;
-}
-
void CurveEval::translate(const float3 &translation)
{
for (SplinePtr &spline : this->splines()) {
@@ -189,7 +185,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
bezt.radius,
bezt.tilt);
}
-
+ spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@@ -203,7 +199,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
}
-
+ spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@@ -214,7 +210,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt);
}
-
+ spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@@ -225,6 +221,9 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
}
}
+ /* Though the curve has no attributes, this is necessary to properly set the custom data size. */
+ curve->attributes.reallocate(curve->splines().size());
+
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
* from multiple curve objects, where the value may be different. */
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
@@ -235,3 +234,39 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
return curve;
}
+
+/**
+ * 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.
+ */
+void CurveEval::assert_valid_point_attributes() const
+{
+#ifdef DEBUG
+ if (splines_.size() == 0) {
+ return;
+ }
+ const int layer_len = splines_.first()->attributes.data.totlayer;
+ Map<StringRefNull, AttributeMetaData> map;
+ for (const SplinePtr &spline : splines_) {
+ BLI_assert(spline->attributes.data.totlayer == layer_len);
+ spline->attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ map.add_or_modify(
+ name,
+ [&](AttributeMetaData *map_data) {
+ /* All unique attribute names should be added on the first spline. */
+ BLI_assert(spline == splines_.first());
+ *map_data = meta_data;
+ },
+ [&](AttributeMetaData *map_data) {
+ /* Attributes on different splines should all have the same type. */
+ BLI_assert(meta_data == *map_data);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+#endif
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 44c5cce92dd..d08681da6ec 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -22,6 +22,12 @@
#include "attribute_access_intern.hh"
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_GSpan;
+using blender::fn::GVMutableArray_For_GMutableSpan;
+
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
@@ -39,7 +45,7 @@ GeometryComponent *CurveComponent::copy() const
{
CurveComponent *new_component = new CurveComponent();
if (curve_ != nullptr) {
- new_component->curve_ = curve_->copy();
+ new_component->curve_ = new CurveEval(*curve_);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
@@ -87,7 +93,7 @@ CurveEval *CurveComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
- curve_ = curve_->copy();
+ curve_ = new CurveEval(*curve_);
ownership_ = GeometryOwnershipType::Owned;
}
return curve_;
@@ -107,7 +113,7 @@ void CurveComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
- curve_ = curve_->copy();
+ curve_ = new CurveEval(*curve_);
ownership_ = GeometryOwnershipType::Owned;
}
}
@@ -445,6 +451,20 @@ template<typename T> class VMutableArray_For_SplinePoints final : public VMutabl
}
};
+template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+template<typename T>
+GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
+ offsets.last(), 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
@@ -581,8 +601,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
spans[i] = get_span_(*splines[i]);
}
- return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
- offsets.last(), std::move(spans), std::move(offsets));
+ return point_data_gvarray(spans, offsets);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
@@ -607,9 +626,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
}
}
- return std::make_unique<
- fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
- offsets.last(), std::move(spans), std::move(offsets));
+ return point_data_gvarray(spans, offsets);
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@@ -662,7 +679,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
}
/* Use the regular position virtual array when there aren't any Bezier splines
- * to avoid the overhead of thecking the spline type for every point. */
+ * to avoid the overhead of checking the spline type for every point. */
if (!curve_has_bezier_spline) {
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
@@ -683,6 +700,256 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
/** \} */
/* -------------------------------------------------------------------- */
+/** \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;
+
+ public:
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ 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_name);
+ 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_name);
+ 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 {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ ReadAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<Span<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_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ /* This function is almost the same as #try_get_for_read, but without const. */
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ 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_name);
+ 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_name);
+ 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 {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ WriteAttributeLookup 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_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ bool layer_freed = false;
+ for (SplinePtr &spline : curve->splines()) {
+ spline->attributes.remove(attribute_name);
+ }
+ return layer_freed;
+ }
+
+ static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
+ const CustomDataType data_type,
+ const int total_size)
+ {
+ switch (initializer.type) {
+ case AttributeInit::Type::Default:
+ /* 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->shallow_copy();
+ case AttributeInit::Type::MoveArray:
+ return std::make_unique<fn::GVArray_For_GSpan>(
+ GSpan(*bke::custom_data_type_to_cpp_type(data_type),
+ static_cast<const AttributeInitMove &>(initializer).data,
+ total_size));
+ }
+ BLI_assert_unreachable();
+ return {};
+ }
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final
+ {
+ BLI_assert(this->type_is_supported(data_type));
+ if (domain != ATTR_DOMAIN_POINT) {
+ return false;
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ 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 AttributeInitMove &>(initializer).data;
+ if (!splines[0]->attributes.create_by_move(attribute_name, 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_name, 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 (initializer.type == AttributeInit::Type::Default) {
+ return true;
+ }
+
+ WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
+ /* We just created the attribute, it should exist. */
+ BLI_assert(write_attribute);
+
+ const int total_size = curve->control_point_offsets().last();
+ GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
+ /* TODO: When we can call a variant of #set_all with a virtual array argument,
+ * this theoretically unnecessary materialize step could be removed. */
+ GVArray_GSpan source_varray_span{*source_varray};
+ write_attribute.varray->set_all(source_varray_span.data());
+
+ if (initializer.type == AttributeInit::Type::MoveArray) {
+ MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
+ }
+
+ return true;
+ }
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ 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(AttributeDomain)> callback) const final
+ {
+ callback(ATTR_DOMAIN_POINT);
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Attribute Provider Declaration
* \{ */
@@ -704,6 +971,20 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_cyclic_read_attribute,
make_cyclic_write_attribute);
+ static CustomDataAccessInfo spline_custom_data_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ nullptr};
+
+ static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
+ spline_custom_data_access);
+
static PositionAttributeProvider position;
static BuiltinPointAttributeProvider<float> radius(
@@ -720,7 +1001,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
[](Spline &spline) { return spline.tilts(); },
[](Spline &spline) { spline.mark_cache_invalid(); });
- return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, {});
+ static DynamicPointAttributeProvider point_custom_data;
+
+ return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
}
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index b6ee0337dbf..9abd00c2b4f 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -15,6 +15,7 @@
*/
#include "BKE_geometry_set_instances.hh"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
@@ -361,6 +362,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const int tot_transforms = set_group.transforms.size();
@@ -374,6 +377,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
+ for (const int slot_index : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[slot_index];
+ materials.add(material);
+ }
}
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
@@ -396,6 +403,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
break;
}
}
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -409,6 +420,14 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
+
+ Array<int> material_index_map(mesh.totcol);
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
@@ -438,6 +457,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh.totvert;
@@ -537,6 +563,17 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp
}
}
+ for (SplinePtr &spline : new_curve->splines()) {
+ /* Spline instances should have no custom attributes, since they always come
+ * from original objects which currently do not support custom attributes.
+ *
+ * This is only true as long as a #GeometrySet cannot be instanced directly. */
+ BLI_assert(spline->attributes.data.totlayer == 0);
+ UNUSED_VARS_NDEBUG(spline);
+ }
+
+ new_curve->attributes.reallocate(new_curve->splines().size());
+
result.replace(new_curve);
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 501ee0c2014..7f839650f33 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -530,14 +530,23 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/**
* Backbone stretch similar to Freestyle.
- * \param gps: Stroke to sample
- * \param dist: Distance of one segment
- * \param tip_length: Ignore tip jittering, set zero to use default value.
+ * \param gps: Stroke to sample.
+ * \param dist: Distance of one segment.
+ * \param overshoot_fac: How exact is the follow curve algorithm.
+ * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End)
*/
-bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
+bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode)
{
+#define BOTH 0
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
- float threshold = (tip_length == 0 ? 0.001f : tip_length);
+ int i;
+ float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac);
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
@@ -547,33 +556,36 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float t
second_last = &pt[gps->totpoints - 2];
next_pt = &pt[1];
- float len1 = 0.0f;
- float len2 = 0.0f;
+ if (mode == BOTH || mode == START) {
+ float len1 = 0.0f;
+ i = 1;
+ while (len1 < threshold && gps->totpoints > i) {
+ next_pt = &pt[i];
+ len1 = len_v3v3(&next_pt->x, &pt->x);
+ i++;
+ }
+ float extend1 = (len1 + dist) / len1;
+ float result1[3];
- int i = 1;
- while (len1 < threshold && gps->totpoints > i) {
- next_pt = &pt[i];
- len1 = len_v3v3(&next_pt->x, &pt->x);
- i++;
+ interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
+ copy_v3_v3(&pt->x, result1);
}
- i = 2;
- while (len2 < threshold && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- len2 = len_v3v3(&last_pt->x, &second_last->x);
- i++;
- }
-
- float extend1 = (len1 + dist) / len1;
- float extend2 = (len2 + dist) / len2;
-
- float result1[3], result2[3];
+ if (mode == BOTH || mode == END) {
+ float len2 = 0.0f;
+ i = 2;
+ while (len2 < threshold && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 = len_v3v3(&last_pt->x, &second_last->x);
+ i++;
+ }
- interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
- interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
+ float extend2 = (len2 + dist) / len2;
+ float result2[3];
+ interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
- copy_v3_v3(&pt->x, result1);
- copy_v3_v3(&last_pt->x, result2);
+ copy_v3_v3(&last_pt->x, result2);
+ }
return true;
}
@@ -702,48 +714,64 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
* Shrink the stroke by length.
* \param gps: Stroke to shrink
* \param dist: delta length
+ * \param mode: 1->Start, 2->End
*/
-bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
+bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
{
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *second_last;
int i;
- if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+ if (gps->totpoints < 2) {
+ if (gps->totpoints == 1) {
+ second_last = &pt[1];
+ if (len_v3v3(&second_last->x, &pt->x) < dist) {
+ BKE_gpencil_stroke_trim_points(gps, 0, 0);
+ return true;
+ }
+ }
+
return false;
}
second_last = &pt[gps->totpoints - 2];
- float len1, this_len1, cut_len1;
- float len2, this_len2, cut_len2;
- int index_start, index_end;
-
- len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
-
- i = 1;
- while (len1 < dist && gps->totpoints > i - 1) {
- this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
- len1 += this_len1;
- cut_len1 = len1 - dist;
- i++;
+ float len1, cut_len1;
+ float len2, cut_len2;
+ len1 = len2 = cut_len1 = cut_len2 = 0.0f;
+
+ int index_start = 0;
+ int index_end = 0;
+ if (mode == START) {
+ i = 0;
+ index_end = gps->totpoints - 1;
+ while (len1 < dist && gps->totpoints > i + 1) {
+ len1 += len_v3v3(&pt[i].x, &pt[i + 1].x);
+ cut_len1 = len1 - dist;
+ i++;
+ }
+ index_start = i - 1;
}
- index_start = i - 2;
- i = 2;
- while (len2 < dist && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
- len2 += this_len2;
- cut_len2 = len2 - dist;
- i++;
+ if (mode == END) {
+ index_start = 0;
+ i = 2;
+ while (len2 < dist && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 += len_v3v3(&second_last[1].x, &second_last->x);
+ cut_len2 = len2 - dist;
+ i++;
+ }
+ index_end = gps->totpoints - i + 2;
}
- index_end = gps->totpoints - i + 2;
- if (len1 < dist || len2 < dist || index_end <= index_start) {
+ if (index_end <= index_start) {
index_start = index_end = 0; /* empty stroke */
}
- if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
+ if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) {
index_start = index_end = 0; /* no length left to cut */
}
@@ -753,22 +781,8 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
return false;
}
- pt = gps->points;
-
- float cut1 = cut_len1 / this_len1;
- float cut2 = cut_len2 / this_len2;
-
- float result1[3], result2[3];
-
- interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
- interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
-
- copy_v3_v3(&pt[0].x, result1);
- copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
-
return true;
}
-
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 795a5ad9468..c2e5006cbc1 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -525,7 +525,13 @@ static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data)
/* Increase used IDs refcount if needed and required. */
if ((data->flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && (cb_flag & IDWALK_CB_USER)) {
- id_us_plus(id);
+ if ((data->flag & LIB_ID_CREATE_NO_MAIN) != 0) {
+ BLI_assert(cb_data->id_self->tag & LIB_TAG_NO_MAIN);
+ id_us_plus_no_lib(id);
+ }
+ else {
+ id_us_plus(id);
+ }
}
return IDWALK_RET_NOP;
@@ -578,7 +584,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag)
}
}
- /* Early output is source is NULL. */
+ /* Early output if source is NULL. */
if (id == NULL) {
return NULL;
}
@@ -1245,6 +1251,13 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
}
BLI_assert(new_id != NULL);
+ if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) != 0) {
+ new_id->tag |= LIB_TAG_COPIED_ON_WRITE;
+ }
+ else {
+ new_id->tag &= ~LIB_TAG_COPIED_ON_WRITE;
+ }
+
const size_t id_len = BKE_libblock_get_alloc_info(GS(new_id->name), NULL);
const size_t id_offset = sizeof(ID);
if ((int)id_len - (int)id_offset > 0) { /* signed to allow neg result */ /* XXX ????? */
@@ -1348,12 +1361,12 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
BLI_remlink(lb, id);
/* Check if we can actually insert id before or after id_sorting_hint, if given. */
- if (!ELEM(id_sorting_hint, NULL, id)) {
+ if (!ELEM(id_sorting_hint, NULL, id) && id_sorting_hint->lib == id->lib) {
BLI_assert(BLI_findindex(lb, id_sorting_hint) >= 0);
ID *id_sorting_hint_next = id_sorting_hint->next;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) < 0 &&
- (id_sorting_hint_next == NULL ||
+ (id_sorting_hint_next == NULL || id_sorting_hint_next->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_next->name, id->name) > 0)) {
BLI_insertlinkafter(lb, id_sorting_hint, id);
return;
@@ -1361,7 +1374,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
ID *id_sorting_hint_prev = id_sorting_hint->prev;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) > 0 &&
- (id_sorting_hint_prev == NULL ||
+ (id_sorting_hint_prev == NULL || id_sorting_hint_prev->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_prev->name, id->name) < 0)) {
BLI_insertlinkbefore(lb, id_sorting_hint, id);
return;
@@ -1376,16 +1389,33 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
/* Note: We start from the end, because in typical 'heavy' case (insertion of lots of IDs at
* once using the same base name), newly inserted items will generally be towards the end
* (higher extension numbers). */
- for (idtest = lb->last, item_array_index = ID_SORT_STEP_SIZE - 1; idtest != NULL;
- idtest = idtest->prev, item_array_index--) {
+ bool is_in_library = false;
+ item_array_index = ID_SORT_STEP_SIZE - 1;
+ for (idtest = lb->last; idtest != NULL; idtest = idtest->prev) {
+ if (is_in_library) {
+ if (idtest->lib != id->lib) {
+ /* We got out of expected library 'range' in the list, so we are done here and can move on
+ * to the next step. */
+ break;
+ }
+ }
+ else if (idtest->lib == id->lib) {
+ /* We are entering the expected library 'range' of IDs in the list. */
+ is_in_library = true;
+ }
+
+ if (!is_in_library) {
+ continue;
+ }
+
item_array[item_array_index] = idtest;
if (item_array_index == 0) {
- if ((idtest->lib == NULL && id->lib != NULL) ||
- BLI_strcasecmp(idtest->name, id->name) <= 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) <= 0) {
break;
}
item_array_index = ID_SORT_STEP_SIZE;
}
+ item_array_index--;
}
/* Step two: we go forward in the selected chunk of items and check all of them, as we know
@@ -1397,7 +1427,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
* So we can increment that index in any case. */
for (item_array_index++; item_array_index < ID_SORT_STEP_SIZE; item_array_index++) {
idtest = item_array[item_array_index];
- if ((idtest->lib != NULL && id->lib == NULL) || BLI_strcasecmp(idtest->name, id->name) > 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) > 0) {
BLI_insertlinkbefore(lb, idtest, id);
break;
}
@@ -1405,12 +1435,18 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
if (item_array_index == ID_SORT_STEP_SIZE) {
if (idtest == NULL) {
/* If idtest is NULL here, it means that in the first loop, the last comparison was
- * performed exactly on the first item of the list, and that it also failed. In other
- * words, all items in the list are greater than inserted one, so we can put it at the
- * start of the list. */
- /* Note that BLI_insertlinkafter() would have same behavior in that case, but better be
- * explicit here. */
- BLI_addhead(lb, id);
+ * performed exactly on the first item of the list, and that it also failed. And that the
+ * second loop was not walked at all.
+ *
+ * In other words, if `id` is local, all the items in the list are greater than the inserted
+ * one, so we can put it at the start of the list. Or, if `id` is linked, it is the first one
+ * of its library, and we can put it at the very end of the list. */
+ if (ID_IS_LINKED(id)) {
+ BLI_addtail(lb, id);
+ }
+ else {
+ BLI_addhead(lb, id);
+ }
}
else {
BLI_insertlinkafter(lb, idtest, id);
@@ -1678,12 +1714,14 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
*/
bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname)
{
- bool result;
+ bool result = false;
char name[MAX_ID_NAME - 2];
- /* if library, don't rename */
+ /* If library, don't rename, but do ensure proper sorting. */
if (ID_IS_LINKED(id)) {
- return false;
+ id_sort_by_name(lb, id, NULL);
+
+ return result;
}
/* if no name given, use name of current ID
diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc
new file mode 100644
index 00000000000..9bfd19ae4d6
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_id_test.cc
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 by Blender Foundation.
+ */
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+
+namespace blender::bke::tests {
+
+struct LibIDMainSortTestContext {
+ Main *bmain;
+};
+
+static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx)
+{
+ ctx->bmain = BKE_main_new();
+}
+
+static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx)
+{
+ BKE_main_free(ctx->bmain);
+}
+
+static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list)
+{
+ ID *prev_id = nullptr;
+ for (ID *id : list) {
+ EXPECT_EQ(id->prev, prev_id);
+ if (prev_id != nullptr) {
+ EXPECT_EQ(prev_id->next, id);
+ }
+ prev_id = id;
+ }
+ EXPECT_EQ(prev_id->next, nullptr);
+}
+
+TEST(lib_id_main_sort, local_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+ EXPECT_TRUE(ctx.bmain->objects.first == id_a);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_c);
+ test_lib_id_main_sort_check_order({id_a, id_b, id_c});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_sort, linked_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A"));
+ Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+
+ id_a->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ id_b->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ id_a->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_a);
+ test_lib_id_main_sort_check_order({id_c, id_b, id_a});
+
+ id_b->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 37d47a984cc..73b64e6efb3 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -77,6 +77,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "GPU_material.h"
@@ -700,6 +701,98 @@ Material *BKE_object_material_get(Object *ob, short act)
return ma_p ? *ma_p : NULL;
}
+static ID *get_evaluated_object_data_with_materials(Object *ob)
+{
+ ID *data = ob->data;
+ /* Meshes in edit mode need special handling. */
+ if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) {
+ Mesh *mesh = ob->data;
+ if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) {
+ data = &mesh->edit_mesh->mesh_eval_final->id;
+ }
+ }
+ return data;
+}
+
+/**
+ * On evaluated objects the number of materials on an object and its data might go out of sync.
+ * This is because during evaluation materials can be added/removed on the object data.
+ *
+ * For rendering or exporting we generally use the materials on the object data. However, some
+ * material indices might be overwritten by the object.
+ */
+Material *BKE_object_material_get_eval(Object *ob, short act)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ const int slot_index = act - 1;
+
+ if (slot_index < 0) {
+ return NULL;
+ }
+ ID *data = get_evaluated_object_data_with_materials(ob);
+ const short *tot_slots_data_ptr = BKE_id_material_len_p(data);
+ const int tot_slots_data = tot_slots_data_ptr ? *tot_slots_data_ptr : 0;
+ if (slot_index >= tot_slots_data) {
+ return NULL;
+ }
+ const int tot_slots_object = ob->totcol;
+
+ Material ***materials_data_ptr = BKE_id_material_array_p(data);
+ Material **materials_data = materials_data_ptr ? *materials_data_ptr : NULL;
+ Material **materials_object = ob->mat;
+
+ /* Check if slot is overwritten by object. */
+ if (slot_index < tot_slots_object) {
+ if (ob->matbits) {
+ if (ob->matbits[slot_index]) {
+ Material *material = materials_object[slot_index];
+ if (material != NULL) {
+ return material;
+ }
+ }
+ }
+ }
+ /* Otherwise use data from object-data. */
+ if (slot_index < tot_slots_data) {
+ Material *material = materials_data[slot_index];
+ return material;
+ }
+ return NULL;
+}
+
+int BKE_object_material_count_eval(Object *ob)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ ID *id = get_evaluated_object_data_with_materials(ob);
+ const short *len_p = BKE_id_material_len_p(id);
+ return len_p ? *len_p : 0;
+}
+
+void BKE_id_material_eval_assign(ID *id, int slot, Material *material)
+{
+ Material ***materials_ptr = BKE_id_material_array_p(id);
+ short *len_ptr = BKE_id_material_len_p(id);
+ if (ELEM(NULL, materials_ptr, len_ptr)) {
+ BLI_assert_unreachable();
+ return;
+ }
+
+ const int slot_index = slot - 1;
+ const int old_length = *len_ptr;
+
+ if (slot_index >= old_length) {
+ /* Need to grow slots array. */
+ const int new_length = slot_index + 1;
+ *materials_ptr = MEM_reallocN(*materials_ptr, sizeof(void *) * new_length);
+ *len_ptr = new_length;
+ for (int i = old_length; i < new_length; i++) {
+ (*materials_ptr)[i] = NULL;
+ }
+ }
+
+ (*materials_ptr)[slot_index] = material;
+}
+
Material *BKE_gpencil_material(Object *ob, short act)
{
Material *ma = BKE_object_material_get(ob, act);
@@ -1028,6 +1121,43 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
BLI_ghash_free(gh_mat_map, NULL, NULL);
}
+/**
+ * Copy materials from evaluated geometry to the original geometry of an object.
+ */
+void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval)
+{
+ ID *data_orig = ob_orig->data;
+
+ short *orig_totcol = BKE_id_material_len_p(data_orig);
+ Material ***orig_mat = BKE_id_material_array_p(data_orig);
+
+ short *eval_totcol = BKE_id_material_len_p(data_eval);
+ Material ***eval_mat = BKE_id_material_array_p(data_eval);
+
+ if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) {
+ return;
+ }
+
+ /* Remove old materials from original geometry. */
+ for (int i = 0; i < *orig_totcol; i++) {
+ id_us_min(&(*orig_mat)[i]->id);
+ }
+ MEM_SAFE_FREE(*orig_mat);
+
+ /* Create new material slots based on materials on evaluated geometry. */
+ *orig_totcol = *eval_totcol;
+ *orig_mat = MEM_callocN(sizeof(void *) * (*eval_totcol), __func__);
+ for (int i = 0; i < *eval_totcol; i++) {
+ Material *material_eval = (*eval_mat)[i];
+ if (material_eval != NULL) {
+ Material *material_orig = (Material *)DEG_get_original_id(&material_eval->id);
+ (*orig_mat)[i] = material_orig;
+ id_us_plus(&material_orig->id);
+ }
+ }
+ BKE_object_materials_test(bmain, ob_orig, data_orig);
+}
+
/* XXX - this calls many more update calls per object then are needed, could be optimized */
void BKE_object_material_array_assign(Main *bmain,
struct Object *ob,
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 098d9c420aa..e893a3983bd 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1211,7 +1211,9 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh)
return mesh_result;
}
-static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object *object)
+static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_origindex)
{
if (DEG_is_original_id(&object->id)) {
return mesh_new_from_mesh(object, (Mesh *)object->data);
@@ -1228,16 +1230,23 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object
Scene *scene = DEG_get_evaluated_scene(depsgraph);
CustomData_MeshMasks mask = CD_MASK_MESH;
+ if (preserve_origindex) {
+ mask.vmask |= CD_MASK_ORIGINDEX;
+ mask.emask |= CD_MASK_ORIGINDEX;
+ mask.lmask |= CD_MASK_ORIGINDEX;
+ mask.pmask |= CD_MASK_ORIGINDEX;
+ }
Mesh *result = mesh_create_eval_final(depsgraph, scene, &object_for_eval, &mask);
return result;
}
static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
Object *object,
- bool preserve_all_data_layers)
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex)
{
- if (preserve_all_data_layers) {
- return mesh_new_from_mesh_object_with_layers(depsgraph, object);
+ if (preserve_all_data_layers || preserve_origindex) {
+ return mesh_new_from_mesh_object_with_layers(depsgraph, object, preserve_origindex);
}
Mesh *mesh_input = object->data;
/* If we are in edit mode, use evaluated mesh from edit structure, matching to what
@@ -1248,7 +1257,10 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
return mesh_new_from_mesh(object, mesh_input);
}
-Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers)
+Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex)
{
Mesh *new_mesh = NULL;
switch (object->type) {
@@ -1261,7 +1273,8 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preser
new_mesh = mesh_new_from_mball_object(object);
break;
case OB_MESH:
- new_mesh = mesh_new_from_mesh_object(depsgraph, object, preserve_all_data_layers);
+ new_mesh = mesh_new_from_mesh_object(
+ depsgraph, object, preserve_all_data_layers, preserve_origindex);
break;
default:
/* Object does not have geometry data. */
@@ -1326,7 +1339,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
{
BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH));
- Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers);
+ Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
if (mesh == NULL) {
/* Unable to convert the object to a mesh, return an empty one. */
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2);
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index a0e444186f2..643dc58af18 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5053,8 +5053,11 @@ static void registerGeometryNodes()
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_resample();
register_node_type_geo_edge_split();
+ register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
+ register_node_type_geo_material_assign();
+ register_node_type_geo_material_replace();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 02be16d1d28..034af924ab1 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -2289,7 +2289,7 @@ Object *BKE_object_add_for_data(
void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int flag)
{
SoftBody *sb = ob_src->soft;
- bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN;
+ const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0;
ob_dst->softflag = ob_src->softflag;
if (sb == NULL) {
@@ -2330,7 +2330,7 @@ void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int fl
sbn->scratch = NULL;
- if (!tagged_no_main) {
+ if (is_orig) {
sbn->shared = MEM_dupallocN(sb->shared);
sbn->shared->pointcache = BKE_ptcache_copy_list(
&sbn->shared->ptcaches, &sb->shared->ptcaches, flag);
@@ -3285,6 +3285,10 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
ctime = cu->ctime;
}
+ if (cu->flag & CU_PATH_CLAMP) {
+ CLAMP(ctime, 0.0f, 1.0f);
+ }
+
unit_m4(r_mat);
/* vec: 4 items! */
@@ -5668,7 +5672,7 @@ Mesh *BKE_object_to_mesh(Depsgraph *depsgraph, Object *object, bool preserve_all
{
BKE_object_to_mesh_clear(object);
- Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers);
+ Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
object->runtime.object_as_temp_mesh = mesh;
return mesh;
}
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index d2f4d0702ed..9d53dad8d03 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -911,8 +911,12 @@ void BKE_ocean_init(struct Ocean *o,
for (i = 0; i < o->_M; i++) {
for (j = 0; j < o->_N; j++) {
/* This ensures we get a value tied to the surface location, avoiding dramatic surface
- * change with changing resolution. */
- int new_seed = seed + BLI_hash_int_2d(o->_kx[i] * 360.0f, o->_kz[j] * 360.0f);
+ * change with changing resolution.
+ * Explicitly cast to signed int first to ensure consistent behavior on all processors,
+ * since behavior of float to unsigned int cast is undefined in C. */
+ const int hash_x = o->_kx[i] * 360.0f;
+ const int hash_z = o->_kz[j] * 360.0f;
+ int new_seed = seed + BLI_hash_int_2d(hash_x, hash_z);
BLI_rng_seed(rng, new_seed);
float r1 = gaussRand(rng);
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 19078446009..2539b990210 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -260,10 +260,12 @@ static RigidBodyOb *rigidbody_copy_object(const Object *ob, const int flag)
RigidBodyOb *rboN = NULL;
if (ob->rigidbody_object) {
+ const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0;
+
/* just duplicate the whole struct first (to catch all the settings) */
rboN = MEM_dupallocN(ob->rigidbody_object);
- if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
+ if (is_orig) {
/* This is a regular copy, and not a CoW copy for depsgraph evaluation */
rboN->shared = MEM_callocN(sizeof(*rboN->shared), "RigidBodyOb_Shared");
}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index 58a8f46730a..4be3ba8576e 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -55,6 +55,9 @@ void BezierSpline::set_resolution(const int value)
this->mark_cache_invalid();
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
void BezierSpline::add_point(const float3 position,
const HandleType handle_type_start,
const float3 handle_position_start,
@@ -83,6 +86,7 @@ void BezierSpline::resize(const int size)
radii_.resize(size);
tilts_.resize(size);
this->mark_cache_invalid();
+ attributes.reallocate(size);
}
MutableSpan<float3> BezierSpline::positions()
@@ -556,6 +560,10 @@ blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
{
BLI_assert(source_data.size() == this->size());
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
const int eval_size = this->evaluated_points_size();
if (eval_size == 1) {
return source_data.shallow_copy();
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
index 7816f303e2e..ae691d26cdb 100644
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -65,6 +65,9 @@ void NURBSpline::set_order(const uint8_t value)
this->mark_cache_invalid();
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
void NURBSpline::add_point(const float3 position,
const float radius,
const float tilt,
@@ -85,6 +88,7 @@ void NURBSpline::resize(const int size)
tilts_.resize(size);
weights_.resize(size);
this->mark_cache_invalid();
+ attributes.reallocate(size);
}
MutableSpan<float3> NURBSpline::positions()
@@ -385,6 +389,10 @@ blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
{
BLI_assert(source_data.size() == this->size());
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
this->calculate_basis_cache();
Span<BasisCache> weights(basis_cache_);
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
index ab6f4704a88..5c33b0052fc 100644
--- a/source/blender/blenkernel/intern/spline_poly.cc
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -36,6 +36,9 @@ int PolySpline::size() const
return size;
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
void PolySpline::add_point(const float3 position, const float radius, const float tilt)
{
positions_.append(position);
@@ -50,6 +53,7 @@ void PolySpline::resize(const int size)
radii_.resize(size);
tilts_.resize(size);
this->mark_cache_invalid();
+ attributes.reallocate(size);
}
MutableSpan<float3> PolySpline::positions()