diff options
Diffstat (limited to 'source/blender/blenkernel/intern')
87 files changed, 4598 insertions, 3366 deletions
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 368b72dbeb1..750f2412c9c 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -66,6 +66,9 @@ # include "DNA_userdef_types.h" #endif +using blender::float3; +using blender::IndexRange; + /* very slow! enable for testing only! */ //#define USE_MODIFIER_VALIDATE @@ -81,6 +84,8 @@ static ThreadRWMutex loops_cache_lock = PTHREAD_RWLOCK_INITIALIZER; static void mesh_init_origspace(Mesh *mesh); static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); +static void editbmesh_calc_modifier_final_normals_or_defer( + Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); /* -------------------------------------------------------------------- */ @@ -660,8 +665,8 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval) mesh_eval->edit_mesh = mesh_input->edit_mesh; } -void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval, - const CustomData_MeshMasks *cd_mask_finalize) +void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval, + const CustomData_MeshMasks *cd_mask_finalize) { if (me_eval->runtime.wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) { editbmesh_calc_modifier_final_normals(me_eval, cd_mask_finalize); @@ -815,6 +820,25 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* Clear errors before evaluation. */ BKE_modifiers_clear_errors(ob); + if (ob->modifier_flag & OB_MODIFIER_FLAG_ADD_REST_POSITION) { + if (mesh_final == nullptr) { + mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); + ASSERT_IS_VALID_MESH(mesh_final); + } + float3 *rest_positions = static_cast<float3 *>(CustomData_add_layer_named(&mesh_final->vdata, + CD_PROP_FLOAT3, + CD_DEFAULT, + nullptr, + mesh_final->totvert, + "rest_position")); + blender::threading::parallel_for( + IndexRange(mesh_final->totvert), 1024, [&](const IndexRange range) { + for (const int i : range) { + rest_positions[i] = mesh_final->mvert[i].co; + } + }); + } + /* Apply all leading deform modifiers. */ if (use_deform) { for (; md; md = md->next, md_datamask = md_datamask->next) { @@ -1269,12 +1293,6 @@ bool editbmesh_modifier_is_enabled(const Scene *scene, static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) { - if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { - /* Generated at draw time. */ - mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); - return; - } - const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || (final_datamask->lmask & CD_MASK_NORMAL) != 0); @@ -1302,6 +1320,18 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, } } +static void editbmesh_calc_modifier_final_normals_or_defer( + Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) +{ + if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { + /* Generated at draw time. */ + mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); + return; + } + + editbmesh_calc_modifier_final_normals(mesh_final, final_datamask); +} + static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, const Scene *scene, Object *ob, @@ -1581,9 +1611,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } /* Compute normals. */ - editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_final, &final_datamask); if (mesh_cage && (mesh_cage != mesh_final)) { - editbmesh_calc_modifier_final_normals(mesh_cage, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_cage, &final_datamask); } /* Return final mesh. */ diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c index 7ef561c8216..0663538f3bb 100644 --- a/source/blender/blenkernel/intern/action_mirror.c +++ b/source/blender/blenkernel/intern/action_mirror.c @@ -363,7 +363,7 @@ static void action_flip_pchan(Object *ob_arm, /* Recalculate handles. */ for (int i = 0; i < fcurve_array_len; i++) { - calchandles_fcurve_ex(fcurve_array[i], 0); + BKE_fcurve_handles_recalc_ex(fcurve_array[i], 0); } MEM_freeN((void *)keyed_frames); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index dfe3d9cc310..f29074c827c 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2661,13 +2661,31 @@ BoundBox *BKE_armature_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_pchan_minmax(const Object *ob, const bPoseChannel *pchan, float r_min[3], float r_max[3]) +void BKE_pchan_minmax(const Object *ob, + const bPoseChannel *pchan, + const bool use_empty_drawtype, + float r_min[3], + float r_max[3]) { const bArmature *arm = ob->data; - const bPoseChannel *pchan_tx = (pchan->custom && pchan->custom_tx) ? pchan->custom_tx : pchan; - const BoundBox *bb_custom = ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) ? - BKE_object_boundbox_get(pchan->custom) : - NULL; + Object *ob_custom = (arm->flag & ARM_NO_CUSTOM) ? NULL : pchan->custom; + const bPoseChannel *pchan_tx = (ob_custom && pchan->custom_tx) ? pchan->custom_tx : pchan; + const BoundBox *bb_custom = NULL; + BoundBox bb_custom_buf; + + if (ob_custom) { + float min[3], max[3]; + if (use_empty_drawtype && (ob_custom->type == OB_EMPTY) && + BKE_object_minmax_empty_drawtype(ob_custom, min, max)) { + memset(&bb_custom_buf, 0x0, sizeof(bb_custom_buf)); + BKE_boundbox_init_from_minmax(&bb_custom_buf, min, max); + bb_custom = &bb_custom_buf; + } + else { + bb_custom = BKE_object_boundbox_get(ob_custom); + } + } + if (bb_custom) { float mat[4][4], smat[4][4], rmat[4][4], tmp[4][4]; scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan)); @@ -2704,7 +2722,7 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden if (pchan->bone && (!((use_hidden == false) && (PBONE_VISIBLE(arm, pchan->bone) == false)) && !((use_select == true) && ((pchan->bone->flag & BONE_SELECTED) == 0)))) { - BKE_pchan_minmax(ob, pchan, r_min, r_max); + BKE_pchan_minmax(ob, pchan, false, r_min, r_max); changed = true; } } diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c index 0769049e9a9..cc1bfcc087c 100644 --- a/source/blender/blenkernel/intern/armature_deform.c +++ b/source/blender/blenkernel/intern/armature_deform.c @@ -159,9 +159,9 @@ float distfactor_to_bone( } static float dist_bone_deform( - bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3]) + const bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3]) { - Bone *bone = pchan->bone; + const Bone *bone = pchan->bone; float fac, contrib = 0.0; if (bone == NULL) { @@ -188,7 +188,7 @@ static float dist_bone_deform( return contrib; } -static void pchan_bone_deform(bPoseChannel *pchan, +static void pchan_bone_deform(const bPoseChannel *pchan, float weight, float vec[3], DualQuat *dq, @@ -196,7 +196,7 @@ static void pchan_bone_deform(bPoseChannel *pchan, const float co[3], float *contrib) { - Bone *bone = pchan->bone; + const Bone *bone = pchan->bone; if (!weight) { return; @@ -223,7 +223,6 @@ static void pchan_bone_deform(bPoseChannel *pchan, typedef struct ArmatureUserdata { const Object *ob_arm; - const Object *ob_target; const Mesh *me_target; float (*vert_coords)[3]; float (*vert_deform_mats)[3][3]; @@ -264,7 +263,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data, const int armature_def_nr = data->armature_def_nr; DualQuat sumdq, *dq = NULL; - bPoseChannel *pchan; + const bPoseChannel *pchan; float *co, dco[3]; float sumvec[3], summat[3][3]; float *vec = NULL, (*smat)[3] = NULL; @@ -319,7 +318,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data, const uint index = dw->def_nr; if (index < data->defbase_len && (pchan = data->pchan_from_defbase[index])) { float weight = dw->weight; - Bone *bone = pchan->bone; + const Bone *bone = pchan->bone; deformed = 1; @@ -434,7 +433,7 @@ static void armature_vert_task_editmesh(void *__restrict userdata, { const ArmatureUserdata *data = userdata; BMVert *v = (BMVert *)iter; - MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset); + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset); armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert); } @@ -459,15 +458,14 @@ static void armature_deform_coords_impl(const Object *ob_arm, BMEditMesh *em_target, bGPDstroke *gps_target) { - bArmature *arm = ob_arm->data; + const bArmature *arm = ob_arm->data; bPoseChannel **pchan_from_defbase = NULL; const MDeformVert *dverts = NULL; - bDeformGroup *dg; const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0; const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0; const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0; - int defbase_len = 0; /* safety for vertexgroup index overflow */ - int i, dverts_len = 0; /* safety for vertexgroup overflow */ + int defbase_len = 0; /* safety for vertexgroup index overflow */ + int dverts_len = 0; /* safety for vertexgroup overflow */ bool use_dverts = false; int armature_def_nr = -1; int cd_dvert_offset = -1; @@ -485,14 +483,11 @@ static void armature_deform_coords_impl(const Object *ob_arm, } if (BKE_object_supports_vertex_groups(ob_target)) { - /* get the def_nr for the overall armature vertex group if present */ - armature_def_nr = BKE_object_defgroup_name_index(ob_target, defgrp_name); - - defbase_len = BKE_object_defgroup_count(ob_target); - + const ID *target_data_id = NULL; if (ob_target->type == OB_MESH) { + target_data_id = me_target == NULL ? (const ID *)ob_target->data : &me_target->id; if (em_target == NULL) { - Mesh *me = ob_target->data; + const Mesh *me = (const Mesh *)target_data_id; dverts = me->dvert; if (dverts) { dverts_len = me->totvert; @@ -500,19 +495,26 @@ static void armature_deform_coords_impl(const Object *ob_arm, } } else if (ob_target->type == OB_LATTICE) { - Lattice *lt = ob_target->data; + const Lattice *lt = ob_target->data; + target_data_id = (const ID *)ob_target->data; dverts = lt->dvert; if (dverts) { dverts_len = lt->pntsu * lt->pntsv * lt->pntsw; } } else if (ob_target->type == OB_GPENCIL) { + target_data_id = (const ID *)ob_target->data; dverts = gps_target->dvert; if (dverts) { dverts_len = gps_target->totpoints; } } + /* Collect the vertex group names from the evaluated data. */ + armature_def_nr = BKE_id_defgroup_name_index(target_data_id, defgrp_name); + const ListBase *defbase = BKE_id_defgroup_list_get(target_data_id); + defbase_len = BLI_listbase_count(defbase); + /* get a vertex-deform-index to posechannel array */ if (deformflag & ARM_DEF_VGROUP) { /* if we have a Mesh, only use dverts if it has them */ @@ -533,8 +535,8 @@ static void armature_deform_coords_impl(const Object *ob_arm, * * - Check whether keeping this consistent across frames gives speedup. */ - const ListBase *defbase = BKE_object_defgroup_list(ob_target); - for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { + int i; + LISTBASE_FOREACH_INDEX (bDeformGroup *, dg, defbase, i) { pchan_from_defbase[i] = BKE_pose_channel_find_name(ob_arm->pose, dg->name); /* exclude non-deforming bones */ if (pchan_from_defbase[i]) { @@ -549,7 +551,6 @@ static void armature_deform_coords_impl(const Object *ob_arm, ArmatureUserdata data = { .ob_arm = ob_arm, - .ob_target = ob_target, .me_target = me_target, .vert_coords = vert_coords, .vert_deform_mats = vert_deform_mats, diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 7c09b4a4ce3..639e190a2c6 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -8,6 +8,7 @@ */ #include <cstring> +#include <optional> #include "MEM_guardedalloc.h" @@ -23,8 +24,8 @@ #include "BLI_string_utils.h" #include "BKE_attribute.h" -#include "BKE_attribute_access.hh" -#include "BKE_curves.h" +#include "BKE_attribute.hh" +#include "BKE_curves.hh" #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_pointcloud.h" @@ -89,6 +90,36 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) } } +namespace blender::bke { + +static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_accessor_for_write( + ID &id) +{ + switch (GS(id.name)) { + case ID_ME: { + Mesh &mesh = reinterpret_cast<Mesh &>(id); + /* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */ + BLI_assert(mesh.edit_mesh == nullptr); + return mesh_attributes_for_write(mesh); + } + case ID_PT: { + PointCloud &pointcloud = reinterpret_cast<PointCloud &>(id); + return pointcloud_attributes_for_write(pointcloud); + } + case ID_CV: { + Curves &curves_id = reinterpret_cast<Curves &>(id); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + return curves.attributes_for_write(); + } + default: { + BLI_assert_unreachable(); + return {}; + } + } +} + +} // namespace blender::bke + bool BKE_id_attributes_supported(const ID *id) { DomainInfo info[ATTR_DOMAIN_NUM]; @@ -115,6 +146,13 @@ bool BKE_id_attribute_rename(ID *id, BLI_assert_msg(0, "Required attribute name is not editable"); return false; } + if (STREQ(new_name, "")) { + BKE_report(reports, RPT_ERROR, "Attribute name can not be empty"); + return false; + } + if (STREQ(old_name, new_name)) { + return false; + } CustomDataLayer *layer = BKE_id_attribute_search( id, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); @@ -172,6 +210,7 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname) CustomDataLayer *BKE_id_attribute_new( ID *id, const char *name, const int type, const eAttrDomain domain, ReportList *reports) { + using namespace blender::bke; DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -184,64 +223,61 @@ CustomDataLayer *BKE_id_attribute_new( char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; BKE_id_attribute_calc_unique_name(id, name, uniquename); - switch (GS(id->name)) { - case ID_ME: { - Mesh *me = (Mesh *)id; - BMEditMesh *em = me->edit_mesh; - if (em != nullptr) { - BM_data_layer_add_named(em->bm, customdata, type, uniquename); - } - else { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - } - break; - } - default: { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - break; + if (GS(id->name) == ID_ME) { + Mesh *mesh = reinterpret_cast<Mesh *>(id); + if (BMEditMesh *em = mesh->edit_mesh) { + BM_data_layer_add_named(em->bm, customdata, type, uniquename); + const int index = CustomData_get_named_layer_index(customdata, type, uniquename); + return (index == -1) ? nullptr : &(customdata->layers[index]); } } + std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id); + if (!attributes) { + return nullptr; + } + + attributes->add(uniquename, domain, eCustomDataType(type), AttributeInitDefault()); + const int index = CustomData_get_named_layer_index(customdata, type, uniquename); return (index == -1) ? nullptr : &(customdata->layers[index]); } CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList *reports) { - const CustomDataLayer *src_layer = BKE_id_attribute_search( - id, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); - if (src_layer == nullptr) { - BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); - return nullptr; - } - - const eCustomDataType type = (eCustomDataType)src_layer->type; - const eAttrDomain domain = BKE_id_attribute_domain(id, src_layer); + using namespace blender::bke; + char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; + BKE_id_attribute_calc_unique_name(id, name, uniquename); - /* Make a copy of name in case CustomData API reallocates the layers. */ - const std::string name_copy = name; + if (GS(id->name) == ID_ME) { + Mesh *mesh = reinterpret_cast<Mesh *>(id); + if (BMEditMesh *em = mesh->edit_mesh) { + BLI_assert_unreachable(); + UNUSED_VARS(em); + return nullptr; + } + } - DomainInfo info[ATTR_DOMAIN_NUM]; - get_domains(id, info); - CustomData *customdata = info[domain].customdata; + std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id); + if (!attributes) { + return nullptr; + } - CustomDataLayer *new_layer = BKE_id_attribute_new(id, name_copy.c_str(), type, domain, reports); - if (new_layer == nullptr) { + GAttributeReader src = attributes->lookup(name); + if (!src) { + BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); return nullptr; } - const int from_index = CustomData_get_named_layer_index(customdata, type, name_copy.c_str()); - const int to_index = CustomData_get_named_layer_index(customdata, type, new_layer->name); - CustomData_copy_data_layer( - customdata, customdata, from_index, to_index, 0, 0, info[domain].length); + const eCustomDataType type = cpp_type_to_custom_data_type(src.varray.type()); + attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray)); - return new_layer; + return BKE_id_attribute_search(id, uniquename, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); } bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports) { + using namespace blender::bke; if (BKE_id_attribute_required(id, name)) { BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed"); return false; @@ -250,31 +286,26 @@ bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports) DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - switch (GS(id->name)) { - case ID_ME: { - Mesh *mesh = reinterpret_cast<Mesh *>(id); - if (BMEditMesh *em = mesh->edit_mesh) { - for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { - if (CustomData *data = info[domain].customdata) { - if (BM_data_layer_free_named(em->bm, data, name)) { - return true; - } - } - } - return false; - } - ATTR_FALLTHROUGH; - } - default: + if (GS(id->name) == ID_ME) { + Mesh *mesh = reinterpret_cast<Mesh *>(id); + if (BMEditMesh *em = mesh->edit_mesh) { for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { if (CustomData *data = info[domain].customdata) { - if (CustomData_free_layer_named(data, name, info[domain].length)) { + if (BM_data_layer_free_named(em->bm, data, name)) { return true; } } } return false; + } } + + std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id); + if (!attributes) { + return false; + } + + return attributes->remove(name); } CustomDataLayer *BKE_id_attribute_find(const ID *id, diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 409941623d2..8d21c6fe792 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -2,7 +2,6 @@ #include <utility> -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_customdata.h" #include "BKE_deform.h" @@ -26,8 +25,6 @@ #include "attribute_access_intern.hh" -static CLG_LogRef LOG = {"bke.attribute_access"}; - using blender::float3; using blender::GMutableSpan; using blender::GSpan; @@ -36,7 +33,6 @@ using blender::Set; using blender::StringRef; using blender::StringRefNull; using blender::bke::AttributeIDRef; -using blender::bke::OutputAttribute; namespace blender::bke { @@ -151,37 +147,6 @@ eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains) return highest_priority_domain; } -GMutableSpan OutputAttribute::as_span() -{ - if (!optional_span_varray_) { - const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_ = std::make_unique<GVMutableArray_GSpan>(varray_, - materialize_old_values); - } - GVMutableArray_GSpan &span_varray = *optional_span_varray_; - return span_varray; -} - -void OutputAttribute::save() -{ - save_has_been_called_ = true; - if (optional_span_varray_) { - optional_span_varray_->save(); - } - if (save_) { - save_(*this); - } -} - -OutputAttribute::~OutputAttribute() -{ - if (!save_has_been_called_) { - if (varray_) { - std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; - } - } -} - static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer) { if (layer.anonymous_id != nullptr) { @@ -293,9 +258,9 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, return layer.name == attribute_id.name(); } -GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent &component) const +GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return {}; } @@ -311,21 +276,20 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent return {}; } - const int domain_num = component.attribute_domain_num(domain_); - return as_read_attribute_(data, domain_num); + const int element_num = custom_data_access_.get_element_num(owner); + return as_read_attribute_(data, element_num); } -WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( - GeometryComponent &component) const +GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const { if (writable_ != Writable) { return {}; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); void *data; if (stored_as_named_attribute_) { @@ -341,72 +305,79 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( void *new_data; if (stored_as_named_attribute_) { new_data = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, name_.c_str(), domain_num); + custom_data, stored_type_, name_.c_str(), element_num); } else { - new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num); + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, element_num); } if (data != new_data) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } data = new_data; } std::function<void()> tag_modified_fn; - if (update_on_write_ != nullptr) { - tag_modified_fn = [component = &component, update = update_on_write_]() { - update(*component); - }; + if (update_on_change_ != nullptr) { + tag_modified_fn = [owner, update = update_on_change_]() { update(owner); }; } - return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)}; + return {as_write_attribute_(data, element_num), domain_, std::move(tag_modified_fn)}; } -bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const +bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const { if (deletable_ != Deletable) { return false; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + auto update = [&]() { + if (update_on_change_ != nullptr) { + update_on_change_(owner); + } + }; + + const int element_num = custom_data_access_.get_element_num(owner); if (stored_as_named_attribute_) { - if (CustomData_free_layer_named(custom_data, name_.c_str(), domain_num)) { + if (CustomData_free_layer_named(custom_data, name_.c_str(), element_num)) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } + update(); return true; } return false; } const int layer_index = CustomData_get_layer_index(custom_data, stored_type_); - if (CustomData_free_layer(custom_data, stored_type_, domain_num, layer_index)) { + if (CustomData_free_layer(custom_data, stored_type_, element_num, layer_index)) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } + update(); return true; } + return false; } -bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, +bool BuiltinCustomDataLayerProvider::try_create(void *owner, const AttributeInit &initializer) const { if (createable_ != Creatable) { return false; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); bool success; if (stored_as_named_attribute_) { if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { @@ -414,7 +385,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_custom_data_layer_from_attribute_init( - name_, *custom_data, stored_type_, domain_num, initializer); + name_, *custom_data, stored_type_, element_num, initializer); } else { if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { @@ -422,19 +393,19 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_num, initializer); + *custom_data, stored_type_, element_num, initializer); } if (success) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } } return success; } -bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const +bool BuiltinCustomDataLayerProvider::exists(const void *owner) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return false; } @@ -444,14 +415,14 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return CustomData_get_layer(custom_data, stored_type_) != nullptr; } -ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( - const GeometryComponent &component, const AttributeIDRef &attribute_id) const +GAttributeReader CustomDataAttributeProvider::try_get_for_read( + const void *owner, const AttributeIDRef &attribute_id) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; @@ -460,61 +431,62 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (type == nullptr) { continue; } - GSpan data{*type, layer.data, domain_num}; + GSpan data{*type, layer.data, element_num}; return {GVArray::ForSpan(data), domain_}; } return {}; } -WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( - GeometryComponent &component, const AttributeIDRef &attribute_id) const +GAttributeWriter CustomDataAttributeProvider::try_get_for_write( + void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_num); + CustomData_duplicate_referenced_layer_named( + custom_data, layer.type, layer.name, element_num); } else { CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), domain_num); + custom_data, layer.type, &attribute_id.anonymous_id(), element_num); } const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type); if (type == nullptr) { continue; } - GMutableSpan data{*type, layer.data, domain_num}; + GMutableSpan data{*type, layer.data, element_num}; return {GVMutableArray::ForSpan(data), domain_}; } return {}; } -bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, - const AttributeIDRef &attribute_id) const +bool CustomDataAttributeProvider::try_delete(void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); + ; for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (this->type_is_supported((eCustomDataType)layer.type) && custom_data_layer_matches_attribute_id(layer, attribute_id)) { - CustomData_free_layer(custom_data, layer.type, domain_num, i); + CustomData_free_layer(custom_data, layer.type, element_num, i); return true; } } return false; } -bool CustomDataAttributeProvider::try_create(GeometryComponent &component, +bool CustomDataAttributeProvider::try_create(void *owner, const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, @@ -526,7 +498,7 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, if (!this->type_is_supported(data_type)) { return false; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } @@ -535,16 +507,16 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); add_custom_data_layer_from_attribute_init( - attribute_id, *custom_data, data_type, domain_num, initializer); + attribute_id, *custom_data, data_type, element_num, initializer); return true; } -bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component, +bool CustomDataAttributeProvider::foreach_attribute(const void *owner, const AttributeForeachCallback callback) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return true; } @@ -561,17 +533,17 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com return true; } -ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( - const GeometryComponent &component, const AttributeIDRef &attribute_id) const +GAttributeReader NamedLegacyCustomDataProvider::try_get_for_read( + const void *owner, const AttributeIDRef &attribute_id) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return {}; } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_num = component.attribute_domain_num(domain_); + const int domain_num = custom_data_access_.get_element_num(owner); return {as_read_attribute_(layer.data, domain_num), domain_}; } } @@ -579,36 +551,36 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( return {}; } -WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( - GeometryComponent &component, const AttributeIDRef &attribute_id) const +GAttributeWriter NamedLegacyCustomDataProvider::try_get_for_write( + void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, layer.name, domain_num); + custom_data, stored_type_, layer.name, element_num); if (data_old != data_new) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } } - return {as_write_attribute_(layer.data, domain_num), domain_}; + return {as_write_attribute_(layer.data, element_num), domain_}; } } } return {}; } -bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, +bool NamedLegacyCustomDataProvider::try_delete(void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } @@ -616,10 +588,10 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_num = component.attribute_domain_num(domain_); - CustomData_free_layer(custom_data, stored_type_, domain_num, i); + const int element_num = custom_data_access_.get_element_num(owner); + CustomData_free_layer(custom_data, stored_type_, element_num, i); if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } return true; } @@ -629,9 +601,9 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, } bool NamedLegacyCustomDataProvider::foreach_attribute( - const GeometryComponent &component, const AttributeForeachCallback callback) const + const void *owner, const AttributeForeachCallback callback) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return true; } @@ -805,275 +777,10 @@ void CustomDataAttributes::reorder(Span<AttributeIDRef> new_order) CustomData_update_typemap(&data); } -} // namespace blender::bke - /* -------------------------------------------------------------------- */ /** \name Geometry Component * \{ */ -const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const -{ - return nullptr; -} - -bool GeometryComponent::attribute_domain_supported(const eAttrDomain domain) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - return providers->supported_domains().contains(domain); -} - -int GeometryComponent::attribute_domain_num(const eAttrDomain UNUSED(domain)) const -{ - return 0; -} - -bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - return providers->builtin_attribute_providers().contains_as(attribute_name); -} - -bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const -{ - /* Anonymous attributes cannot be built-in. */ - return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name()); -} - -blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return {}; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); - if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; - } - } - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id); - if (attribute) { - return attribute; - } - } - return {}; -} - -blender::GVArray GeometryComponent::attribute_try_adapt_domain_impl( - const blender::GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const -{ - if (from_domain == to_domain) { - return varray; - } - return {}; -} - -blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( - const AttributeIDRef &attribute_id) -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return {}; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); - if (builtin_provider != nullptr) { - return builtin_provider->try_get_for_write(*this); - } - } - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id); - if (attribute) { - return attribute; - } - } - return {}; -} - -bool GeometryComponent::attribute_try_delete(const AttributeIDRef &attribute_id) -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return {}; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); - if (builtin_provider != nullptr) { - return builtin_provider->try_delete(*this); - } - } - bool success = false; - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - success = dynamic_provider->try_delete(*this, attribute_id) || success; - } - return success; -} - -void GeometryComponent::attributes_remove_anonymous() -{ - using namespace blender; - Vector<const AnonymousAttributeID *> anonymous_ids; - for (const AttributeIDRef &id : this->attribute_ids()) { - if (id.is_anonymous()) { - anonymous_ids.append(&id.anonymous_id()); - } - } - - while (!anonymous_ids.is_empty()) { - this->attribute_try_delete(anonymous_ids.pop_last()); - } -} - -bool GeometryComponent::attribute_try_create(const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const AttributeInit &initializer) -{ - using namespace blender::bke; - if (!attribute_id) { - return false; - } - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - if (this->attribute_exists(attribute_id)) { - return false; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); - if (builtin_provider != nullptr) { - if (builtin_provider->domain() != domain) { - return false; - } - if (builtin_provider->data_type() != data_type) { - return false; - } - return builtin_provider->try_create(*this, initializer); - } - } - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) { - return true; - } - } - return false; -} - -bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name, - const AttributeInit &initializer) -{ - using namespace blender::bke; - if (attribute_name.is_empty()) { - return false; - } - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider == nullptr) { - return false; - } - return builtin_provider->try_create(*this, initializer); -} - -Set<AttributeIDRef> GeometryComponent::attribute_ids() const -{ - Set<AttributeIDRef> attributes; - this->attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - attributes.add(attribute_id); - return true; - }); - return attributes; -} - -bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return true; - } - - /* Keep track handled attribute names to make sure that we do not return the same name twice. */ - Set<std::string> handled_attribute_names; - - for (const BuiltinAttributeProvider *provider : - providers->builtin_attribute_providers().values()) { - if (provider->exists(*this)) { - AttributeMetaData meta_data{provider->domain(), provider->data_type()}; - if (!callback(provider->name(), meta_data)) { - return false; - } - handled_attribute_names.add_new(provider->name()); - } - } - for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { - const bool continue_loop = provider->foreach_attribute( - *this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) { - return callback(attribute_id, meta_data); - } - return true; - }); - if (!continue_loop) { - return false; - } - } - - return true; -} - -bool GeometryComponent::attribute_exists(const AttributeIDRef &attribute_id) const -{ - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (attribute) { - return true; - } - return false; -} - -std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( - const AttributeIDRef &attribute_id) const -{ - std::optional<AttributeMetaData> result{std::nullopt}; - this->attribute_foreach( - [&](const AttributeIDRef ¤t_attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id == current_attribute_id) { - result = meta_data; - return false; - } - return true; - }); - return result; -} - static blender::GVArray try_adapt_data_type(blender::GVArray varray, const blender::CPPType &to_type) { @@ -1082,294 +789,6 @@ static blender::GVArray try_adapt_data_type(blender::GVArray varray, return conversions.try_convert(std::move(varray), to_type); } -blender::GVArray GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type) const -{ - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (!attribute) { - return {}; - } - - blender::GVArray varray = std::move(attribute.varray); - if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) { - varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); - if (!varray) { - return {}; - } - } - - const blender::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - if (varray.type() != *cpp_type) { - varray = try_adapt_data_type(std::move(varray), *cpp_type); - if (!varray) { - return {}; - } - } - - return varray; -} - -blender::GVArray GeometryComponent::attribute_try_get_for_read(const AttributeIDRef &attribute_id, - const eAttrDomain domain) const -{ - if (!this->attribute_domain_supported(domain)) { - return {}; - } - - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (!attribute) { - return {}; - } - - if (attribute.domain != domain) { - return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain); - } - - return std::move(attribute.varray); -} - -blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id, const eCustomDataType data_type) const -{ - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (!attribute) { - return {}; - } - const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(type != nullptr); - if (attribute.varray.type() == *type) { - return attribute; - } - const blender::bke::DataTypeConversions &conversions = - blender::bke::get_implicit_type_conversions(); - return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; -} - -blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const void *default_value) const -{ - blender::GVArray varray = this->attribute_try_get_for_read(attribute_id, domain, data_type); - if (varray) { - return varray; - } - const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - if (default_value == nullptr) { - default_value = type->default_value(); - } - const int domain_num = this->attribute_domain_num(domain); - return blender::GVArray::ForSingle(*type, domain_num, default_value); -} - -class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { - public: - GeometryComponent *component; - std::string attribute_name; - blender::bke::WeakAnonymousAttributeID anonymous_attribute_id; - - GVMutableAttribute_For_OutputAttribute(GMutableSpan data, - GeometryComponent &component, - const AttributeIDRef &attribute_id) - : blender::GVArrayImpl_For_GSpan(data), component(&component) - { - if (attribute_id.is_named()) { - this->attribute_name = attribute_id.name(); - } - else { - const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id(); - BKE_anonymous_attribute_id_increment_weak(anonymous_id); - this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id}; - } - } - - ~GVMutableAttribute_For_OutputAttribute() override - { - type_->destruct_n(data_, size_); - MEM_freeN(data_); - } -}; - -static void save_output_attribute(OutputAttribute &output_attribute) -{ - using namespace blender; - using namespace blender::fn; - using namespace blender::bke; - - GVMutableAttribute_For_OutputAttribute &varray = - dynamic_cast<GVMutableAttribute_For_OutputAttribute &>( - *output_attribute.varray().get_implementation()); - - GeometryComponent &component = *varray.component; - AttributeIDRef attribute_id; - if (!varray.attribute_name.empty()) { - attribute_id = varray.attribute_name; - } - else { - attribute_id = varray.anonymous_attribute_id.extract(); - } - const eAttrDomain domain = output_attribute.domain(); - const eCustomDataType data_type = output_attribute.custom_data_type(); - const CPPType &cpp_type = output_attribute.cpp_type(); - - component.attribute_try_delete(attribute_id); - if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) { - if (!varray.attribute_name.empty()) { - CLOG_WARN(&LOG, - "Could not create the '%s' attribute with type '%s'.", - varray.attribute_name.c_str(), - cpp_type.name().c_str()); - } - return; - } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); - BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); - for (const int i : IndexRange(varray.size())) { - varray.get(i, buffer); - write_attribute.varray.set_by_relocate(i, buffer); - } - if (write_attribute.tag_modified_fn) { - write_attribute.tag_modified_fn(); - } -} - -static std::function<void(OutputAttribute &)> get_simple_output_attribute_save_method( - const blender::bke::WriteAttributeLookup &attribute) -{ - if (!attribute.tag_modified_fn) { - return {}; - } - return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) { - tag_modified_fn(); - }; -} - -static OutputAttribute create_output_attribute(GeometryComponent &component, - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const bool ignore_old_values, - const void *default_value) -{ - using namespace blender; - using namespace blender::fn; - using namespace blender::bke; - - if (!attribute_id) { - return {}; - } - - const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - const DataTypeConversions &conversions = get_implicit_type_conversions(); - - if (component.attribute_is_builtin(attribute_id)) { - const StringRef attribute_name = attribute_id.name(); - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); - if (!attribute) { - if (default_value) { - const int64_t domain_num = component.attribute_domain_num(domain); - component.attribute_try_create_builtin( - attribute_name, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); - } - else { - component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); - } - attribute = component.attribute_try_get_for_write(attribute_name); - if (!attribute) { - /* Builtin attribute does not exist and can't be created. */ - return {}; - } - } - if (attribute.domain != domain) { - /* Builtin attribute is on different domain. */ - return {}; - } - GVMutableArray varray = std::move(attribute.varray); - if (varray.type() == *cpp_type) { - /* Builtin attribute matches exactly. */ - return OutputAttribute(std::move(varray), - domain, - get_simple_output_attribute_save_method(attribute), - ignore_old_values); - } - /* Builtin attribute is on the same domain but has a different data type. */ - varray = conversions.try_convert(std::move(varray), *cpp_type); - return OutputAttribute(std::move(varray), - domain, - get_simple_output_attribute_save_method(attribute), - ignore_old_values); - } - - const int domain_num = component.attribute_domain_num(domain); - - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); - if (!attribute) { - if (default_value) { - component.attribute_try_create( - attribute_id, - domain, - data_type, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); - } - else { - component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); - } - - attribute = component.attribute_try_get_for_write(attribute_id); - if (!attribute) { - /* Can't create the attribute. */ - return {}; - } - } - if (attribute.domain == domain && attribute.varray.type() == *cpp_type) { - /* Existing generic attribute matches exactly. */ - - return OutputAttribute(std::move(attribute.varray), - domain, - get_simple_output_attribute_save_method(attribute), - ignore_old_values); - } - - /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing - * attribute after processing is done. */ - void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__); - if (ignore_old_values) { - /* This does nothing for trivially constructible types, but is necessary for correctness. */ - cpp_type->default_construct_n(data, domain); - } - else { - /* Fill the temporary array with values from the existing attribute. */ - GVArray old_varray = component.attribute_get_for_read( - attribute_id, domain, data_type, default_value); - old_varray.materialize_to_uninitialized(IndexRange(domain_num), data); - } - GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>( - GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id); - - return OutputAttribute(std::move(varray), domain, save_output_attribute, true); -} - -OutputAttribute GeometryComponent::attribute_try_get_for_output(const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const void *default_value) -{ - return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value); -} - -OutputAttribute GeometryComponent::attribute_try_get_for_output_only( - const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type) -{ - return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr); -} - -namespace blender::bke { - GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &UNUSED(scope)) const @@ -1388,7 +807,10 @@ GVArray AttributeFieldInput::get_varray_for_context(const GeometryComponent &com IndexMask UNUSED(mask)) const { const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_); - return component.attribute_try_get_for_read(name_, domain, data_type); + if (auto attributes = component.attributes()) { + return attributes->lookup(name_, domain, data_type); + } + return {}; } std::string AttributeFieldInput::socket_inspection_name() const @@ -1428,10 +850,10 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c { const StringRef name = get_random_id_attribute_name(domain); - GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); - if (attribute) { - BLI_assert(attribute.size() == component.attribute_domain_num(domain)); - return attribute; + if (auto attributes = component.attributes()) { + if (GVArray attribute = attributes->lookup(name, domain, CD_PROP_INT32)) { + return attribute; + } } /* Use the index as the fallback if no random ID attribute exists. */ @@ -1460,7 +882,7 @@ GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryCompo IndexMask UNUSED(mask)) const { const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_); - return component.attribute_try_get_for_read(anonymous_id_.get(), domain, data_type); + return component.attributes()->lookup(anonymous_id_.get(), domain, data_type); } std::string AnonymousAttributeFieldInput::socket_inspection_name() const @@ -1484,6 +906,194 @@ bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const return false; } +GVArray AttributeAccessor::lookup(const AttributeIDRef &attribute_id, + const std::optional<eAttrDomain> domain, + const std::optional<eCustomDataType> data_type) const +{ + GAttributeReader attribute = this->lookup(attribute_id); + if (!attribute) { + return {}; + } + GVArray varray = std::move(attribute.varray); + if (domain.has_value()) { + if (attribute.domain != domain) { + varray = this->adapt_domain(varray, attribute.domain, *domain); + if (!varray) { + return {}; + } + } + } + if (data_type.has_value()) { + const CPPType &type = *custom_data_type_to_cpp_type(*data_type); + if (varray.type() != type) { + varray = try_adapt_data_type(std::move(varray), type); + if (!varray) { + return {}; + } + } + } + return varray; +} + +GVArray AttributeAccessor::lookup_or_default(const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const void *default_value) const +{ + GVArray varray = this->lookup(attribute_id, domain, data_type); + if (varray) { + return varray; + } + const CPPType &type = *custom_data_type_to_cpp_type(data_type); + const int64_t domain_size = this->domain_size(domain); + if (default_value == nullptr) { + return GVArray::ForSingleRef(type, domain_size, type.default_value()); + } + return GVArray::ForSingle(type, domain_size, default_value); +} + +Set<AttributeIDRef> AttributeAccessor::all_ids() const +{ + Set<AttributeIDRef> ids; + this->for_all( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData & /* meta_data */) { + ids.add(attribute_id); + return true; + }); + return ids; +} + +void MutableAttributeAccessor::remove_anonymous() +{ + Vector<const AnonymousAttributeID *> anonymous_ids; + for (const AttributeIDRef &id : this->all_ids()) { + if (id.is_anonymous()) { + anonymous_ids.append(&id.anonymous_id()); + } + } + + while (!anonymous_ids.is_empty()) { + this->remove(anonymous_ids.pop_last()); + } +} + +/** + * Debug utility that checks whether the #finish function of an #AttributeWriter has been called. + */ +#ifdef DEBUG +struct FinishCallChecker { + std::string name; + bool finish_called = false; + std::function<void()> real_finish_fn; + + ~FinishCallChecker() + { + if (!this->finish_called) { + std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n"; + } + } +}; +#endif + +GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id) +{ + GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id); + /* Check that the #finish method is called in debug builds. */ +#ifdef DEBUG + if (attribute) { + auto checker = std::make_shared<FinishCallChecker>(); + if (attribute_id.is_named()) { + checker->name = attribute_id.name(); + } + else { + checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id()); + } + checker->real_finish_fn = attribute.tag_modified_fn; + attribute.tag_modified_fn = [checker]() { + if (checker->real_finish_fn) { + checker->real_finish_fn(); + } + checker->finish_called = true; + }; + } +#endif + return attribute; +} + +GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write( + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer) +{ + std::optional<AttributeMetaData> meta_data = this->lookup_meta_data(attribute_id); + if (meta_data.has_value()) { + if (meta_data->domain == domain && meta_data->data_type == data_type) { + return this->lookup_for_write(attribute_id); + } + return {}; + } + if (this->add(attribute_id, domain, data_type, initializer)) { + return this->lookup_for_write(attribute_id); + } + return {}; +} + +GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_span( + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer) +{ + GAttributeWriter attribute = this->lookup_or_add_for_write( + attribute_id, domain, data_type, initializer); + if (attribute) { + return GSpanAttributeWriter{std::move(attribute), true}; + } + return {}; +} + +GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span( + const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type) +{ + GAttributeWriter attribute = this->lookup_or_add_for_write(attribute_id, domain, data_type); + if (attribute) { + return GSpanAttributeWriter{std::move(attribute), false}; + } + return {}; +} + +Vector<AttributeTransferData> retrieve_attributes_for_transfer( + const bke::AttributeAccessor src_attributes, + bke::MutableAttributeAccessor dst_attributes, + const eAttrDomainMask domain_mask, + const Set<std::string> &skip) +{ + Vector<AttributeTransferData> attributes; + src_attributes.for_all( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { + if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) { + return true; + } + if (id.is_named() && skip.contains(id.name())) { + return true; + } + if (!id.should_be_kept()) { + return true; + } + + GVArray src = src_attributes.lookup(id, meta_data.domain); + BLI_assert(src); + bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, meta_data.domain, meta_data.data_type); + BLI_assert(dst); + attributes.append({std::move(src), meta_data, std::move(dst)}); + + return true; + }); + return attributes; +} + } // namespace blender::bke /** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index ac43754dd1a..1a2607d9403 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -15,12 +15,14 @@ namespace blender::bke { * components in a generic way. */ struct CustomDataAccessInfo { - using CustomDataGetter = CustomData *(*)(GeometryComponent &component); - using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component); - using UpdateCustomDataPointers = void (*)(GeometryComponent &component); + using CustomDataGetter = CustomData *(*)(void *owner); + using ConstCustomDataGetter = const CustomData *(*)(const void *owner); + using GetElementNum = int (*)(const void *owner); + using UpdateCustomDataPointers = void (*)(void *owner); CustomDataGetter get_custom_data; ConstCustomDataGetter get_const_custom_data; + GetElementNum get_element_num; UpdateCustomDataPointers update_custom_data_pointers; }; @@ -69,12 +71,11 @@ class BuiltinAttributeProvider { { } - virtual GVArray try_get_for_read(const GeometryComponent &component) const = 0; - virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0; - virtual bool try_delete(GeometryComponent &component) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const = 0; - virtual bool exists(const GeometryComponent &component) const = 0; + virtual GVArray try_get_for_read(const void *owner) const = 0; + virtual GAttributeWriter try_get_for_write(void *owner) const = 0; + virtual bool try_delete(void *owner) const = 0; + virtual bool try_create(void *onwer, const AttributeInit &initializer) const = 0; + virtual bool exists(const void *owner) const = 0; StringRefNull name() const { @@ -98,23 +99,23 @@ class BuiltinAttributeProvider { */ class DynamicAttributesProvider { public: - virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const = 0; - virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const = 0; - virtual bool try_delete(GeometryComponent &component, - const AttributeIDRef &attribute_id) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component), - const AttributeIDRef &UNUSED(attribute_id), - const eAttrDomain UNUSED(domain), - const eCustomDataType UNUSED(data_type), - const AttributeInit &UNUSED(initializer)) const + virtual GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const = 0; + virtual GAttributeWriter try_get_for_write(void *owner, + const AttributeIDRef &attribute_id) const = 0; + virtual bool try_delete(void *owner, const AttributeIDRef &attribute_id) const = 0; + virtual bool try_create(void *owner, + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer) const { + UNUSED_VARS(owner, attribute_id, domain, data_type, initializer); /* Some providers should not create new attributes. */ return false; }; - virtual bool foreach_attribute(const GeometryComponent &component, + virtual bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const = 0; virtual void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const = 0; }; @@ -138,22 +139,20 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { { } - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final; - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final; - bool try_create(GeometryComponent &component, + bool try_create(void *owner, const AttributeIDRef &attribute_id, eAttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer) const final; - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final; + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final { @@ -197,13 +196,11 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { { } - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final; + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final; + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final; + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final; + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final; }; @@ -226,15 +223,15 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const void *data, int domain_num); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); - using UpdateOnRead = void (*)(const GeometryComponent &component); - using UpdateOnWrite = void (*)(GeometryComponent &component); + using AsReadAttribute = GVArray (*)(const void *data, int element_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int element_num); + using UpdateOnRead = void (*)(const void *owner); + using UpdateOnChange = void (*)(void *owner); const eCustomDataType stored_type_; const CustomDataAccessInfo custom_data_access_; const AsReadAttribute as_read_attribute_; const AsWriteAttribute as_write_attribute_; - const UpdateOnWrite update_on_write_; + const UpdateOnChange update_on_change_; bool stored_as_named_attribute_; public: @@ -248,23 +245,23 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const CustomDataAccessInfo custom_data_access, const AsReadAttribute as_read_attribute, const AsWriteAttribute as_write_attribute, - const UpdateOnWrite update_on_write) + const UpdateOnChange update_on_write) : BuiltinAttributeProvider( std::move(attribute_name), domain, attribute_type, creatable, writable, deletable), stored_type_(stored_type), custom_data_access_(custom_data_access), as_read_attribute_(as_read_attribute), as_write_attribute_(as_write_attribute), - update_on_write_(update_on_write), + update_on_change_(update_on_write), stored_as_named_attribute_(data_type_ == stored_type_) { } - GVArray try_get_for_read(const GeometryComponent &component) const final; - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final; - bool try_delete(GeometryComponent &component) const final; - bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; - bool exists(const GeometryComponent &component) const final; + GVArray try_get_for_read(const void *owner) const final; + GAttributeWriter try_get_for_write(void *owner) const final; + bool try_delete(void *owner) const final; + bool try_create(void *owner, const AttributeInit &initializer) const final; + bool exists(const void *owner) const final; }; /** @@ -321,4 +318,183 @@ class ComponentAttributeProviders { } }; +namespace attribute_accessor_functions { + +template<const ComponentAttributeProviders &providers> +inline bool is_builtin(const void *UNUSED(owner), const AttributeIDRef &attribute_id) +{ + if (!attribute_id.is_named()) { + return false; + } + const StringRef name = attribute_id.name(); + return providers.builtin_attribute_providers().contains_as(name); +} + +template<const ComponentAttributeProviders &providers> +inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + return {provider->try_get_for_read(owner), provider->domain()}; + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + GAttributeReader attribute = provider->try_get_for_read(owner, attribute_id); + if (attribute) { + return attribute; + } + } + return {}; +} + +template<const ComponentAttributeProviders &providers> +inline bool for_all(const void *owner, + FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn) +{ + Set<AttributeIDRef> handled_attribute_ids; + for (const BuiltinAttributeProvider *provider : + providers.builtin_attribute_providers().values()) { + if (provider->exists(owner)) { + AttributeMetaData meta_data{provider->domain(), provider->data_type()}; + if (!fn(provider->name(), meta_data)) { + return false; + } + handled_attribute_ids.add_new(provider->name()); + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + const bool continue_loop = provider->foreach_attribute( + owner, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (handled_attribute_ids.add(attribute_id)) { + return fn(attribute_id, meta_data); + } + return true; + }); + if (!continue_loop) { + return false; + } + } + return true; +} + +template<const ComponentAttributeProviders &providers> +inline bool contains(const void *owner, const blender::bke::AttributeIDRef &attribute_id) +{ + bool found = false; + for_all<providers>( + owner, + [&](const AttributeIDRef &other_attribute_id, const AttributeMetaData & /* meta_data */) { + if (attribute_id == other_attribute_id) { + found = true; + return false; + } + return true; + }); + return found; +} + +template<const ComponentAttributeProviders &providers> +inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner, + const AttributeIDRef &attribute_id) +{ + std::optional<AttributeMetaData> meta_data; + for_all<providers>( + owner, + [&](const AttributeIDRef &other_attribute_id, const AttributeMetaData &other_meta_data) { + if (attribute_id == other_attribute_id) { + meta_data = other_meta_data; + return false; + } + return true; + }); + return meta_data; +} + +template<const ComponentAttributeProviders &providers> +inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + return provider->try_get_for_write(owner); + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + GAttributeWriter attribute = provider->try_get_for_write(owner, attribute_id); + if (attribute) { + return attribute; + } + } + return {}; +} + +template<const ComponentAttributeProviders &providers> +inline bool remove(void *owner, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + return provider->try_delete(owner); + } + } + bool success = false; + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + success = provider->try_delete(owner, attribute_id) || success; + } + return success; +} + +template<const ComponentAttributeProviders &providers> +inline bool add(void *owner, + const AttributeIDRef &attribute_id, + eAttrDomain domain, + eCustomDataType data_type, + const AttributeInit &initializer) +{ + if (contains<providers>(owner, attribute_id)) { + return false; + } + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + if (provider->domain() != domain) { + return false; + } + if (provider->data_type() != data_type) { + return false; + } + return provider->try_create(owner, initializer); + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + if (provider->try_create(owner, attribute_id, domain, data_type, initializer)) { + return true; + } + } + return false; +} + +template<const ComponentAttributeProviders &providers> +inline AttributeAccessorFunctions accessor_functions_for_providers() +{ + return AttributeAccessorFunctions{contains<providers>, + lookup_meta_data<providers>, + nullptr, + nullptr, + is_builtin<providers>, + lookup<providers>, + nullptr, + for_all<providers>, + lookup_for_write<providers>, + remove<providers>, + add<providers>}; +} + +} // namespace attribute_accessor_functions + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.cc index 1cda0e8a4bb..99733c8edb3 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.cc @@ -68,12 +68,12 @@ static void brush_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c BKE_previewimg_id_copy(&brush_dst->id, &brush_src->id); } else { - brush_dst->preview = NULL; + brush_dst->preview = nullptr; } brush_dst->curve = BKE_curvemapping_copy(brush_src->curve); - if (brush_src->gpencil_settings != NULL) { - brush_dst->gpencil_settings = MEM_dupallocN(brush_src->gpencil_settings); + if (brush_src->gpencil_settings != nullptr) { + brush_dst->gpencil_settings = MEM_cnew(__func__, *(brush_src->gpencil_settings)); brush_dst->gpencil_settings->curve_sensitivity = BKE_curvemapping_copy( brush_src->gpencil_settings->curve_sensitivity); brush_dst->gpencil_settings->curve_strength = BKE_curvemapping_copy( @@ -94,8 +94,8 @@ static void brush_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c brush_dst->gpencil_settings->curve_rand_value = BKE_curvemapping_copy( brush_src->gpencil_settings->curve_rand_value); } - if (brush_src->curves_sculpt_settings != NULL) { - brush_dst->curves_sculpt_settings = MEM_dupallocN(brush_src->curves_sculpt_settings); + if (brush_src->curves_sculpt_settings != nullptr) { + brush_dst->curves_sculpt_settings = MEM_cnew(__func__, *(brush_src->curves_sculpt_settings)); } /* enable fake user by default */ @@ -110,7 +110,7 @@ static void brush_free_data(ID *id) } BKE_curvemapping_free(brush->curve); - if (brush->gpencil_settings != NULL) { + if (brush->gpencil_settings != nullptr) { BKE_curvemapping_free(brush->gpencil_settings->curve_sensitivity); BKE_curvemapping_free(brush->gpencil_settings->curve_strength); BKE_curvemapping_free(brush->gpencil_settings->curve_jitter); @@ -124,7 +124,7 @@ static void brush_free_data(ID *id) MEM_SAFE_FREE(brush->gpencil_settings); } - if (brush->curves_sculpt_settings != NULL) { + if (brush->curves_sculpt_settings != nullptr) { MEM_freeN(brush->curves_sculpt_settings); } @@ -153,7 +153,7 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) /* NOTE: assert below ensures that the comment above is valid, and that exception is * acceptable for the time being. */ BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0); - BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == NULL); + BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == nullptr); } if (force_local) { @@ -268,7 +268,7 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) /* grease pencil */ BLO_read_data_address(reader, &brush->gpencil_settings); - if (brush->gpencil_settings != NULL) { + if (brush->gpencil_settings != nullptr) { BLO_read_data_address(reader, &brush->gpencil_settings->curve_sensitivity); BLO_read_data_address(reader, &brush->gpencil_settings->curve_strength); BLO_read_data_address(reader, &brush->gpencil_settings->curve_jitter); @@ -319,8 +319,8 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &brush->curves_sculpt_settings); - brush->preview = NULL; - brush->icon_imbuf = NULL; + brush->preview = nullptr; + brush->icon_imbuf = nullptr; } static void brush_blend_read_lib(BlendLibReader *reader, ID *id) @@ -335,7 +335,7 @@ static void brush_blend_read_lib(BlendLibReader *reader, ID *id) BLO_read_id_address(reader, brush->id.lib, &brush->paint_curve); /* link default grease pencil palette */ - if (brush->gpencil_settings != NULL) { + if (brush->gpencil_settings != nullptr) { if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) { BLO_read_id_address(reader, brush->id.lib, &brush->gpencil_settings->material); @@ -344,7 +344,7 @@ static void brush_blend_read_lib(BlendLibReader *reader, ID *id) } } else { - brush->gpencil_settings->material = NULL; + brush->gpencil_settings->material = nullptr; } } } @@ -356,20 +356,20 @@ static void brush_blend_read_expand(BlendExpander *expander, ID *id) BLO_expand(expander, brush->mask_mtex.tex); BLO_expand(expander, brush->clone.image); BLO_expand(expander, brush->paint_curve); - if (brush->gpencil_settings != NULL) { + if (brush->gpencil_settings != nullptr) { BLO_expand(expander, brush->gpencil_settings->material); } } static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) { - BlendLibReader *reader = cb_data->user_data; + BlendLibReader *reader = (BlendLibReader *)cb_data->user_data; ID *id_old = *cb_data->id_pointer; /* Old data has not been remapped to new values of the pointers, if we want to keep the old * pointer here we need its new address. */ - ID *id_old_new = id_old != NULL ? BLO_read_get_new_id_address(reader, id_old->lib, id_old) : - NULL; - BLI_assert(id_old_new == NULL || ELEM(id_old, id_old_new, id_old_new->orig_id)); + ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address(reader, id_old->lib, id_old) : + nullptr; + BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id)); if (cb_data->cb_flag & IDWALK_CB_USER) { id_us_plus_no_lib(id_old_new); id_us_min(id_old); @@ -381,11 +381,11 @@ static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) { /* Whole Brush is preserved across undo-steps. */ - BKE_lib_id_swap(NULL, id_new, id_old); + BKE_lib_id_swap(nullptr, id_new, id_old); /* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid. * NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */ - BKE_library_foreach_ID_link(NULL, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); + BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be * fairly delicate. */ @@ -393,33 +393,33 @@ static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) } IDTypeInfo IDType_ID_BR = { - .id_code = ID_BR, - .id_filter = FILTER_ID_BR, - .main_listbase_index = INDEX_ID_BR, - .struct_size = sizeof(Brush), - .name = "Brush", - .name_plural = "brushes", - .translation_context = BLT_I18NCONTEXT_ID_BRUSH, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, - .asset_type_info = NULL, - - .init_data = brush_init_data, - .copy_data = brush_copy_data, - .free_data = brush_free_data, - .make_local = brush_make_local, - .foreach_id = brush_foreach_id, - .foreach_cache = NULL, - .foreach_path = brush_foreach_path, - .owner_get = NULL, - - .blend_write = brush_blend_write, - .blend_read_data = brush_blend_read_data, - .blend_read_lib = brush_blend_read_lib, - .blend_read_expand = brush_blend_read_expand, - - .blend_read_undo_preserve = brush_undo_preserve, - - .lib_override_apply_post = NULL, + /* id_code */ ID_BR, + /* id_filter */ FILTER_ID_BR, + /* main_listbase_index */ INDEX_ID_BR, + /* struct_size */ sizeof(Brush), + /* name */ "Brush", + /* name_plural */ "brushes", + /* translation_context */ BLT_I18NCONTEXT_ID_BRUSH, + /* flags */ IDTYPE_FLAGS_NO_ANIMDATA, + /* asset_type_info */ nullptr, + + /* init_data */ brush_init_data, + /* copy_data */ brush_copy_data, + /* free_data */ brush_free_data, + /* make_local */ brush_make_local, + /* foreach_id */ brush_foreach_id, + /* foreach_cache */ nullptr, + /* foreach_path */ brush_foreach_path, + /* owner_get */ nullptr, + + /* blend_write */ brush_blend_write, + /* blend_read_data */ brush_blend_read_data, + /* blend_read_lib */ brush_blend_read_lib, + /* blend_read_expand */ brush_blend_read_expand, + + /* blend_read_undo_preserve */ brush_undo_preserve, + + /* lib_override_apply_post */ nullptr, }; static RNG *brush_rng; @@ -432,11 +432,11 @@ void BKE_brush_system_init(void) void BKE_brush_system_exit(void) { - if (brush_rng == NULL) { + if (brush_rng == nullptr) { return; } BLI_rng_free(brush_rng); - brush_rng = NULL; + brush_rng = nullptr; } static void brush_defaults(Brush *brush) @@ -444,7 +444,8 @@ static void brush_defaults(Brush *brush) const Brush *brush_def = DNA_struct_default_get(Brush); -#define FROM_DEFAULT(member) memcpy(&brush->member, &brush_def->member, sizeof(brush->member)) +#define FROM_DEFAULT(member) \ + memcpy((void *)&brush->member, (void *)&brush_def->member, sizeof(brush->member)) #define FROM_DEFAULT_PTR(member) memcpy(brush->member, brush_def->member, sizeof(brush->member)) FROM_DEFAULT(blend); @@ -494,9 +495,7 @@ static void brush_defaults(Brush *brush) Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) { - Brush *brush; - - brush = BKE_id_new(bmain, ID_BR, name); + Brush *brush = (Brush *)BKE_id_new(bmain, ID_BR, name); brush->ob_mode = ob_mode; @@ -509,8 +508,8 @@ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) void BKE_brush_init_gpencil_settings(Brush *brush) { - if (brush->gpencil_settings == NULL) { - brush->gpencil_settings = MEM_callocN(sizeof(BrushGpencilSettings), "BrushGpencilSettings"); + if (brush->gpencil_settings == nullptr) { + brush->gpencil_settings = MEM_cnew<BrushGpencilSettings>("BrushGpencilSettings"); } brush->gpencil_settings->draw_smoothlvl = 1; @@ -536,7 +535,7 @@ void BKE_brush_init_gpencil_settings(Brush *brush) Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name, eObjectMode mode) { - Paint *paint = NULL; + Paint *paint = nullptr; Brush *brush; switch (mode) { case OB_MODE_PAINT_GPENCIL: { @@ -588,15 +587,15 @@ bool BKE_brush_delete(Main *bmain, Brush *brush) return true; } -/* grease pencil cumapping->preset */ -typedef enum eGPCurveMappingPreset { +/** Local grease pencil curve mapping preset. */ +using eGPCurveMappingPreset = enum eGPCurveMappingPreset { GPCURVE_PRESET_PENCIL = 0, GPCURVE_PRESET_INK = 1, GPCURVE_PRESET_INKNOISE = 2, GPCURVE_PRESET_MARKER = 3, GPCURVE_PRESET_CHISEL_SENSIVITY = 4, GPCURVE_PRESET_CHISEL_STRENGTH = 5, -} eGPCurveMappingPreset; +}; static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) { @@ -605,7 +604,7 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) } cuma->totpoint = tot; - cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), __func__); + cuma->curve = (CurveMapPoint *)MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), __func__); switch (preset) { case GPCURVE_PRESET_PENCIL: @@ -673,7 +672,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) #define SMOOTH_STROKE_FACTOR 0.9f #define ACTIVE_SMOOTH 0.35f - CurveMapping *custom_curve = NULL; + CurveMapping *custom_curve = nullptr; /* Optionally assign a material preset. */ enum { @@ -695,7 +694,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->curve_preset = BRUSH_CURVE_SMOOTH; - if (brush->gpencil_settings == NULL) { + if (brush->gpencil_settings == nullptr) { return; } @@ -1266,8 +1265,8 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) * This material is required because the brush uses the material * to define how the stroke is drawn. */ const char *ma_id = "Dots Stroke"; - Material *ma = BLI_findstring(&bmain->materials, ma_id, offsetof(ID, name) + 2); - if (ma == NULL) { + Material *ma = (Material *)BLI_findstring(&bmain->materials, ma_id, offsetof(ID, name) + 2); + if (ma == nullptr) { ma = BKE_gpencil_material_add(bmain, ma_id); ma->gp_style->mode = GP_MATERIAL_MODE_DOT; BLI_assert(ma->id.us == 1); @@ -1287,19 +1286,19 @@ static Brush *gpencil_brush_ensure( Main *bmain, ToolSettings *ts, const char *brush_name, eObjectMode mode, bool *r_new) { *r_new = false; - Brush *brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + Brush *brush = (Brush *)BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); /* If the brush exist, but the type is not GPencil or the mode is wrong, create a new one. */ - if ((brush != NULL) && ((brush->gpencil_settings == NULL) || (brush->ob_mode != mode))) { - brush = NULL; + if ((brush != nullptr) && ((brush->gpencil_settings == nullptr) || (brush->ob_mode != mode))) { + brush = nullptr; } - if (brush == NULL) { + if (brush == nullptr) { brush = BKE_brush_add_gpencil(bmain, ts, brush_name, mode); *r_new = true; } - if (brush->gpencil_settings == NULL) { + if (brush->gpencil_settings == nullptr) { BKE_brush_init_gpencil_settings(brush); } @@ -1399,7 +1398,7 @@ void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool r } /* Set default Draw brush. */ - if ((reset == false) && (brush_prev != NULL)) { + if ((reset == false) && (brush_prev != nullptr)) { BKE_paint_brush_set(paint, brush_prev); } else { @@ -1443,11 +1442,11 @@ void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts, const bool } /* Set default Vertex brush. */ - if (reset || brush_prev == NULL) { + if (reset || brush_prev == nullptr) { BKE_paint_brush_set(vertexpaint, deft_vertex); } else { - if (brush_prev != NULL) { + if (brush_prev != nullptr) { BKE_paint_brush_set(vertexpaint, brush_prev); } } @@ -1517,11 +1516,11 @@ void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts, const bool } /* Set default brush. */ - if (reset || brush_prev == NULL) { + if (reset || brush_prev == nullptr) { BKE_paint_brush_set(sculptpaint, deft_sculpt); } else { - if (brush_prev != NULL) { + if (brush_prev != nullptr) { BKE_paint_brush_set(sculptpaint, brush_prev); } } @@ -1542,11 +1541,11 @@ void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, const bool deft_weight = brush; /* save default brush. */ /* Set default brush. */ - if (reset || brush_prev == NULL) { + if (reset || brush_prev == nullptr) { BKE_paint_brush_set(weightpaint, deft_weight); } else { - if (brush_prev != NULL) { + if (brush_prev != nullptr) { BKE_paint_brush_set(weightpaint, brush_prev); } } @@ -1554,32 +1553,31 @@ void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, const bool void BKE_brush_init_curves_sculpt_settings(Brush *brush) { - if (brush->curves_sculpt_settings == NULL) { - brush->curves_sculpt_settings = MEM_callocN(sizeof(BrushCurvesSculptSettings), __func__); + if (brush->curves_sculpt_settings == nullptr) { + brush->curves_sculpt_settings = MEM_cnew<BrushCurvesSculptSettings>(__func__); } BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings; settings->add_amount = 1; settings->points_per_curve = 8; settings->minimum_length = 0.01f; settings->curve_length = 0.3f; + settings->density_add_attempts = 100; } struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) { - Brush *brush; - - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { if (brush->ob_mode & ob_mode) { return brush; } } - return NULL; + return nullptr; } void BKE_brush_debug_print_state(Brush *br) { /* create a fake brush and set it to the defaults */ - Brush def = {{NULL}}; + Brush def = {{nullptr}}; brush_defaults(&def); #define BR_TEST(field, t) \ @@ -1943,8 +1941,8 @@ void BKE_brush_sculpt_reset(Brush *br) void BKE_brush_curve_preset(Brush *b, eCurveMappingPreset preset) { - CurveMapping *cumap = NULL; - CurveMap *cuma = NULL; + CurveMapping *cumap = nullptr; + CurveMap *cuma = nullptr; if (!b->curve) { b->curve = BKE_curvemapping_add(1, 0, 0, 1, 1); @@ -2475,7 +2473,7 @@ static bool brush_gen_texture(const Brush *br, float *rect) { const MTex *mtex = (use_secondary) ? &br->mask_mtex : &br->mtex; - if (mtex->tex == NULL) { + if (mtex->tex == nullptr) { return false; } @@ -2490,7 +2488,7 @@ static bool brush_gen_texture(const Brush *br, float intensity; float rgba_dummy[4]; - RE_texture_evaluate(mtex, co, 0, NULL, false, false, &intensity, rgba_dummy); + RE_texture_evaluate(mtex, co, 0, nullptr, false, false, &intensity, rgba_dummy); rect[iy * side + ix] = intensity; } @@ -2501,12 +2499,12 @@ static bool brush_gen_texture(const Brush *br, struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool display_gradient) { - ImBuf *im = MEM_callocN(sizeof(ImBuf), "radial control texture"); + ImBuf *im = MEM_cnew<ImBuf>("radial control texture"); int side = 512; int half = side / 2; BKE_curvemapping_init(br->curve); - im->rect_float = MEM_callocN(sizeof(float) * side * side, "radial control rect"); + im->rect_float = (float *)MEM_callocN(sizeof(float) * side * side, "radial control rect"); im->x = im->y = side; const bool have_texture = brush_gen_texture(br, side, secondary, im->rect_float); diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 35c2039634a..03dd5c89b70 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -19,6 +19,7 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BKE_attribute.hh" #include "BKE_bvhutils.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" @@ -1430,13 +1431,17 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data, return nullptr; } - for (int i = 0; i < pointcloud->totpoint; i++) { - BLI_bvhtree_insert(tree, i, pointcloud->co[i], 1); + blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(*pointcloud); + blender::VArraySpan<blender::float3> positions = attributes.lookup_or_default<blender::float3>( + "position", ATTR_DOMAIN_POINT, blender::float3(0)); + + for (const int i : positions.index_range()) { + BLI_bvhtree_insert(tree, i, positions[i], 1); } BLI_assert(BLI_bvhtree_get_len(tree) == pointcloud->totpoint); bvhtree_balance(tree, false); - data->coords = pointcloud->co; + data->coords = (const float(*)[3])positions.data(); data->tree = tree; data->nearest_callback = nullptr; diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index fbe03ac365c..9aea3b2768f 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -25,6 +25,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_action.h" #include "BKE_anim_data.h" #include "BKE_camera.h" #include "BKE_idtype.h" @@ -221,7 +222,16 @@ float BKE_camera_object_dof_distance(const Object *ob) if (cam->dof.focus_object) { float view_dir[3], dof_dir[3]; normalize_v3_v3(view_dir, ob->obmat[2]); - sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]); + bPoseChannel *pchan = BKE_pose_channel_find_name(cam->dof.focus_object->pose, + cam->dof.focus_subtarget); + if (pchan) { + float posemat[4][4]; + mul_m4_m4m4(posemat, cam->dof.focus_object->obmat, pchan->pose_mat); + sub_v3_v3v3(dof_dir, ob->obmat[3], posemat[3]); + } + else { + sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]); + } return fabsf(dot_v3v3(view_dir, dof_dir)); } return cam->dof.focus_distance; @@ -559,6 +569,11 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec #define CAMERA_VIEWFRAME_NUM_PLANES 4 +#define Y_MIN 0 +#define Y_MAX 1 +#define Z_MIN 2 +#define Z_MAX 3 + typedef struct CameraViewFrameData { float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */ float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */ @@ -622,15 +637,13 @@ static void camera_frame_fit_data_init(const Scene *scene, invert_m4(camera_rotmat_transposed_inversed); /* Extract frustum planes from projection matrix. */ - planes_from_projmat( - params->winmat, - /* left right top bottom near far */ - data->plane_tx[2], - data->plane_tx[0], - data->plane_tx[3], - data->plane_tx[1], - NULL, - NULL); + planes_from_projmat(params->winmat, + data->plane_tx[Y_MIN], + data->plane_tx[Y_MAX], + data->plane_tx[Z_MIN], + data->plane_tx[Z_MAX], + NULL, + NULL); /* Rotate planes and get normals from them */ for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { @@ -670,21 +683,21 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, const float *cam_axis_y = data->camera_rotmat[1]; const float *cam_axis_z = data->camera_rotmat[2]; const float *dists = data->dist_vals; - float scale_diff; + const float dist_span_y = dists[Y_MIN] + dists[Y_MAX]; + const float dist_span_z = dists[Z_MIN] + dists[Z_MAX]; + const float dist_mid_y = (dists[Y_MIN] - dists[Y_MAX]) * 0.5f; + const float dist_mid_z = (dists[Z_MIN] - dists[Z_MAX]) * 0.5f; + const float scale_diff = (dist_span_z < dist_span_y) ? + (dist_span_z * (BLI_rctf_size_x(¶ms->viewplane) / + BLI_rctf_size_y(¶ms->viewplane))) : + (dist_span_y * (BLI_rctf_size_y(¶ms->viewplane) / + BLI_rctf_size_x(¶ms->viewplane))); - if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { - scale_diff = (dists[1] + dists[3]) * - (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); - } - else { - scale_diff = (dists[0] + dists[2]) * - (BLI_rctf_size_y(¶ms->viewplane) / BLI_rctf_size_x(¶ms->viewplane)); - } *r_scale = params->ortho_scale - scale_diff; zero_v3(r_co); - madd_v3_v3fl(r_co, cam_axis_x, (dists[2] - dists[0]) * 0.5f + params->shiftx * scale_diff); - madd_v3_v3fl(r_co, cam_axis_y, (dists[1] - dists[3]) * 0.5f + params->shifty * scale_diff); + madd_v3_v3fl(r_co, cam_axis_x, dist_mid_y + (params->shiftx * scale_diff)); + madd_v3_v3fl(r_co, cam_axis_y, dist_mid_z + (params->shifty * scale_diff)); madd_v3_v3fl(r_co, cam_axis_z, -(data->z_range[0] - 1.0f - params->clip_start)); } else { @@ -700,36 +713,37 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]); } - if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) || - (!isect_plane_plane_v3(plane_tx[1], plane_tx[3], plane_isect_2, plane_isect_2_no))) { + if ((!isect_plane_plane_v3( + plane_tx[Y_MIN], plane_tx[Y_MAX], plane_isect_1, plane_isect_1_no)) || + (!isect_plane_plane_v3( + plane_tx[Z_MIN], plane_tx[Z_MAX], plane_isect_2, plane_isect_2_no))) { return false; } add_v3_v3v3(plane_isect_1_other, plane_isect_1, plane_isect_1_no); add_v3_v3v3(plane_isect_2_other, plane_isect_2, plane_isect_2_no); - if (isect_line_line_v3(plane_isect_1, - plane_isect_1_other, - plane_isect_2, - plane_isect_2_other, - plane_isect_pt_1, - plane_isect_pt_2) == 0) { + if (!isect_line_line_v3(plane_isect_1, + plane_isect_1_other, + plane_isect_2, + plane_isect_2_other, + plane_isect_pt_1, + plane_isect_pt_2)) { return false; } float cam_plane_no[3]; float plane_isect_delta[3]; - float plane_isect_delta_len; - float shift_fac = BKE_camera_sensor_size( - params->sensor_fit, params->sensor_x, params->sensor_y) / - params->lens; + const float shift_fac = BKE_camera_sensor_size( + params->sensor_fit, params->sensor_x, params->sensor_y) / + params->lens; /* we want (0, 0, -1) transformed by camera_rotmat, this is a quicker shortcut. */ negate_v3_v3(cam_plane_no, data->camera_rotmat[2]); sub_v3_v3v3(plane_isect_delta, plane_isect_pt_2, plane_isect_pt_1); - plane_isect_delta_len = len_v3(plane_isect_delta); + const float plane_isect_delta_len = len_v3(plane_isect_delta); if (dot_v3v3(plane_isect_delta, cam_plane_no) > 0.0f) { copy_v3_v3(r_co, plane_isect_pt_1); @@ -755,6 +769,11 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, return true; } +#undef Y_MIN +#undef Y_MAX +#undef Z_MIN +#undef Z_MAX + bool BKE_camera_view_frame_fit_to_scene(Depsgraph *depsgraph, const Scene *scene, Object *camera_ob, diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index aa09541c043..0534899a86c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -116,7 +116,6 @@ void BKE_constraint_unique_name(bConstraint *con, ListBase *list) /* ----------------- Evaluation Loop Preparation --------------- */ -/* package an object/bone for use in constraint evaluation */ bConstraintOb *BKE_constraints_make_evalob( Depsgraph *depsgraph, Scene *scene, Object *ob, void *subdata, short datatype) { @@ -5845,9 +5844,12 @@ static bConstraint *add_new_constraint(Object *ob, return con; } -bool BKE_constraint_target_uses_bbone(struct bConstraint *con, - struct bConstraintTarget *UNUSED(ct)) +bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *ct) { + if (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE) { + return false; + } + return (con->flag & CONSTRAINT_BBONE_SHAPE) || (con->type == CONSTRAINT_TYPE_ARMATURE); } @@ -6260,6 +6262,9 @@ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph, } } + /* Initialize the custom space for use in calculating the matrices. */ + BKE_constraint_custom_object_space_init(cob, con); + /* get targets - we only need the first one though (and there should only be one) */ cti->get_constraint_targets(con, &targets); @@ -6323,33 +6328,23 @@ void BKE_constraint_targets_for_solving_get(struct Depsgraph *depsgraph, } } -void BKE_constraint_custom_object_space_get(float r_mat[4][4], bConstraint *con) +void BKE_constraint_custom_object_space_init(bConstraintOb *cob, bConstraint *con) { - if (!con || - (con->ownspace != CONSTRAINT_SPACE_CUSTOM && con->tarspace != CONSTRAINT_SPACE_CUSTOM)) { - return; - } - bConstraintTarget *ct; - ListBase target = {NULL, NULL}; - SINGLETARGET_GET_TARS(con, con->space_object, con->space_subtarget, ct, &target); - - /* Basically default_get_tarmat but without the unused parameters. */ - if (VALID_CONS_TARGET(ct)) { - constraint_target_to_mat4(ct->tar, - ct->subtarget, + if (con && con->space_object && is_custom_space_needed(con)) { + /* Basically default_get_tarmat but without the unused parameters. */ + constraint_target_to_mat4(con->space_object, + con->space_subtarget, NULL, - ct->matrix, + cob->space_obj_world_matrix, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_WORLD, 0, 0); - copy_m4_m4(r_mat, ct->matrix); - } - else { - unit_m4(r_mat); + + return; } - SINGLETARGET_FLUSH_TARS(con, con->space_object, con->space_subtarget, ct, &target, true); + unit_m4(cob->space_obj_world_matrix); } /* ---------- Evaluation ----------- */ @@ -6394,8 +6389,8 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph, */ enf = con->enforce; - /* Get custom space matrix. */ - BKE_constraint_custom_object_space_get(cob->space_obj_world_matrix, con); + /* Initialize the custom space for use in calculating the matrices. */ + BKE_constraint_custom_object_space_init(cob, con); /* make copy of world-space matrix pre-constraint for use with blending later */ copy_m4_m4(oldmat, cob->matrix); diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.cc index 14e862c2377..978606ef1fa 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.cc @@ -19,7 +19,10 @@ #include "BKE_DerivedMesh.h" #include "BKE_crazyspace.h" +#include "BKE_crazyspace.hh" +#include "BKE_curves.hh" #include "BKE_editmesh.h" +#include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -70,9 +73,9 @@ static void set_crazy_vertex_quat(float r_quat[4], static bool modifiers_disable_subsurf_temporary(struct Scene *scene, Object *ob) { bool disabled = false; - int cageIndex = BKE_modifiers_get_cage_index(scene, ob, NULL, 1); + int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, 1); - ModifierData *md = ob->modifiers.first; + ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first); for (int i = 0; md && i <= cageIndex; i++, md = md->next) { if (md->type == eModifierType_Subsurf) { md->mode ^= eModifierMode_DisableTemporary; @@ -88,7 +91,7 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object Scene *scene = DEG_get_input_scene(depsgraph); Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit); - Mesh *mesh_eval = obedit_eval->data; + Mesh *mesh_eval = static_cast<Mesh *>(obedit_eval->data); BMEditMesh *editmesh_eval = mesh_eval->edit_mesh; /* disable subsurf temporal, get mapped cos, and enable it */ @@ -102,7 +105,8 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object depsgraph, scene, obedit, &CD_MASK_BAREMESH); const int nverts = editmesh_eval->bm->totvert; - float(*vertexcos)[3] = MEM_mallocN(sizeof(*vertexcos) * nverts, "vertexcos map"); + float(*vertexcos)[3] = static_cast<float(*)[3]>( + MEM_mallocN(sizeof(*vertexcos) * nverts, "vertexcos map")); mesh_get_mapped_verts_coords(mesh_eval_cage, vertexcos, nverts); /* set back the flag, no new cage needs to be built, transform does it */ @@ -234,13 +238,13 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra float (**deformcos)[3]) { ModifierData *md; - Mesh *me_input = ob->data; - Mesh *me = NULL; + Mesh *me_input = static_cast<Mesh *>(ob->data); + Mesh *me = nullptr; int i, a, modifiers_left_num = 0, verts_num = 0; - int cageIndex = BKE_modifiers_get_cage_index(scene, ob, NULL, 1); - float(*defmats)[3][3] = NULL, (*deformedVerts)[3] = NULL; + int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, 1); + float(*defmats)[3][3] = nullptr, (*deformedVerts)[3] = nullptr; VirtualModifierData virtualModifierData; - ModifierEvalContext mectx = {depsgraph, ob, 0}; + ModifierEvalContext mectx = {depsgraph, ob, ModifierApplyFlag(0)}; BKE_modifiers_clear_errors(ob); @@ -250,9 +254,9 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra * modifiers with on cage editing that are enabled and support computing * deform matrices */ for (i = 0; md && i <= cageIndex; i++, md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); - if (!editbmesh_modifier_is_enabled(scene, ob, md, me != NULL)) { + if (!editbmesh_modifier_is_enabled(scene, ob, md, me != nullptr)) { continue; } @@ -261,13 +265,14 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; CustomData_MeshMasks cd_mask_extra = CD_MASK_BAREMESH; CDMaskLink *datamasks = BKE_modifier_calc_data_masks( - scene, ob, md, &cd_mask_extra, required_mode, NULL, NULL); + scene, ob, md, &cd_mask_extra, required_mode, nullptr, nullptr); cd_mask_extra = datamasks->mask; - BLI_linklist_free((LinkNode *)datamasks, NULL); + BLI_linklist_free((LinkNode *)datamasks, nullptr); - me = BKE_mesh_wrapper_from_editmesh_with_coords(em, &cd_mask_extra, NULL, me_input); + me = BKE_mesh_wrapper_from_editmesh_with_coords(em, &cd_mask_extra, nullptr, me_input); deformedVerts = editbmesh_vert_coords_alloc(em, &verts_num); - defmats = MEM_mallocN(sizeof(*defmats) * verts_num, "defmats"); + defmats = static_cast<float(*)[3][3]>( + MEM_mallocN(sizeof(*defmats) * verts_num, "defmats")); for (a = 0; a < verts_num; a++) { unit_m3(defmats[a]); @@ -281,14 +286,14 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra } for (; md && i <= cageIndex; md = md->next, i++) { - if (editbmesh_modifier_is_enabled(scene, ob, md, me != NULL) && + if (editbmesh_modifier_is_enabled(scene, ob, md, me != nullptr) && BKE_modifier_is_correctable_deformed(md)) { modifiers_left_num++; } } if (me) { - BKE_id_free(NULL, me); + BKE_id_free(nullptr, me); } *deformmats = defmats; @@ -309,8 +314,8 @@ static void crazyspace_init_object_for_eval(struct Depsgraph *depsgraph, Object *object_crazy) { Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - *object_crazy = *object_eval; - if (object_crazy->runtime.data_orig != NULL) { + *object_crazy = blender::dna::shallow_copy(*object_eval); + if (object_crazy->runtime.data_orig != nullptr) { object_crazy->data = object_crazy->runtime.data_orig; } } @@ -321,7 +326,8 @@ static void crazyspace_init_verts_and_matrices(const Mesh *mesh, { int verts_num; *deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num); - *deformmats = MEM_callocN(sizeof(**deformmats) * verts_num, "defmats"); + *deformmats = static_cast<float(*)[3][3]>( + MEM_callocN(sizeof(**deformmats) * verts_num, "defmats")); for (int a = 0; a < verts_num; a++) { unit_m3((*deformmats)[a]); } @@ -333,13 +339,13 @@ static bool crazyspace_modifier_supports_deform_matrices(ModifierData *md) if (ELEM(md->type, eModifierType_Subsurf, eModifierType_Multires)) { return true; } - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); return (mti->type == eModifierTypeType_OnlyDeform); } static bool crazyspace_modifier_supports_deform(ModifierData *md) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); return (mti->type == eModifierTypeType_OnlyDeform); } @@ -350,20 +356,20 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, float (**deformcos)[3]) { ModifierData *md; - Mesh *me_eval = NULL; - float(*defmats)[3][3] = NULL, (*deformedVerts)[3] = NULL; + Mesh *me_eval = nullptr; + float(*defmats)[3][3] = nullptr, (*deformedVerts)[3] = nullptr; int modifiers_left_num = 0; VirtualModifierData virtualModifierData; Object object_eval; crazyspace_init_object_for_eval(depsgraph, object, &object_eval); MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, 0); const bool is_sculpt_mode = (object->mode & OB_MODE_SCULPT) != 0; - const bool has_multires = mmd != NULL && mmd->sculptlvl > 0; - const ModifierEvalContext mectx = {depsgraph, &object_eval, 0}; + const bool has_multires = mmd != nullptr && mmd->sculptlvl > 0; + const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)}; if (is_sculpt_mode && has_multires) { - *deformmats = NULL; - *deformcos = NULL; + *deformmats = nullptr; + *deformcos = nullptr; return modifiers_left_num; } @@ -375,10 +381,10 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, } if (crazyspace_modifier_supports_deform_matrices(md)) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - if (defmats == NULL) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); + if (defmats == nullptr) { /* NOTE: Evaluated object is re-set to its original un-deformed state. */ - Mesh *me = object_eval.data; + Mesh *me = static_cast<Mesh *>(object_eval.data); me_eval = BKE_mesh_copy_for_eval(me, true); crazyspace_init_verts_and_matrices(me_eval, &defmats, &deformedVerts); } @@ -405,8 +411,8 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, } } - if (me_eval != NULL) { - BKE_id_free(NULL, me_eval); + if (me_eval != nullptr) { + BKE_id_free(nullptr, me_eval); } *deformmats = defmats; @@ -429,21 +435,21 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, * Need additional crazy-space correction. */ Mesh *mesh = (Mesh *)object->data; - Mesh *mesh_eval = NULL; + Mesh *mesh_eval = nullptr; - if (*deformcos == NULL) { + if (*deformcos == nullptr) { crazyspace_init_verts_and_matrices(mesh, deformmats, deformcos); } float(*deformedVerts)[3] = *deformcos; - float(*origVerts)[3] = MEM_dupallocN(deformedVerts); + float(*origVerts)[3] = static_cast<float(*)[3]>(MEM_dupallocN(deformedVerts)); float(*quats)[4]; int i, deformed = 0; VirtualModifierData virtualModifierData; Object object_eval; crazyspace_init_object_for_eval(depsgraph, object, &object_eval); ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&object_eval, &virtualModifierData); - const ModifierEvalContext mectx = {depsgraph, &object_eval, 0}; + const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)}; for (; md; md = md->next) { if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { @@ -451,7 +457,7 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, } if (crazyspace_modifier_supports_deform(md)) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); /* skip leading modifiers which have been already * handled in sculpt_get_first_deform_matrices */ @@ -459,7 +465,7 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, continue; } - if (mesh_eval == NULL) { + if (mesh_eval == nullptr) { mesh_eval = BKE_mesh_copy_for_eval(mesh, true); } @@ -468,7 +474,7 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, } } - quats = MEM_mallocN(mesh->totvert * sizeof(*quats), "crazy quats"); + quats = static_cast<float(*)[4]>(MEM_mallocN(mesh->totvert * sizeof(*quats), "crazy quats")); BKE_crazyspace_set_quats_mesh(mesh, origVerts, deformedVerts, quats); @@ -483,17 +489,18 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, MEM_freeN(origVerts); MEM_freeN(quats); - if (mesh_eval != NULL) { - BKE_id_free(NULL, mesh_eval); + if (mesh_eval != nullptr) { + BKE_id_free(nullptr, mesh_eval); } } - if (*deformmats == NULL) { + if (*deformmats == nullptr) { int a, verts_num; Mesh *mesh = (Mesh *)object->data; *deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num); - *deformmats = MEM_callocN(sizeof(*(*deformmats)) * verts_num, "defmats"); + *deformmats = static_cast<float(*)[3][3]>( + MEM_callocN(sizeof(*(*deformmats)) * verts_num, "defmats")); for (a = 0; a < verts_num; a++) { unit_m3((*deformmats)[a]); @@ -510,8 +517,8 @@ void BKE_crazyspace_api_eval(Depsgraph *depsgraph, Object *object, struct ReportList *reports) { - if (object->runtime.crazyspace_deform_imats != NULL || - object->runtime.crazyspace_deform_cos != NULL) { + if (object->runtime.crazyspace_deform_imats != nullptr || + object->runtime.crazyspace_deform_cos != nullptr) { return; } @@ -582,3 +589,64 @@ void BKE_crazyspace_api_eval_clear(Object *object) } /** \} */ + +namespace blender::bke::crazyspace { + +GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, + const Object &ob_orig) +{ + BLI_assert(ob_orig.type == OB_CURVES); + const Curves &curves_id_orig = *static_cast<const Curves *>(ob_orig.data); + const CurvesGeometry &curves_orig = CurvesGeometry::wrap(curves_id_orig.geometry); + const int points_num = curves_orig.points_num(); + + GeometryDeformation deformation; + /* Use the undeformed positions by default. */ + deformation.positions = curves_orig.positions(); + + const Object *ob_eval = DEG_get_evaluated_object(&depsgraph, const_cast<Object *>(&ob_orig)); + if (ob_eval == nullptr) { + return deformation; + } + const GeometrySet *geometry_eval = ob_eval->runtime.geometry_set_eval; + if (geometry_eval == nullptr) { + return deformation; + } + + /* If available, use deformation information generated during evaluation. */ + const GeometryComponentEditData *edit_component_eval = + geometry_eval->get_component_for_read<GeometryComponentEditData>(); + bool uses_extra_positions = false; + if (edit_component_eval != nullptr) { + const CurvesEditHints *edit_hints = edit_component_eval->curves_edit_hints_.get(); + if (edit_hints != nullptr && &edit_hints->curves_id_orig == &curves_id_orig) { + if (edit_hints->positions.has_value()) { + BLI_assert(edit_hints->positions->size() == points_num); + deformation.positions = *edit_hints->positions; + uses_extra_positions = true; + } + if (edit_hints->deform_mats.has_value()) { + BLI_assert(edit_hints->deform_mats->size() == points_num); + deformation.deform_mats = *edit_hints->deform_mats; + } + } + } + + /* Use the positions of the evaluated curves directly, if the number of points matches. */ + if (!uses_extra_positions) { + const CurveComponent *curves_component_eval = + geometry_eval->get_component_for_read<CurveComponent>(); + if (curves_component_eval != nullptr) { + const Curves *curves_id_eval = curves_component_eval->get_for_read(); + if (curves_id_eval != nullptr) { + const CurvesGeometry &curves_eval = CurvesGeometry::wrap(curves_id_eval->geometry); + if (curves_eval.points_num() == points_num) { + deformation.positions = curves_eval.positions(); + } + } + } + } + return deformation; +} + +} // namespace blender::bke::crazyspace diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc index 5ba17f1761b..59b09384698 100644 --- a/source/blender/blenkernel/intern/curve_bezier.cc +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -16,15 +16,14 @@ bool segment_is_vector(const Span<int8_t> handle_types_left, const int segment_index) { BLI_assert(handle_types_left.index_range().drop_back(1).contains(segment_index)); - return handle_types_right[segment_index] == BEZIER_HANDLE_VECTOR && - handle_types_left[segment_index + 1] == BEZIER_HANDLE_VECTOR; + return segment_is_vector(handle_types_right[segment_index], + handle_types_left[segment_index + 1]); } bool last_cyclic_segment_is_vector(const Span<int8_t> handle_types_left, const Span<int8_t> handle_types_right) { - return handle_types_right.last() == BEZIER_HANDLE_VECTOR && - handle_types_left.first() == BEZIER_HANDLE_VECTOR; + return segment_is_vector(handle_types_right.last(), handle_types_left.first()); } void calculate_evaluated_offsets(const Span<int8_t> handle_types_left, @@ -59,6 +58,26 @@ void calculate_evaluated_offsets(const Span<int8_t> handle_types_left, evaluated_offsets.last() = offset; } +Insertion insert(const float3 &point_prev, + const float3 &handle_prev, + const float3 &handle_next, + const float3 &point_next, + float parameter) +{ + /* De Casteljau Bezier subdivision. */ + BLI_assert(parameter <= 1.0f && parameter >= 0.0f); + + const float3 center_point = math::interpolate(handle_prev, handle_next, parameter); + + Insertion result; + result.handle_prev = math::interpolate(point_prev, handle_prev, parameter); + result.handle_next = math::interpolate(handle_next, point_next, parameter); + result.left_handle = math::interpolate(result.handle_prev, center_point, parameter); + result.right_handle = math::interpolate(center_point, result.handle_next, parameter); + result.position = math::interpolate(result.left_handle, result.right_handle, parameter); + return result; +} + static float3 calculate_aligned_handle(const float3 &position, const float3 &other_handle, const float3 &aligned_handle) @@ -106,11 +125,11 @@ static void calculate_point_handles(const HandleType type_left, } if (type_left == BEZIER_HANDLE_VECTOR) { - left = math::interpolate(position, prev_position, 1.0f / 3.0f); + left = calculate_vector_handle(position, prev_position); } if (type_right == BEZIER_HANDLE_VECTOR) { - right = math::interpolate(position, next_position, 1.0f / 3.0f); + right = calculate_vector_handle(position, next_position); } /* When one of the handles is "aligned" handle, it must be aligned with the other, i.e. point in diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index 4b2174c912c..952d59edcf9 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -11,7 +11,7 @@ namespace blender::bke::curves::catmull_rom { int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution) { - const int eval_num = resolution * curve_segment_num(points_num, cyclic); + const int eval_num = resolution * segments_num(points_num, cyclic); /* If the curve isn't cyclic, one last point is added to the final point. */ return cyclic ? eval_num : eval_num + 1; } @@ -39,15 +39,18 @@ static void evaluate_segment(const T &a, const T &b, const T &c, const T &d, Mut } } -template<typename T> +/** + * \param range_fn: Returns an index range describing where in the #dst span each segment should be + * evaluated to, and how many points to add to it. This is used to avoid the need to allocate an + * actual offsets array in typical evaluation use cases where the resolution is per-curve. + */ +template<typename T, typename RangeForSegmentFn> static void interpolate_to_evaluated(const Span<T> src, const bool cyclic, - const int resolution, + const RangeForSegmentFn &range_fn, MutableSpan<T> dst) { - BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution)); - /* - First deal with one and two point curves need special attention. * - Then evaluate the first and last segment(s) whose control points need to wrap around * to the other side of the source array. @@ -57,11 +60,14 @@ static void interpolate_to_evaluated(const Span<T> src, dst.first() = src.first(); return; } + + const IndexRange first = range_fn(0); + if (src.size() == 2) { - evaluate_segment(src.first(), src.first(), src.last(), src.last(), dst.take_front(resolution)); + evaluate_segment(src.first(), src.first(), src.last(), src.last(), dst.slice(first)); if (cyclic) { - evaluate_segment( - src.last(), src.last(), src.first(), src.first(), dst.take_back(resolution)); + const IndexRange last = range_fn(1); + evaluate_segment(src.last(), src.last(), src.first(), src.first(), dst.slice(last)); } else { dst.last() = src.last(); @@ -69,39 +75,65 @@ static void interpolate_to_evaluated(const Span<T> src, return; } + const IndexRange second_to_last = range_fn(src.index_range().last(1)); + const IndexRange last = range_fn(src.index_range().last()); if (cyclic) { - /* The first segment. */ - evaluate_segment(src.last(), src[0], src[1], src[2], dst.take_front(resolution)); - /* The second-to-last segment. */ - evaluate_segment(src.last(2), - src.last(1), - src.last(), - src.first(), - dst.take_back(resolution * 2).drop_back(resolution)); - /* The last segment. */ - evaluate_segment(src.last(1), src.last(), src[0], src[1], dst.take_back(resolution)); + evaluate_segment(src.last(), src[0], src[1], src[2], dst.slice(first)); + evaluate_segment(src.last(2), src.last(1), src.last(), src.first(), dst.slice(second_to_last)); + evaluate_segment(src.last(1), src.last(), src[0], src[1], dst.slice(last)); } else { - /* The first segment. */ - evaluate_segment(src[0], src[0], src[1], src[2], dst.take_front(resolution)); - /* The last segment. */ - evaluate_segment( - src.last(2), src.last(1), src.last(), src.last(), dst.drop_back(1).take_back(resolution)); - /* The final point of the last segment. */ + evaluate_segment(src[0], src[0], src[1], src[2], dst.slice(first)); + evaluate_segment(src.last(2), src.last(1), src.last(), src.last(), dst.slice(second_to_last)); + /* For non-cyclic curves, the last segment should always just have a single point. We could + * assert that the size of the provided range is 1 here, but that would require specializing + * the #range_fn implementation for the last point, which may have a performance cost. */ dst.last() = src.last(); } /* Evaluate every segment that isn't the first or last. */ - const int grain_size = std::max(512 / resolution, 1); const IndexRange inner_range = src.index_range().drop_back(2).drop_front(1); - threading::parallel_for(inner_range, grain_size, [&](IndexRange range) { + threading::parallel_for(inner_range, 512, [&](IndexRange range) { for (const int i : range) { - const IndexRange segment_range(resolution * i, resolution); - evaluate_segment(src[i - 1], src[i], src[i + 1], src[i + 2], dst.slice(segment_range)); + const IndexRange segment = range_fn(i); + evaluate_segment(src[i - 1], src[i], src[i + 1], src[i + 2], dst.slice(segment)); } }); } +template<typename T> +static void interpolate_to_evaluated(const Span<T> src, + const bool cyclic, + const int resolution, + MutableSpan<T> dst) + +{ + BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution)); + interpolate_to_evaluated( + src, + cyclic, + [resolution](const int segment_i) -> IndexRange { + return {segment_i * resolution, resolution}; + }, + dst); +} + +template<typename T> +static void interpolate_to_evaluated(const Span<T> src, + const bool cyclic, + const Span<int> evaluated_offsets, + MutableSpan<T> dst) + +{ + interpolate_to_evaluated( + src, + cyclic, + [evaluated_offsets](const int segment_i) -> IndexRange { + return bke::offsets_to_range(evaluated_offsets, segment_i); + }, + dst); +} + void interpolate_to_evaluated(const GSpan src, const bool cyclic, const int resolution, @@ -117,4 +149,19 @@ void interpolate_to_evaluated(const GSpan src, }); } +void interpolate_to_evaluated(const GSpan src, + const bool cyclic, + const Span<int> evaluated_offsets, + GMutableSpan dst) +{ + 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>()); + } + }); +} + } // namespace blender::bke::curves::catmull_rom diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index dd2bd982506..3bee82fadab 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -21,19 +21,17 @@ using blender::Array; using blender::float3; using blender::float4x4; using blender::GVArray; -using blender::GVArray_GSpan; +using blender::GVArraySpan; using blender::IndexRange; using blender::Map; using blender::MutableSpan; using blender::Span; using blender::StringRefNull; using blender::VArray; -using blender::VArray_Span; +using blender::VArraySpan; using blender::Vector; using blender::bke::AttributeIDRef; -using blender::bke::OutputAttribute; -using blender::bke::OutputAttribute_Typed; -using blender::bke::ReadAttributeLookup; +using blender::bke::AttributeMetaData; blender::Span<SplinePtr> CurveEval::splines() const { @@ -345,32 +343,31 @@ 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 GeometryComponent &src_component, - GeometryComponent &dst_component, - Span<std::string> skip) +static void copy_attributes_between_components( + const blender::bke::AttributeAccessor &src_attributes, + blender::bke::MutableAttributeAccessor &dst_attributes, + Span<std::string> skip) { - src_component.attribute_foreach( - [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (id.is_named() && skip.contains(id.name())) { - return true; - } + 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_component.attribute_try_get_for_read( - id, meta_data.domain, meta_data.data_type); - if (!src_attribute) { - return true; - } - GVArray_GSpan src_attribute_data{src_attribute}; + 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}; - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - id, meta_data.domain, meta_data.data_type); - if (!dst_attribute) { - return true; - } - dst_attribute.varray().set_all(src_attribute_data.data()); - dst_attribute.save(); - return true; - }); + 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) @@ -379,21 +376,22 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) 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(); - VArray_Span<float> nurbs_weights{ - src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; - VArray_Span<int8_t> nurbs_orders{ - src_component.attribute_get_for_read<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; - VArray_Span<int8_t> nurbs_knots_modes{ - src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; + 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)}; - VArray_Span<int8_t> handle_types_right{ - src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)}; - VArray_Span<int8_t> handle_types_left{ - src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 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(); @@ -446,9 +444,10 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) 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_component, - dst_component, + copy_attributes_between_components(src_attributes, + dst_attributes, {"curve_type", "resolution", "normal_mode", @@ -467,37 +466,38 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) 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(); - OutputAttribute_Typed<int8_t> normal_mode = - dst_component.attribute_try_get_for_output_only<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); - OutputAttribute_Typed<float> nurbs_weight; - OutputAttribute_Typed<int> nurbs_order; - OutputAttribute_Typed<int8_t> nurbs_knots_mode; + 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_component.attribute_try_get_for_output_only<float>("nurbs_weight", - ATTR_DOMAIN_POINT); - nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order", - ATTR_DOMAIN_CURVE); - nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode", - ATTR_DOMAIN_CURVE); + 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); } - OutputAttribute_Typed<int8_t> handle_type_right; - OutputAttribute_Typed<int8_t> handle_type_left; + 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_component.attribute_try_get_for_output_only<int8_t>( + handle_type_right = dst_attributes.lookup_or_add_for_write_only_span<int8_t>( "handle_type_right", ATTR_DOMAIN_POINT); - handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left", - 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.as_span()[curve_index] = curve_eval.splines()[curve_index]->normal_mode; + normal_mode.span[curve_index] = curve_eval.splines()[curve_index]->normal_mode; const IndexRange points = curves.points_for_curve(curve_index); switch (spline.type()) { @@ -505,15 +505,15 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) break; case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); - handle_type_right.as_span().slice(points).copy_from(src.handle_types_right()); - handle_type_left.as_span().slice(points).copy_from(src.handle_types_left()); + 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.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode); - nurbs_order.as_span()[curve_index] = src.order(); - nurbs_weight.as_span().slice(points).copy_from(src.weights()); + 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: { @@ -525,17 +525,18 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) curves.update_curve_types(); - normal_mode.save(); - nurbs_weight.save(); - nurbs_order.save(); - nurbs_knots_mode.save(); - handle_type_right.save(); - handle_type_left.save(); + 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_component, dst_component, {}); + copy_attributes_between_components(src_attributes, dst_attributes, {}); return curves_id; } diff --git a/source/blender/blenkernel/intern/curve_legacy_convert.cc b/source/blender/blenkernel/intern/curve_legacy_convert.cc index 61299f165be..5c62f292832 100644 --- a/source/blender/blenkernel/intern/curve_legacy_convert.cc +++ b/source/blender/blenkernel/intern/curve_legacy_convert.cc @@ -83,8 +83,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ Curves *curves_id = curves_new_nomain(0, src_curves.size()); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); - CurveComponent component; - component.replace(curves_id, GeometryOwnershipType::Editable); + MutableAttributeAccessor curves_attributes = curves.attributes_for_write(); MutableSpan<int8_t> types = curves.curve_types_for_write(); MutableSpan<bool> cyclic = curves.cyclic_for_write(); @@ -110,13 +109,13 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ } MutableSpan<float3> positions = curves.positions_for_write(); - OutputAttribute_Typed<float> radius_attribute = - component.attribute_try_get_for_output_only<float>("radius", ATTR_DOMAIN_POINT); - MutableSpan<float> radii = radius_attribute.as_span(); + SpanAttributeWriter<float> radius_attribute = + curves_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT); + MutableSpan<float> radii = radius_attribute.span; MutableSpan<float> tilts = curves.tilt_for_write(); auto create_poly = [&](IndexMask selection) { - threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) { + threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) { for (const int curve_i : selection.slice(range)) { const Nurb &src_curve = *src_curves[curve_i]; const Span<BPoint> src_points(src_curve.bp, src_curve.pntsu); @@ -143,7 +142,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ MutableSpan<int8_t> handle_types_l = curves.handle_types_left_for_write(); MutableSpan<int8_t> handle_types_r = curves.handle_types_right_for_write(); - threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) { + threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) { for (const int curve_i : selection.slice(range)) { const Nurb &src_curve = *src_curves[curve_i]; const Span<BezTriple> src_points(src_curve.bezt, src_curve.pntsu); @@ -166,12 +165,12 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ }; auto create_nurbs = [&](IndexMask selection) { - threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) { - MutableSpan<int> resolutions = curves.resolution_for_write(); - MutableSpan<float> nurbs_weights = curves.nurbs_weights_for_write(); - MutableSpan<int8_t> nurbs_orders = curves.nurbs_orders_for_write(); - MutableSpan<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes_for_write(); - + MutableSpan<int> resolutions = curves.resolution_for_write(); + MutableSpan<float> nurbs_weights = curves.nurbs_weights_for_write(); + MutableSpan<int8_t> nurbs_orders = curves.nurbs_orders_for_write(); + MutableSpan<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes_for_write(); + + threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) { for (const int curve_i : selection.slice(range)) { const Nurb &src_curve = *src_curves[curve_i]; const Span src_points(src_curve.bp, src_curve.pntsu); @@ -196,14 +195,14 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ curves.curve_types(), curves.curve_type_counts(), curves.curves_range(), - [&](IndexMask /* selection */) {}, + [&](IndexMask /*selection*/) { BLI_assert_unreachable(); }, create_poly, create_bezier, create_nurbs); curves.normal_mode_for_write().fill(normal_mode_from_legacy(curve_legacy.twist_mode)); - radius_attribute.save(); + radius_attribute.finish(); return curves_id; } diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index cd6b64e9a03..3ab6fb01ea5 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -38,7 +38,7 @@ int calculate_evaluated_num(const int points_num, if (!check_valid_num_and_order(points_num, order, cyclic, knots_mode)) { return points_num; } - return resolution * curve_segment_num(points_num, cyclic); + return resolution * segments_num(points_num, cyclic); } int knots_num(const int points_num, const int8_t order, const bool cyclic) @@ -168,7 +168,7 @@ void calculate_basis_cache(const int points_num, MutableSpan<int> basis_start_indices(basis_cache.start_indices); const int last_control_point_index = cyclic ? points_num + degree : points_num; - const int evaluated_segment_num = curve_segment_num(evaluated_num, cyclic); + const int evaluated_segment_num = segments_num(evaluated_num, cyclic); const float start = knots[degree]; const float end = knots[last_control_point_index]; diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 58380a1a35f..7f051b683b4 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -8,7 +8,6 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" @@ -39,8 +38,8 @@ static void fill_mesh_topology(const int vert_offset, MutableSpan<MLoop> loops, MutableSpan<MPoly> polys) { - const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); - const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); + const int main_segment_num = curves::segments_num(main_point_num, main_cyclic); + const int profile_segment_num = curves::segments_num(profile_point_num, profile_cyclic); if (profile_point_num == 1) { for (const int i : IndexRange(main_point_num - 1)) { @@ -228,8 +227,8 @@ struct CurvesInfo { const CurvesGeometry &profile; /* Make sure these are spans because they are potentially accessed many times. */ - VArray_Span<bool> main_cyclic; - VArray_Span<bool> profile_cyclic; + VArraySpan<bool> main_cyclic; + VArraySpan<bool> profile_cyclic; }; static CurvesInfo get_curves_info(const CurvesGeometry &main, const CurvesGeometry &profile) { @@ -273,7 +272,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool for (const int i_main : info.main.curves_range()) { const bool main_cyclic = info.main_cyclic[i_main]; const int main_point_num = info.main.evaluated_points_for_curve(i_main).size(); - const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); + const int main_segment_num = curves::segments_num(main_point_num, main_cyclic); for (const int i_profile : info.profile.curves_range()) { result.vert[mesh_index] = vert_offset; result.edge[mesh_index] = edge_offset; @@ -285,7 +284,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool const bool profile_cyclic = info.profile_cyclic[i_profile]; const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size(); - const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); + const int profile_segment_num = curves::segments_num(profile_point_num, profile_cyclic); const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; const int tube_face_num = main_segment_num * profile_segment_num; @@ -315,15 +314,15 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool return result; } -static eAttrDomain get_attribute_domain_for_mesh(const MeshComponent &mesh, +static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_attributes, const AttributeIDRef &attribute_id) { /* Only use a different domain if it is builtin and must only exist on one domain. */ - if (!mesh.attribute_is_builtin(attribute_id)) { + if (!mesh_attributes.is_builtin(attribute_id)) { return ATTR_DOMAIN_POINT; } - std::optional<AttributeMetaData> meta_data = mesh.attribute_get_meta_data(attribute_id); + std::optional<AttributeMetaData> meta_data = mesh_attributes.lookup_meta_data(attribute_id); if (!meta_data) { return ATTR_DOMAIN_POINT; } @@ -331,16 +330,17 @@ static eAttrDomain get_attribute_domain_for_mesh(const MeshComponent &mesh, return meta_data->domain; } -static bool should_add_attribute_to_mesh(const CurveComponent &curve_component, - const MeshComponent &mesh_component, +static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes, + const AttributeAccessor &mesh_attributes, const AttributeIDRef &id) { + /* The position attribute has special non-generic evaluation. */ if (id.is_named() && id.name() == "position") { return false; } /* Don't propagate built-in curves attributes that are not built-in on meshes. */ - if (curve_component.attribute_is_builtin(id) && !mesh_component.attribute_is_builtin(id)) { + if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) { return false; } if (!id.should_be_kept()) { @@ -407,8 +407,8 @@ static void foreach_curve_combination(const CurvesInfo &info, profile_points, main_cyclic, profile_cyclic, - curves::curve_segment_num(main_points.size(), main_cyclic), - curves::curve_segment_num(profile_points.size(), profile_cyclic), + curves::segments_num(main_points.size(), main_cyclic), + curves::segments_num(profile_points.size(), profile_cyclic), offsets_to_range(offsets.vert.as_span(), i), offsets_to_range(offsets.edge.as_span(), i), offsets_to_range(offsets.poly.as_span(), i), @@ -667,20 +667,13 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, Vector<std::byte> eval_buffer; - Curves main_id = {{nullptr}}; - main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main); - CurveComponent main_component; - main_component.replace(&main_id, GeometryOwnershipType::Editable); - - Curves profile_id = {{nullptr}}; - profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile); - CurveComponent profile_component; - profile_component.replace(&profile_id, GeometryOwnershipType::Editable); + const AttributeAccessor main_attributes = main.attributes(); + const AttributeAccessor profile_attributes = profile.attributes(); Span<float> radii = {}; - if (main_component.attribute_exists("radius")) { + if (main_attributes.contains("radius")) { radii = evaluated_attribute_if_necessary( - main_component.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 1.0f), + main_attributes.lookup_or_default<float>("radius", ATTR_DOMAIN_POINT, 1.0f), main, main.curve_type_counts(), eval_buffer) @@ -700,8 +693,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, if (profile.curve_type_counts()[CURVE_TYPE_BEZIER] > 0) { const VArray<int8_t> curve_types = profile.curve_types(); - const VArray_Span<int8_t> handle_types_left{profile.handle_types_left()}; - const VArray_Span<int8_t> handle_types_right{profile.handle_types_right()}; + const VArraySpan<int8_t> handle_types_left{profile.handle_types_left()}; + const VArraySpan<int8_t> handle_types_right{profile.handle_types_right()}; foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { if (curve_types[info.i_profile] == CURVE_TYPE_BEZIER) { @@ -716,24 +709,23 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, }); } - Set<AttributeIDRef> main_attributes; + Set<AttributeIDRef> main_attributes_set; - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); + MutableAttributeAccessor mesh_attributes = bke::mesh_attributes_for_write(*mesh); - main_component.attribute_foreach([&](const AttributeIDRef &id, - const AttributeMetaData meta_data) { - if (!should_add_attribute_to_mesh(main_component, mesh_component, id)) { + main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id)) { return true; } - main_attributes.add_new(id); + main_attributes_set.add_new(id); const eAttrDomain src_domain = meta_data.domain; const eCustomDataType type = meta_data.data_type; - GVArray src = main_component.attribute_try_get_for_read(id, src_domain, type); + GVArray src = main_attributes.lookup(id, src_domain, type); - const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id); - OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type); + const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, id); + GSpanAttributeWriter dst = mesh_attributes.lookup_or_add_for_write_only_span( + id, dst_domain, type); if (!dst) { return true; } @@ -744,31 +736,31 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, offsets, dst_domain, evaluated_attribute_if_necessary(src, main, main.curve_type_counts(), eval_buffer), - dst.as_span()); + dst.span); } else if (src_domain == ATTR_DOMAIN_CURVE) { copy_curve_domain_attribute_to_mesh( - offsets, offsets.main_indices, dst_domain, src, dst.as_span()); + offsets, offsets.main_indices, dst_domain, src, dst.span); } - dst.save(); + dst.finish(); return true; }); - profile_component.attribute_foreach([&](const AttributeIDRef &id, - const AttributeMetaData meta_data) { + profile_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (main_attributes.contains(id)) { return true; } - if (!should_add_attribute_to_mesh(profile_component, mesh_component, id)) { + if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id)) { return true; } const eAttrDomain src_domain = meta_data.domain; const eCustomDataType type = meta_data.data_type; - GVArray src = profile_component.attribute_try_get_for_read(id, src_domain, type); + GVArray src = profile_attributes.lookup(id, src_domain, type); - const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id); - OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type); + const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, id); + GSpanAttributeWriter dst = mesh_attributes.lookup_or_add_for_write_only_span( + id, dst_domain, type); if (!dst) { return true; } @@ -779,14 +771,14 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, offsets, dst_domain, evaluated_attribute_if_necessary(src, profile, profile.curve_type_counts(), eval_buffer), - dst.as_span()); + dst.span); } else if (src_domain == ATTR_DOMAIN_CURVE) { copy_curve_domain_attribute_to_mesh( - offsets, offsets.profile_indices, dst_domain, src, dst.as_span()); + offsets, offsets.profile_indices, dst_domain, src, dst.span); } - dst.save(); + dst.finish(); return true; }); diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 7ad83263b73..6554f42d3dd 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -53,8 +53,6 @@ using blender::Vector; static const char *ATTR_POSITION = "position"; -static void update_custom_data_pointers(Curves &curves); - static void curves_init_data(ID *id) { Curves *curves = (Curves *)id; @@ -97,8 +95,6 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, dst.runtime->type_counts = src.runtime->type_counts; - dst.update_customdata_pointers(); - curves_dst->batch_cache = nullptr; } @@ -170,7 +166,6 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); - update_custom_data_pointers(*curves); BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); @@ -233,11 +228,6 @@ IDTypeInfo IDType_ID_CV = { /*lib_override_apply_post */ nullptr, }; -static void update_custom_data_pointers(Curves &curves) -{ - blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers(); -} - void *BKE_curves_add(Main *bmain, const char *name) { Curves *curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, name)); @@ -329,6 +319,14 @@ void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob Curves *curves = static_cast<Curves *>(object->data); GeometrySet geometry_set = GeometrySet::create_with_curves(curves, GeometryOwnershipType::ReadOnly); + if (object->mode == OB_MODE_SCULPT_CURVES) { + /* Try to propagate deformation data through modifier evaluation, so that sculpt mode can work + * on evaluated curves. */ + GeometryComponentEditData &edit_component = + geometry_set.get_component_for_write<GeometryComponentEditData>(); + edit_component.curves_edit_hints_ = std::make_unique<blender::bke::CurvesEditHints>( + *static_cast<const Curves *>(DEG_get_original_object(object)->data)); + } curves_evaluate_modifiers(depsgraph, scene, object, geometry_set); /* Assign evaluated object. */ @@ -388,4 +386,51 @@ Curves *curves_new_nomain(CurvesGeometry curves) return curves_id; } +void curves_copy_parameters(const Curves &src, Curves &dst) +{ + dst.flag = src.flag; + dst.attributes_active_index = src.attributes_active_index; + MEM_SAFE_FREE(dst.mat); + dst.mat = static_cast<Material **>(MEM_malloc_arrayN(src.totcol, sizeof(Material *), __func__)); + dst.totcol = src.totcol; + MutableSpan(dst.mat, dst.totcol).copy_from(Span(src.mat, src.totcol)); + dst.symmetry = src.symmetry; + dst.selection_domain = src.selection_domain; + dst.surface = src.surface; + MEM_SAFE_FREE(dst.surface_uv_map); + if (src.surface_uv_map != nullptr) { + dst.surface_uv_map = BLI_strdup(src.surface_uv_map); + } +} + +CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob) +{ + this->curves_to_world = curves_ob.obmat; + this->world_to_curves = this->curves_to_world.inverted(); + + if (surface_ob != nullptr) { + this->surface_to_world = surface_ob->obmat; + this->world_to_surface = this->surface_to_world.inverted(); + this->surface_to_curves = this->world_to_curves * this->surface_to_world; + this->curves_to_surface = this->world_to_surface * this->curves_to_world; + this->surface_to_curves_normal = this->surface_to_curves.inverted().transposed(); + } +} + +bool CurvesEditHints::is_valid() const +{ + const int point_num = this->curves_id_orig.geometry.point_num; + if (this->positions.has_value()) { + if (this->positions->size() != point_num) { + return false; + } + } + if (this->deform_mats.has_value()) { + if (this->deform_mats->size() != point_num) { + return false; + } + } + return true; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index b58781ce806..3050e01e528 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -62,9 +62,11 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) this->point_num, ATTR_POSITION.c_str()); - this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_num + 1, sizeof(int), __func__); - - this->update_customdata_pointers(); + this->curve_offsets = (int *)MEM_malloc_arrayN(this->curve_num + 1, sizeof(int), __func__); +#ifdef DEBUG + this->offsets_for_write().fill(-1); +#endif + this->offsets_for_write().first() = 0; this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); /* Fill the type counts with the default so they're in a valid state. */ @@ -84,15 +86,13 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num); MEM_SAFE_FREE(dst.curve_offsets); - dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_num + 1, sizeof(int), __func__); + dst.curve_offsets = (int *)MEM_malloc_arrayN(dst.point_num + 1, sizeof(int), __func__); dst.offsets_for_write().copy_from(src.offsets()); dst.tag_topology_changed(); /* Though type counts are a cache, they must be copied because they are calculated eagerly. */ dst.runtime->type_counts = src.runtime->type_counts; - - dst.update_customdata_pointers(); } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) @@ -126,9 +126,6 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) MEM_SAFE_FREE(src.curve_offsets); std::swap(dst.runtime, src.runtime); - - src.update_customdata_pointers(); - dst.update_customdata_pointers(); } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) @@ -302,13 +299,11 @@ void CurvesGeometry::update_curve_types() Span<float3> CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_num}; + return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } MutableSpan<float3> CurvesGeometry::positions_for_write() { - this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); - return {(float3 *)this->position, this->point_num}; + return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } Span<int> CurvesGeometry::offsets() const @@ -473,8 +468,8 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, VArray<int> resolution = curves.resolution(); VArray<bool> cyclic = curves.cyclic(); - VArray_Span<int8_t> handle_types_left{curves.handle_types_left()}; - VArray_Span<int8_t> handle_types_right{curves.handle_types_right()}; + VArraySpan<int8_t> handle_types_left{curves.handle_types_left()}; + VArraySpan<int8_t> handle_types_right{curves.handle_types_right()}; VArray<int8_t> nurbs_orders = curves.nurbs_orders(); VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes(); @@ -720,8 +715,9 @@ Span<float3> CurvesGeometry::evaluated_tangents() const } }); - /* Correct the first and last tangents of Bezier curves so that they align with the inner - * handles. This is a separate loop to avoid the cost when Bezier type curves are not used. */ + /* Correct the first and last tangents of non-cyclic Bezier curves so that they align with the + * inner handles. This is a separate loop to avoid the cost when Bezier type curves are not + * used. */ Vector<int64_t> bezier_indices; const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, bezier_indices); if (!bezier_mask.is_empty()) { @@ -731,6 +727,9 @@ Span<float3> CurvesGeometry::evaluated_tangents() const threading::parallel_for(bezier_mask.index_range(), 1024, [&](IndexRange range) { for (const int curve_index : bezier_mask.slice(range)) { + if (cyclic[curve_index]) { + continue; + } const IndexRange points = this->points_for_curve(curve_index); const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); @@ -957,7 +956,6 @@ void CurvesGeometry::resize(const int points_num, const int curves_num) this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); - this->update_customdata_pointers(); } void CurvesGeometry::tag_positions_changed() @@ -1009,8 +1007,8 @@ void CurvesGeometry::calculate_bezier_auto_handles() return; } const VArray<bool> cyclic = this->cyclic(); - const VArray_Span<int8_t> types_left{this->handle_types_left()}; - const VArray_Span<int8_t> types_right{this->handle_types_right()}; + const VArraySpan<int8_t> types_left{this->handle_types_left()}; + const VArraySpan<int8_t> types_right{this->handle_types_right()}; const Span<float3> positions = this->positions(); MutableSpan<float3> positions_left = this->handle_positions_left_for_write(); MutableSpan<float3> positions_right = this->handle_positions_right_for_write(); @@ -1056,10 +1054,11 @@ void CurvesGeometry::transform(const float4x4 &matrix) static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves) { - Span<float3> positions = curves.positions(); - if (curves.radius) { - Span<float> radii{curves.radius, curves.points_num()}; - return bounds::min_max_with_radii(positions, radii); + const Span<float3> positions = curves.positions(); + const VArray<float> radii = curves.attributes().lookup_or_default<float>( + ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f); + if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) { + return bounds::min_max_with_radii(positions, radii.get_internal_span()); } return bounds::min_max(positions); } @@ -1075,31 +1074,6 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const return true; } -void CurvesGeometry::update_customdata_pointers() -{ - this->position = (float(*)[3])CustomData_get_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str()); - this->radius = (float *)CustomData_get_layer_named( - &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str()); - this->curve_type = (int8_t *)CustomData_get_layer_named( - &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); -} - -static void *ensure_customdata_layer(CustomData &custom_data, - const StringRefNull name, - const eCustomDataType data_type, - const int tot_elements) -{ - for (const int other_layer_i : IndexRange(custom_data.totlayer)) { - CustomDataLayer &new_layer = custom_data.layers[other_layer_i]; - if (name == StringRef(new_layer.name)) { - return new_layer.data; - } - } - return CustomData_add_layer_named( - &custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str()); -} - static void copy_between_buffers(const CPPType &type, const void *src_buffer, void *dst_buffer, @@ -1193,58 +1167,38 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, threading::parallel_invoke( /* Initialize curve offsets. */ [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); }, - /* Copy over point attributes. */ [&]() { - const CustomData &old_point_data = curves.point_data; - CustomData &new_point_data = new_curves.point_data; - for (const int layer_i : IndexRange(old_point_data.totlayer)) { - const CustomDataLayer &old_layer = old_point_data.layers[layer_i]; - const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - void *dst_buffer = ensure_customdata_layer( - new_point_data, old_layer.name, data_type, new_point_count); - - threading::parallel_for( - copy_point_ranges.index_range(), 128, [&](const IndexRange ranges_range) { - for (const int range_i : ranges_range) { - const IndexRange src_range = copy_point_ranges[range_i]; - copy_between_buffers(type, - old_layer.data, - dst_buffer, - src_range, - {copy_point_range_dst_offsets[range_i], src_range.size()}); - } - }); + /* Copy over point attributes. */ + for (auto &attribute : bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) { + for (const int range_i : range) { + const IndexRange src_range = copy_point_ranges[range_i]; + copy_between_buffers(attribute.src.type(), + attribute.src.data(), + attribute.dst.span.data(), + src_range, + {copy_point_range_dst_offsets[range_i], src_range.size()}); + } + }); + attribute.dst.finish(); } - }, - /* Copy over curve attributes. - * In some cases points are just dissolved, so the the number of - * curves will be the same. That could be optimized in the future. */ - [&]() { - const CustomData &old_curve_data = curves.curve_data; - CustomData &new_curve_data = new_curves.curve_data; - for (const int layer_i : IndexRange(old_curve_data.totlayer)) { - const CustomDataLayer &old_layer = old_curve_data.layers[layer_i]; - const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - void *dst_buffer = ensure_customdata_layer( - new_curve_data, old_layer.name, data_type, new_curve_count); + /* Copy over curve attributes. + * In some cases points are just dissolved, so the the number of + * curves will be the same. That could be optimized in the future. */ + for (auto &attribute : bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { if (new_curves.curves_num() == curves.curves_num()) { - type.copy_construct_n(old_layer.data, dst_buffer, new_curves.curves_num()); + attribute.dst.span.copy_from(attribute.src); } else { - copy_with_map({type, old_layer.data, curves.curves_num()}, - new_curve_orig_indices, - {type, dst_buffer, new_curves.curves_num()}); + copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span); } + attribute.dst.finish(); } }); - new_curves.update_curve_types(); - return new_curves; } @@ -1309,57 +1263,37 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } }); }, - /* Copy over point attributes. */ [&]() { - const CustomData &old_point_data = curves.point_data; - CustomData &new_point_data = new_curves.point_data; - for (const int layer_i : IndexRange(old_point_data.totlayer)) { - const CustomDataLayer &old_layer = old_point_data.layers[layer_i]; - const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - void *dst_buffer = ensure_customdata_layer( - new_point_data, old_layer.name, data_type, new_tot_points); - - threading::parallel_for( - old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { - for (const int range_i : ranges_range) { - copy_between_buffers(type, - old_layer.data, - dst_buffer, - old_point_ranges[range_i], - new_point_ranges[range_i]); - } - }); + /* Copy over point attributes. */ + for (auto &attribute : bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { + for (const int range_i : range) { + copy_between_buffers(attribute.src.type(), + attribute.src.data(), + attribute.dst.span.data(), + old_point_ranges[range_i], + new_point_ranges[range_i]); + } + }); + attribute.dst.finish(); } - }, - /* Copy over curve attributes. */ - [&]() { - const CustomData &old_curve_data = curves.curve_data; - CustomData &new_curve_data = new_curves.curve_data; - for (const int layer_i : IndexRange(old_curve_data.totlayer)) { - const CustomDataLayer &old_layer = old_curve_data.layers[layer_i]; - const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - void *dst_buffer = ensure_customdata_layer( - new_curve_data, old_layer.name, data_type, new_tot_curves); - - threading::parallel_for( - old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { - for (const int range_i : ranges_range) { - copy_between_buffers(type, - old_layer.data, - dst_buffer, - old_curve_ranges[range_i], - new_curve_ranges[range_i]); - } - }); + /* Copy over curve attributes. */ + for (auto &attribute : bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { + for (const int range_i : range) { + copy_between_buffers(attribute.src.type(), + attribute.src.data(), + attribute.dst.span.data(), + old_curve_ranges[range_i], + new_curve_ranges[range_i]); + } + }); + attribute.dst.finish(); } }); - new_curves.update_curve_types(); - return new_curves; } @@ -1493,7 +1427,6 @@ void CurvesGeometry::remove_attributes_based_on_types() if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) { CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num); } - this->update_customdata_pointers(); } /** \} */ diff --git a/source/blender/blenkernel/intern/curves_geometry_test.cc b/source/blender/blenkernel/intern/curves_geometry_test.cc index 48493743cfc..2c87fc539fe 100644 --- a/source/blender/blenkernel/intern/curves_geometry_test.cc +++ b/source/blender/blenkernel/intern/curves_geometry_test.cc @@ -16,7 +16,7 @@ static CurvesGeometry create_basic_curves(const int points_size, const int curve const int curve_length = points_size / curves_size; for (const int i : curves.curves_range()) { - curves.offsets_for_write()[i] = points_size * curve_length; + curves.offsets_for_write()[i] = curve_length * i; } curves.offsets_for_write().last() = points_size; diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc index 802469399ab..d98832e796c 100644 --- a/source/blender/blenkernel/intern/curves_utils.cc +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -84,6 +84,18 @@ void fill_points(const CurvesGeometry &curves, }); } +bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves) +{ + bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + dst_curves.runtime->type_counts = src_curves.runtime->type_counts; + return dst_curves; +} + IndexMask indices_for_type(const VArray<int8_t> &types, const std::array<int, CURVE_TYPES_NUM> &type_counts, const CurveType type, @@ -109,14 +121,18 @@ void foreach_curve_by_type(const VArray<int8_t> &types, FunctionRef<void(IndexMask)> bezier_fn, FunctionRef<void(IndexMask)> nurbs_fn) { - Vector<int64_t> catmull_rom; - Vector<int64_t> poly; - Vector<int64_t> bezier; - Vector<int64_t> nurbs; - catmull_rom_fn(indices_for_type(types, counts, CURVE_TYPE_CATMULL_ROM, selection, catmull_rom)); - poly_fn(indices_for_type(types, counts, CURVE_TYPE_POLY, selection, poly)); - bezier_fn(indices_for_type(types, counts, CURVE_TYPE_BEZIER, selection, bezier)); - nurbs_fn(indices_for_type(types, counts, CURVE_TYPE_NURBS, selection, nurbs)); + Vector<int64_t> indices; + auto call_if_not_empty = [&](const CurveType type, FunctionRef<void(IndexMask)> fn) { + indices.clear(); + const IndexMask mask = indices_for_type(types, counts, type, selection, indices); + if (!mask.is_empty()) { + fn(mask); + } + }; + call_if_not_empty(CURVE_TYPE_CATMULL_ROM, catmull_rom_fn); + call_if_not_empty(CURVE_TYPE_POLY, poly_fn); + call_if_not_empty(CURVE_TYPE_BEZIER, bezier_fn); + call_if_not_empty(CURVE_TYPE_NURBS, nurbs_fn); } } // namespace blender::bke::curves diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index bb5b2ee0836..b12eafa9cef 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2329,6 +2329,7 @@ bool CustomData_merge(const CustomData *source, void CustomData_realloc(CustomData *data, int totelem) { + BLI_assert(totelem >= 0); for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; const LayerTypeInfo *typeInfo; @@ -3516,97 +3517,6 @@ void CustomData_set(const CustomData *data, int index, int type, const void *sou /* BMesh functions */ -void CustomData_to_bmeshpoly(CustomData *fdata, CustomData *ldata, int totloop) -{ - for (int i = 0; i < fdata->totlayer; i++) { - if (fdata->layers[i].type == CD_MTFACE) { - CustomData_add_layer_named( - ldata, CD_MLOOPUV, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); - } - else if (fdata->layers[i].type == CD_MCOL) { - CustomData_add_layer_named( - ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); - } - else if (fdata->layers[i].type == CD_MDISPS) { - CustomData_add_layer_named( - ldata, CD_MDISPS, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); - } - else if (fdata->layers[i].type == CD_TESSLOOPNORMAL) { - CustomData_add_layer_named( - ldata, CD_NORMAL, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); - } - } -} - -void CustomData_from_bmeshpoly(CustomData *fdata, CustomData *ldata, int total) -{ - /* avoid accumulating extra layers */ - BLI_assert(!CustomData_from_bmeshpoly_test(fdata, ldata, false)); - - for (int i = 0; i < ldata->totlayer; i++) { - if (ldata->layers[i].type == CD_MLOOPUV) { - CustomData_add_layer_named( - fdata, CD_MTFACE, CD_CALLOC, nullptr, total, ldata->layers[i].name); - } - if (ldata->layers[i].type == CD_PROP_BYTE_COLOR) { - CustomData_add_layer_named(fdata, CD_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name); - } - else if (ldata->layers[i].type == CD_PREVIEW_MLOOPCOL) { - CustomData_add_layer_named( - fdata, CD_PREVIEW_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name); - } - else if (ldata->layers[i].type == CD_ORIGSPACE_MLOOP) { - CustomData_add_layer_named( - fdata, CD_ORIGSPACE, CD_CALLOC, nullptr, total, ldata->layers[i].name); - } - else if (ldata->layers[i].type == CD_NORMAL) { - CustomData_add_layer_named( - fdata, CD_TESSLOOPNORMAL, CD_CALLOC, nullptr, total, ldata->layers[i].name); - } - else if (ldata->layers[i].type == CD_TANGENT) { - CustomData_add_layer_named( - fdata, CD_TANGENT, CD_CALLOC, nullptr, total, ldata->layers[i].name); - } - } - - CustomData_bmesh_update_active_layers(fdata, ldata); -} - -#ifndef NDEBUG -bool CustomData_from_bmeshpoly_test(CustomData *fdata, CustomData *ldata, bool fallback) -{ - int a_num = 0, b_num = 0; -# define LAYER_CMP(l_a, t_a, l_b, t_b) \ - ((a_num += CustomData_number_of_layers(l_a, t_a)) == \ - (b_num += CustomData_number_of_layers(l_b, t_b))) - - if (!LAYER_CMP(ldata, CD_MLOOPUV, fdata, CD_MTFACE)) { - return false; - } - if (!LAYER_CMP(ldata, CD_PROP_BYTE_COLOR, fdata, CD_MCOL)) { - return false; - } - if (!LAYER_CMP(ldata, CD_PREVIEW_MLOOPCOL, fdata, CD_PREVIEW_MCOL)) { - return false; - } - if (!LAYER_CMP(ldata, CD_ORIGSPACE_MLOOP, fdata, CD_ORIGSPACE)) { - return false; - } - if (!LAYER_CMP(ldata, CD_NORMAL, fdata, CD_TESSLOOPNORMAL)) { - return false; - } - if (!LAYER_CMP(ldata, CD_TANGENT, fdata, CD_TANGENT)) { - return false; - } - -# undef LAYER_CMP - - /* if no layers are on either CustomData's, - * then there was nothing to do... */ - return a_num ? true : fallback; -} -#endif - void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) { int act; @@ -3640,39 +3550,6 @@ void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) } } -void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, CustomData *ldata) -{ - int act; - - if (CustomData_has_layer(fdata, CD_MTFACE)) { - act = CustomData_get_active_layer(fdata, CD_MTFACE); - CustomData_set_layer_active(ldata, CD_MLOOPUV, act); - - act = CustomData_get_render_layer(fdata, CD_MTFACE); - CustomData_set_layer_render(ldata, CD_MLOOPUV, act); - - act = CustomData_get_clone_layer(fdata, CD_MTFACE); - CustomData_set_layer_clone(ldata, CD_MLOOPUV, act); - - act = CustomData_get_stencil_layer(fdata, CD_MTFACE); - CustomData_set_layer_stencil(ldata, CD_MLOOPUV, act); - } - - if (CustomData_has_layer(fdata, CD_MCOL)) { - act = CustomData_get_active_layer(fdata, CD_MCOL); - CustomData_set_layer_active(ldata, CD_PROP_BYTE_COLOR, act); - - act = CustomData_get_render_layer(fdata, CD_MCOL); - CustomData_set_layer_render(ldata, CD_PROP_BYTE_COLOR, act); - - act = CustomData_get_clone_layer(fdata, CD_MCOL); - CustomData_set_layer_clone(ldata, CD_PROP_BYTE_COLOR, act); - - act = CustomData_get_stencil_layer(fdata, CD_MCOL); - CustomData_set_layer_stencil(ldata, CD_PROP_BYTE_COLOR, act); - } -} - void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) { int chunksize; @@ -4566,9 +4443,52 @@ bool CustomData_verify_versions(CustomData *data, int index) return keeplayer; } +static bool CustomData_layer_ensure_data_exists(CustomDataLayer *layer, size_t count) +{ + BLI_assert(layer); + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + BLI_assert(typeInfo); + + if (layer->data || count == 0) { + return false; + } + + switch (layer->type) { + /* When more instances of corrupt files are found, add them here. */ + case CD_PROP_BOOL: /* See T84935. */ + case CD_MLOOPUV: /* See T90620. */ + layer->data = MEM_calloc_arrayN(count, typeInfo->size, layerType_getName(layer->type)); + BLI_assert(layer->data); + if (typeInfo->set_default) { + typeInfo->set_default(layer->data, count); + } + return true; + break; + + case CD_MTEXPOLY: + /* TODO: Investigate multiple test failures on cycles, e.g. cycles_shadow_catcher_cpu. */ + break; + + default: + /* Log an error so we can collect instances of bad files. */ + CLOG_WARN(&LOG, "CustomDataLayer->data is NULL for type %d.", layer->type); + break; + } + return false; +} + bool CustomData_layer_validate(CustomDataLayer *layer, const uint totitems, const bool do_fixes) { + BLI_assert(layer); const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + BLI_assert(typeInfo); + + if (do_fixes) { + CustomData_layer_ensure_data_exists(layer, totitems); + } + + BLI_assert((totitems == 0) || layer->data); + BLI_assert(MEM_allocN_len(layer->data) >= totitems * typeInfo->size); if (typeInfo->validate != nullptr) { return typeInfo->validate(layer->data, totitems, do_fixes); @@ -5329,16 +5249,15 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count) if (CustomData_verify_versions(data, i)) { BLO_read_data_address(reader, &layer->data); - if (layer->data == nullptr && count > 0 && layer->type == CD_PROP_BOOL) { - /* Usually this should never happen, except when a custom data layer has not been written - * to a file correctly. */ - CLOG_WARN(&LOG, "Reallocating custom data layer that was not saved correctly."); - const LayerTypeInfo *info = layerType_getInfo(layer->type); - layer->data = MEM_calloc_arrayN((size_t)count, info->size, layerType_getName(layer->type)); - if (info->set_default) { - info->set_default(layer->data, count); - } + if (CustomData_layer_ensure_data_exists(layer, count)) { + /* Under normal operations, this shouldn't happen, but... + * For a CD_PROP_BOOL example, see T84935. + * For a CD_MLOOPUV example, see T90620. */ + CLOG_WARN(&LOG, + "Allocated custom data layer that was not saved correctly for layer->type = %d.", + layer->type); } + if (layer->type == CD_MDISPS) { blend_read_mdisps( reader, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL); diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 196a6a00ade..17a74b5564a 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -1410,7 +1410,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, BKE_mesh_remap_calc_source_cddata_masks_from_map_modes( map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, &me_src_mask); if (is_modifier) { - me_src = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_src, false); + me_src = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_src); if (me_src == NULL || !CustomData_MeshMasks_are_matching(&ob_src->runtime.last_data_mask, &me_src_mask)) { diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index a4262e08e39..423e76fce8c 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2552,7 +2552,7 @@ static void dynamic_paint_find_island_border(const DynamicPaintCreateUVSurfaceDa const int vert1 = mloop[loop_idx[(edge_idx + 1) % 3]].v; /* Use a pre-computed vert-to-looptri mapping, - * speeds up things a lot compared to looping over all loopti. */ + * speeds up things a lot compared to looping over all looptri. */ const MeshElemMap *map = &bdata->vert_to_looptri_map[vert0]; bool found_other = false; diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 0203620df84..972ff377519 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1146,7 +1146,7 @@ void fcurve_samples_to_keyframes(FCurve *fcu, const int start, const int end) MEM_SAFE_FREE(fcu->fpt); /* Not strictly needed since we use linear interpolation, but better be consistent here. */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* ***************************** F-Curve Sanity ********************************* */ @@ -1216,7 +1216,7 @@ static BezTriple *cycle_offset_triple( return out; } -void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) +void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) { BezTriple *bezt, *prev, *next; int a = fcu->totvert; @@ -1299,9 +1299,9 @@ void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) } } -void calchandles_fcurve(FCurve *fcu) +void BKE_fcurve_handles_recalc(FCurve *fcu) { - calchandles_fcurve_ex(fcu, SELECT); + BKE_fcurve_handles_recalc_ex(fcu, SELECT); } void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) @@ -1320,7 +1320,7 @@ void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_ha } /* Recalculate handles. */ - calchandles_fcurve_ex(fcu, sel_flag); + BKE_fcurve_handles_recalc_ex(fcu, sel_flag); } void sort_time_fcurve(FCurve *fcu) @@ -1590,6 +1590,12 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) } } +static void fcurve_bezt_free(FCurve *fcu) +{ + MEM_SAFE_FREE(fcu->bezt); + fcu->totvert = 0; +} + bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, @@ -1651,6 +1657,69 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, return true; } +void BKE_fcurve_delete_key(FCurve *fcu, int index) +{ + /* sanity check */ + if (fcu == NULL) { + return; + } + + /* verify the index: + * 1) cannot be greater than the number of available keyframes + * 2) negative indices are for specifying a value from the end of the array + */ + if (abs(index) >= fcu->totvert) { + return; + } + if (index < 0) { + index += fcu->totvert; + } + + /* Delete this keyframe */ + memmove( + &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); + fcu->totvert--; + + /* Free the array of BezTriples if there are not keyframes */ + if (fcu->totvert == 0) { + fcurve_bezt_free(fcu); + } +} + +bool BKE_fcurve_delete_keys_selected(FCurve *fcu) +{ + bool changed = false; + + if (fcu->bezt == NULL) { /* ignore baked curves */ + return false; + } + + /* Delete selected BezTriples */ + for (int i = 0; i < fcu->totvert; i++) { + if (fcu->bezt[i].f2 & SELECT) { + if (i == fcu->active_keyframe_index) { + BKE_fcurve_active_keyframe_set(fcu, NULL); + } + memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); + fcu->totvert--; + i--; + changed = true; + } + } + + /* Free the array of BezTriples if there are not keyframes */ + if (fcu->totvert == 0) { + fcurve_bezt_free(fcu); + } + + return changed; +} + +void BKE_fcurve_delete_keys_all(FCurve *fcu) +{ + fcurve_bezt_free(fcu); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 8e95bf18c6b..0fc09803088 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1038,8 +1038,6 @@ static void obstacles_from_mesh(Object *coll_ob, /* Transform mesh vertices to domain grid space for fast lookups. * This is valid because the mesh is copied above. */ - BKE_mesh_vertex_normals_ensure(me); - float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(me); for (i = 0; i < numverts; i++) { float co[3]; @@ -1047,11 +1045,6 @@ static void obstacles_from_mesh(Object *coll_ob, mul_m4_v3(coll_ob->obmat, mvert[i].co); manta_pos_to_cell(fds, mvert[i].co); - /* Vertex normal. */ - mul_mat3_m4_v3(coll_ob->obmat, vert_normals[i]); - mul_mat3_m4_v3(fds->imat, vert_normals[i]); - normalize_v3(vert_normals[i]); - /* Vertex velocity. */ add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift); if (has_velocity) { diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 1bb7c49d616..e4c7572b9e4 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -1127,7 +1127,7 @@ FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu) /* update the fcurve if the Cycles modifier is added */ if ((owner_fcu) && (type == FMODIFIER_TYPE_CYCLES)) { - calchandles_fcurve(owner_fcu); + BKE_fcurve_handles_recalc(owner_fcu); } /* return modifier for further editing */ @@ -1215,7 +1215,7 @@ bool remove_fmodifier(ListBase *modifiers, FModifier *fcm) /* update the fcurve if the Cycles modifier is removed */ if (update_fcu) { - calchandles_fcurve(update_fcu); + BKE_fcurve_handles_recalc(update_fcu); } return true; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 898869c3c44..22f105af0f1 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -5,7 +5,6 @@ #include "DNA_ID_enums.h" #include "DNA_curve_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curve.h" #include "BKE_geometry_set.hh" @@ -17,7 +16,7 @@ using blender::GMutableSpan; using blender::GSpan; using blender::GVArray; -using blender::GVArray_GSpan; +using blender::GVArraySpan; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -114,24 +113,6 @@ void CurveComponentLegacy::ensure_owns_direct_data() /** \name Attribute Access Helper Functions * \{ */ -int CurveComponentLegacy::attribute_domain_num(const eAttrDomain domain) const -{ - if (curve_ == nullptr) { - return 0; - } - if (domain == ATTR_DOMAIN_POINT) { - int total = 0; - for (const SplinePtr &spline : curve_->splines()) { - total += spline->size(); - } - return total; - } - if (domain == ATTR_DOMAIN_CURVE) { - return curve_->splines().size(); - } - return 0; -} - namespace blender::bke { namespace { @@ -231,7 +212,7 @@ 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. */ - VArray_Span<T> original_data_; + VArraySpan<T> original_data_; Array<int> offsets_; public: @@ -308,9 +289,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra } // namespace blender::bke -GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(const GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const +static GVArray adapt_curve_attribute_domain(const CurveEval &curve, + const GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { if (!varray) { return {}; @@ -323,30 +305,15 @@ GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(const GVArray &var } if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) { - return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray)); + 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 blender::bke::adapt_curve_domain_spline_to_point(curve, std::move(varray)); } return {}; } -static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(component); - return curve_component.get_for_write(); -} - -static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>( - component); - return curve_component.get_for_read(); -} - /** \} */ namespace blender::bke { @@ -380,41 +347,41 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const final + GVArray try_get_for_read(const void *owner) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return {}; } return as_read_attribute_(*curve); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + GAttributeWriter try_get_for_write(void *owner) const final { if (writable_ != Writable) { return {}; } - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } return {as_write_attribute_(*curve), domain_}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0; + const CurveEval *curve = static_cast<const CurveEval *>(owner); + return !curve->splines().is_empty(); } }; @@ -600,12 +567,11 @@ static GVArray varray_from_initializer(const AttributeInit &initializer, return {}; } -static bool create_point_attribute(GeometryComponent &component, +static bool create_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id, const AttributeInit &initializer, const eCustomDataType data_type) { - CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr || curve->splines().size() == 0) { return false; } @@ -638,15 +604,16 @@ static bool create_point_attribute(GeometryComponent &component, return true; } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); + 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. */ - GVArray_GSpan source_varray_span{source_varray}; - write_attribute.varray.set_all(source_varray_span.data()); + 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 AttributeInitMove &>(initializer).data); @@ -655,10 +622,8 @@ static bool create_point_attribute(GeometryComponent &component, return true; } -static bool remove_point_attribute(GeometryComponent &component, - const AttributeIDRef &attribute_id) +static bool remove_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id) { - CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { return false; } @@ -934,14 +899,14 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu { } - GVArray try_get_for_read(const GeometryComponent &component) const override + GVArray try_get_for_read(const void *owner) const override { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return {}; } - if (!this->exists(component)) { + if (!this->exists(owner)) { return {}; } @@ -962,14 +927,14 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu return point_data_varray(spans, offsets); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override + GAttributeWriter try_get_for_write(void *owner) const override { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } - if (!this->exists(component)) { + if (!this->exists(owner)) { return {}; } @@ -998,25 +963,27 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu return {point_data_varray_mutable(spans, offsets), domain_, tag_modified_fn}; } - bool try_delete(GeometryComponent &component) const final + bool try_delete(void *owner) const final { if (deletable_ == DeletableEnum::NonDeletable) { return false; } - return remove_point_attribute(component, name_); + CurveEval *curve = static_cast<CurveEval *>(owner); + return remove_point_attribute(curve, name_); } - bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final + bool try_create(void *owner, const AttributeInit &initializer) const final { if (createable_ == CreatableEnum::NonCreatable) { return false; } - return create_point_attribute(component, name_, initializer, CD_PROP_INT32); + CurveEval *curve = static_cast<CurveEval *>(owner); + return create_point_attribute(curve, name_, initializer, CD_PROP_INT32); } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return false; } @@ -1067,9 +1034,9 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo { } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + GAttributeWriter try_get_for_write(void *owner) const final { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } @@ -1077,7 +1044,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 checking the spline type for every point. */ if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { - return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); + return BuiltinPointAttributeProvider<float3>::try_get_for_write(owner); } auto tag_modified_fn = [curve]() { @@ -1110,9 +1077,9 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const override + GVArray try_get_for_read(const void *owner) const override { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return {}; } @@ -1128,9 +1095,9 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { const_cast<CurveEval *>(curve)->splines(), std::move(offsets), is_right_); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override + GAttributeWriter try_get_for_write(void *owner) const override { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } @@ -1148,26 +1115,27 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { tag_modified_fn}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return false; } - return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && - component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0; + CurveComponentLegacy component; + component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly); + + return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && !curve->splines().is_empty(); } }; @@ -1190,10 +1158,10 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { CD_MASK_PROP_INT8; public: - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr || curve->splines().size() == 0) { return {}; } @@ -1228,7 +1196,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT}; } - ReadAttributeLookup attribute = {}; + GAttributeReader attribute = {}; Array<int> offsets = curve->control_point_offsets(); attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { using T = decltype(dummy); @@ -1246,10 +1214,9 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { } /* This function is almost the same as #try_get_for_read, but without const. */ - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr || curve->splines().size() == 0) { return {}; } @@ -1284,7 +1251,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT}; } - WriteAttributeLookup attribute = {}; + GAttributeWriter attribute = {}; Array<int> offsets = curve->control_point_offsets(); attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { using T = decltype(dummy); @@ -1298,12 +1265,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return attribute; } - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final { - return remove_point_attribute(component, attribute_id); + CurveEval *curve = static_cast<CurveEval *>(owner); + return remove_point_attribute(curve, attribute_id); } - bool try_create(GeometryComponent &component, + bool try_create(void *owner, const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, @@ -1313,13 +1281,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { if (domain != ATTR_DOMAIN_POINT) { return false; } - return create_point_attribute(component, attribute_id, initializer, data_type); + CurveEval *curve = static_cast<CurveEval *>(owner); + return create_point_attribute(curve, attribute_id, initializer, data_type); } - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr || curve->splines().size() == 0) { return false; } @@ -1371,14 +1339,18 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_cyclic_write_attribute); static CustomDataAccessInfo spline_custom_data_access = { - [](GeometryComponent &component) -> CustomData * { - CurveEval *curve = get_curve_from_component_for_write(component); + [](void *owner) -> CustomData * { + CurveEval *curve = static_cast<CurveEval *>(owner); return curve ? &curve->attributes.data : nullptr; }, - [](const GeometryComponent &component) -> const CustomData * { - const CurveEval *curve = get_curve_from_component_for_read(component); + [](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(); + }, nullptr}; static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE, @@ -1430,12 +1402,63 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() /** \} */ +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 -const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers() - const +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() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_curve(); - return &providers; + 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 af058534f68..2714c78e381 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -5,7 +5,6 @@ #include "DNA_ID_enums.h" #include "DNA_curve_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curve.h" #include "BKE_curves.hh" @@ -218,7 +217,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr const VArray<int8_t> types = curves.curve_types(); if (curves.is_single_type(CURVE_TYPE_POLY)) { - return component.attribute_try_adapt_domain<float3>( + return component.attributes()->adapt_domain<float3>( VArray<float3>::ForSpan(curves.evaluated_normals()), ATTR_DOMAIN_POINT, domain); } @@ -229,7 +228,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr } if (domain == ATTR_DOMAIN_CURVE) { - return component.attribute_try_adapt_domain<float3>( + return component.attributes()->adapt_domain<float3>( VArray<float3>::ForContainer(std::move(normals)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); } @@ -264,7 +263,7 @@ static VArray<float> construct_curve_length_gvarray(const CurveComponent &compon } if (domain == ATTR_DOMAIN_POINT) { - return component.attribute_try_adapt_domain<float>( + return component.attributes()->adapt_domain<float>( std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } @@ -307,75 +306,29 @@ bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_num(const eAttrDomain domain) const +static void tag_component_topology_changed(void *owner) { - if (curves_ == nullptr) { - return 0; - } - const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( - curves_->geometry); - if (domain == ATTR_DOMAIN_POINT) { - return curves.points_num(); - } - if (domain == ATTR_DOMAIN_CURVE) { - return curves.curves_num(); - } - return 0; -} - -GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const -{ - return blender::bke::CurvesGeometry::wrap(curves_->geometry) - .adapt_domain(varray, from_domain, to_domain); -} - -static Curves *get_curves_from_component_for_write(GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - CurveComponent &curve_component = static_cast<CurveComponent &>(component); - return curve_component.get_for_write(); -} - -static const Curves *get_curves_from_component_for_read(const GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return curve_component.get_for_read(); + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.tag_topology_changed(); } -static void tag_component_topology_changed(GeometryComponent &component) +static void tag_component_curve_types_changed(void *owner) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); - } + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.update_curve_types(); + curves.tag_topology_changed(); } -static void tag_component_curve_types_changed(GeometryComponent &component) +static void tag_component_positions_changed(void *owner) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).update_curve_types(); - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); - } + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.tag_positions_changed(); } -static void tag_component_positions_changed(GeometryComponent &component) +static void tag_component_normals_changed(void *owner) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed(); - } -} - -static void tag_component_normals_changed(GeometryComponent &component) -{ - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed(); - } + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.tag_normals_changed(); } /** \} */ @@ -393,35 +346,33 @@ namespace blender::bke { static ComponentAttributeProviders create_attribute_providers_for_curve() { static CustomDataAccessInfo curve_access = { - [](GeometryComponent &component) -> CustomData * { - Curves *curves = get_curves_from_component_for_write(component); - return curves ? &curves->geometry.curve_data : nullptr; + [](void *owner) -> CustomData * { + CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); + return &curves.curve_data; }, - [](const GeometryComponent &component) -> const CustomData * { - const Curves *curves = get_curves_from_component_for_read(component); - return curves ? &curves->geometry.curve_data : nullptr; + [](const void *owner) -> const CustomData * { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return &curves.curve_data; }, - [](GeometryComponent &component) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); - } - }}; + [](const void *owner) -> int { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return curves.curves_num(); + }, + [](void * /*owner*/) {}}; static CustomDataAccessInfo point_access = { - [](GeometryComponent &component) -> CustomData * { - Curves *curves = get_curves_from_component_for_write(component); - return curves ? &curves->geometry.point_data : nullptr; + [](void *owner) -> CustomData * { + CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); + return &curves.point_data; }, - [](const GeometryComponent &component) -> const CustomData * { - const Curves *curves = get_curves_from_component_for_read(component); - return curves ? &curves->geometry.point_data : nullptr; + [](const void *owner) -> const CustomData * { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return &curves.point_data; }, - [](GeometryComponent &component) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); - } - }}; + [](const void *owner) -> int { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return curves.points_num(); + }, + [](void * /*owner*/) {}}; static BuiltinCustomDataLayerProvider position("position", ATTR_DOMAIN_POINT, @@ -626,11 +577,68 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() /** \} */ +static AttributeAccessorFunctions get_curves_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) { + if (owner == nullptr) { + return 0; + } + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return curves.points_num(); + case ATTR_DOMAIN_CURVE: + return curves.curves_num(); + 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 CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return curves.adapt_domain(varray, from_domain, to_domain); + }; + return fn; +} + +static const AttributeAccessorFunctions &get_curves_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_curves_accessor_functions(); + return fn; +} + +AttributeAccessor CurvesGeometry::attributes() const +{ + return AttributeAccessor(this, get_curves_accessor_functions_ref()); +} + +MutableAttributeAccessor CurvesGeometry::attributes_for_write() +{ + return MutableAttributeAccessor(this, get_curves_accessor_functions_ref()); +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +std::optional<blender::bke::AttributeAccessor> CurveComponent::attributes() const +{ + return blender::bke::AttributeAccessor(curves_ ? &curves_->geometry : nullptr, + blender::bke::get_curves_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> CurveComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_curve(); - return &providers; + Curves *curves = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(curves ? &curves->geometry : nullptr, + blender::bke::get_curves_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_edit_data.cc b/source/blender/blenkernel/intern/geometry_component_edit_data.cc new file mode 100644 index 00000000000..2c00de3254f --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_component_edit_data.cc @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" + +using namespace blender; +using namespace blender::bke; + +GeometryComponentEditData::GeometryComponentEditData() : GeometryComponent(GEO_COMPONENT_TYPE_EDIT) +{ +} + +GeometryComponent *GeometryComponentEditData::copy() const +{ + GeometryComponentEditData *new_component = new GeometryComponentEditData(); + if (curves_edit_hints_) { + new_component->curves_edit_hints_ = std::make_unique<CurvesEditHints>(*curves_edit_hints_); + } + return new_component; +} + +bool GeometryComponentEditData::owns_direct_data() const +{ + return true; +} + +void GeometryComponentEditData::ensure_owns_direct_data() +{ + /* Nothing to do. */ +} + +void GeometryComponentEditData::remember_deformed_curve_positions_if_necessary( + GeometrySet &geometry) +{ + /* This component should be created at the start of object evaluation if it's necessary. */ + if (!geometry.has<GeometryComponentEditData>()) { + return; + } + GeometryComponentEditData &edit_component = + geometry.get_component_for_write<GeometryComponentEditData>(); + if (!edit_component.curves_edit_hints_) { + return; + } + if (edit_component.curves_edit_hints_->positions.has_value()) { + return; + } + const Curves *curves_id = geometry.get_curves_for_read(); + if (curves_id == nullptr) { + return; + } + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + const int points_num = curves.points_num(); + if (points_num != edit_component.curves_edit_hints_->curves_id_orig.geometry.point_num) { + return; + } + edit_component.curves_edit_hints_->positions.emplace(points_num); + edit_component.curves_edit_hints_->positions->as_mutable_span().copy_from(curves.positions()); +} diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 653be03b991..c16311945ba 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -13,7 +13,6 @@ #include "DNA_collection_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" @@ -157,7 +156,7 @@ void InstancesComponent::remove_instances(const IndexMask mask) dst_attributes.reallocate(mask.size()); src_attributes.foreach_attribute( - [&](const bke::AttributeIDRef &id, const AttributeMetaData &meta_data) { + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { if (!id.should_be_kept()) { return true; } @@ -366,20 +365,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } -int InstancesComponent::attribute_domain_num(const eAttrDomain domain) const -{ - if (domain != ATTR_DOMAIN_INSTANCE) { - return 0; - } - return this->instances_num(); -} - -blender::bke::CustomDataAttributes &InstancesComponent::attributes() +blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() { return this->attributes_; } -const blender::bke::CustomDataAttributes &InstancesComponent::attributes() const +const blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() const { return this->attributes_; } @@ -404,17 +395,17 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider { } - GVArray try_get_for_read(const GeometryComponent &component) const final + GVArray try_get_for_read(const void *owner) const final { - const InstancesComponent &instances_component = static_cast<const InstancesComponent &>( - component); + const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>( + owner); Span<float4x4> transforms = instances_component.instance_transforms(); return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(transforms); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + GAttributeWriter try_get_for_write(void *owner) const final { - InstancesComponent &instances_component = static_cast<InstancesComponent &>(component); + InstancesComponent &instances_component = *static_cast<InstancesComponent *>(owner); MutableSpan<float4x4> transforms = instances_component.instance_transforms(); return {VMutableArray<float3>::ForDerivedSpan<float4x4, get_transform_position, @@ -422,18 +413,17 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider domain_}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &UNUSED(component)) const final + bool exists(const void *UNUSED(owner)) const final { return true; } @@ -443,13 +433,17 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() { static InstancePositionAttributeProvider position; static CustomDataAccessInfo instance_custom_data_access = { - [](GeometryComponent &component) -> CustomData * { - InstancesComponent &inst = static_cast<InstancesComponent &>(component); - return &inst.attributes().data; + [](void *owner) -> CustomData * { + InstancesComponent &inst = *static_cast<InstancesComponent *>(owner); + return &inst.instance_attributes().data; + }, + [](const void *owner) -> const CustomData * { + const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner); + return &inst.instance_attributes().data; }, - [](const GeometryComponent &component) -> const CustomData * { - const InstancesComponent &inst = static_cast<const InstancesComponent &>(component); - return &inst.attributes().data; + [](const void *owner) -> int { + const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner); + return inst.instances_num(); }, nullptr}; @@ -476,14 +470,57 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() return ComponentAttributeProviders({&position, &id}, {&instance_custom_data}); } + +static AttributeAccessorFunctions get_instances_accessor_functions() +{ + static const ComponentAttributeProviders providers = create_attribute_providers_for_instances(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const InstancesComponent &instances = *static_cast<const InstancesComponent *>(owner); + switch (domain) { + case ATTR_DOMAIN_INSTANCE: + return instances.instances_num(); + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return domain == ATTR_DOMAIN_INSTANCE; + }; + fn.adapt_domain = [](const void *UNUSED(owner), + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { + if (from_domain == to_domain && from_domain == ATTR_DOMAIN_INSTANCE) { + return varray; + } + return blender::GVArray{}; + }; + return fn; +} + +static const AttributeAccessorFunctions &get_instances_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_instances_accessor_functions(); + return fn; +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers() - const +std::optional<blender::bke::AttributeAccessor> InstancesComponent::attributes() const +{ + return blender::bke::AttributeAccessor(this, + blender::bke::get_instances_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> InstancesComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_instances(); - return &providers; + return blender::bke::MutableAttributeAccessor( + this, blender::bke::get_instances_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 9e64acf218b..436868ba375 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -7,7 +7,6 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_deform.h" #include "BKE_geometry_fields.hh" @@ -151,7 +150,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, * array and copy the face normal for each of its corners. In this case using the mesh * component's generic domain interpolation is fine, the data will still be normalized, * since the face normal is just copied to every corner. */ - return mesh_component.attribute_try_adapt_domain( + return mesh_component.attributes()->adapt_domain( VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); @@ -169,26 +168,6 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, /** \name Attribute Access * \{ */ -int MeshComponent::attribute_domain_num(const eAttrDomain domain) const -{ - if (mesh_ == nullptr) { - return 0; - } - switch (domain) { - case ATTR_DOMAIN_CORNER: - return mesh_->totloop; - case ATTR_DOMAIN_POINT: - return mesh_->totvert; - case ATTR_DOMAIN_EDGE: - return mesh_->totedge; - case ATTR_DOMAIN_FACE: - return mesh_->totpoly; - default: - break; - } - return 0; -} - namespace blender::bke { template<typename T> @@ -319,12 +298,14 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, const MPoly &poly = mesh.mpoly[poly_index]; /* For every edge, mix values from the two adjacent corners (the current and next corner). */ - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_next = (loop_index + 1) % poly.totloop; - const MLoop &loop = mesh.mloop[loop_index]; + for (const int i : IndexRange(poly.totloop)) { + const int next_i = (i + 1) % poly.totloop; + const int loop_i = poly.loopstart + i; + const int next_loop_i = poly.loopstart + next_i; + const MLoop &loop = mesh.mloop[loop_i]; const int edge_index = loop.e; - mixer.mix_in(edge_index, old_values[loop_index]); - mixer.mix_in(edge_index, old_values[loop_index_next]); + mixer.mix_in(edge_index, old_values[loop_i]); + mixer.mix_in(edge_index, old_values[next_loop_i]); } } @@ -346,13 +327,16 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, for (const int poly_index : IndexRange(mesh.totpoly)) { const MPoly &poly = mesh.mpoly[poly_index]; - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1); - const MLoop &loop = mesh.mloop[loop_index]; + for (const int i : IndexRange(poly.totloop)) { + const int next_i = (i + 1) % poly.totloop; + const int loop_i = poly.loopstart + i; + const int next_loop_i = poly.loopstart + next_i; + const MLoop &loop = mesh.mloop[loop_i]; const int edge_index = loop.e; + loose_edges[edge_index] = false; - if (!old_values[loop_index] || !old_values[loop_index_next]) { + if (!old_values[loop_i] || !old_values[next_loop_i]) { r_values[edge_index] = false; } } @@ -747,9 +731,10 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v } // namespace blender::bke -blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const +static blender::GVArray adapt_mesh_attribute_domain(const Mesh &mesh, + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { if (!varray) { return {}; @@ -765,11 +750,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_CORNER: { switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, varray); + return blender::bke::adapt_mesh_domain_corner_to_point(mesh, varray); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, varray); + return blender::bke::adapt_mesh_domain_corner_to_face(mesh, varray); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, varray); + return blender::bke::adapt_mesh_domain_corner_to_edge(mesh, varray); default: break; } @@ -778,11 +763,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_POINT: { switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, varray); + return blender::bke::adapt_mesh_domain_point_to_corner(mesh, varray); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, varray); + return blender::bke::adapt_mesh_domain_point_to_face(mesh, varray); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, varray); + return blender::bke::adapt_mesh_domain_point_to_edge(mesh, varray); default: break; } @@ -791,11 +776,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_FACE: { switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, varray); + return blender::bke::adapt_mesh_domain_face_to_point(mesh, varray); case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, varray); + return blender::bke::adapt_mesh_domain_face_to_corner(mesh, varray); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, varray); + return blender::bke::adapt_mesh_domain_face_to_edge(mesh, varray); default: break; } @@ -804,11 +789,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_EDGE: { switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, varray); + return blender::bke::adapt_mesh_domain_edge_to_corner(mesh, varray); case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, varray); + return blender::bke::adapt_mesh_domain_edge_to_point(mesh, varray); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, varray); + return blender::bke::adapt_mesh_domain_edge_to_face(mesh, varray); default: break; } @@ -821,20 +806,6 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G return {}; } -static Mesh *get_mesh_from_component_for_write(GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - return mesh_component.get_for_write(); -} - -static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return mesh_component.get_for_read(); -} - namespace blender::bke { template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> @@ -864,9 +835,9 @@ static void set_vertex_position(MVert &vert, float3 position) copy_v3_v3(vert.co, position); } -static void tag_component_positions_changed(GeometryComponent &component) +static void tag_component_positions_changed(void *owner) { - Mesh *mesh = get_mesh_from_component_for_write(component); + Mesh *mesh = static_cast<Mesh *>(owner); if (mesh != nullptr) { BKE_mesh_tag_coords_changed(mesh); } @@ -1001,15 +972,13 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> { */ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); if (!attribute_id.is_named()) { return {}; } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = static_cast<const Mesh *>(owner); if (mesh == nullptr) { return {}; } @@ -1028,15 +997,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); if (!attribute_id.is_named()) { return {}; } - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - Mesh *mesh = mesh_component.get_for_write(); + Mesh *mesh = static_cast<Mesh *>(owner); if (mesh == nullptr) { return {}; } @@ -1060,14 +1026,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); if (!attribute_id.is_named()) { return false; } - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - Mesh *mesh = mesh_component.get_for_write(); + Mesh *mesh = static_cast<Mesh *>(owner); if (mesh == nullptr) { return true; } @@ -1101,12 +1065,9 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { return true; } - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = static_cast<const Mesh *>(owner); if (mesh == nullptr) { return true; } @@ -1136,35 +1097,34 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const final + GVArray try_get_for_read(const void *owner) const final { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = static_cast<const Mesh *>(owner); if (mesh == nullptr || mesh->totpoly == 0) { return {}; } return VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}); } - WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final + GAttributeWriter try_get_for_write(void *UNUSED(owner)) const final { return {}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0; + const Mesh *mesh = static_cast<const Mesh *>(owner); + return mesh->totpoly != 0; } }; @@ -1174,34 +1134,42 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { */ static ComponentAttributeProviders create_attribute_providers_for_mesh() { - static auto update_custom_data_pointers = [](GeometryComponent &component) { - if (Mesh *mesh = get_mesh_from_component_for_write(component)) { - BKE_mesh_update_customdata_pointers(mesh, false); - } + static auto update_custom_data_pointers = [](void *owner) { + Mesh *mesh = static_cast<Mesh *>(owner); + BKE_mesh_update_customdata_pointers(mesh, false); }; #define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \ - [](GeometryComponent &component) -> CustomData * { \ - Mesh *mesh = get_mesh_from_component_for_write(component); \ - return mesh ? &mesh->NAME : nullptr; \ + [](void *owner) -> CustomData * { \ + Mesh *mesh = static_cast<Mesh *>(owner); \ + return &mesh->NAME; \ } #define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \ - [](const GeometryComponent &component) -> const CustomData * { \ - const Mesh *mesh = get_mesh_from_component_for_read(component); \ - return mesh ? &mesh->NAME : nullptr; \ + [](const void *owner) -> const CustomData * { \ + const Mesh *mesh = static_cast<const Mesh *>(owner); \ + return &mesh->NAME; \ + } +#define MAKE_GET_ELEMENT_NUM_GETTER(NAME) \ + [](const void *owner) -> int { \ + const Mesh *mesh = static_cast<const Mesh *>(owner); \ + return mesh->NAME; \ } static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata), MAKE_CONST_CUSTOM_DATA_GETTER(ldata), + MAKE_GET_ELEMENT_NUM_GETTER(totloop), update_custom_data_pointers}; static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata), MAKE_CONST_CUSTOM_DATA_GETTER(vdata), + MAKE_GET_ELEMENT_NUM_GETTER(totvert), update_custom_data_pointers}; static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata), MAKE_CONST_CUSTOM_DATA_GETTER(edata), + MAKE_GET_ELEMENT_NUM_GETTER(totedge), update_custom_data_pointers}; static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata), MAKE_CONST_CUSTOM_DATA_GETTER(pdata), + MAKE_GET_ELEMENT_NUM_GETTER(totpoly), update_custom_data_pointers}; #undef MAKE_CONST_CUSTOM_DATA_GETTER @@ -1297,13 +1265,73 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() &face_custom_data}); } +static AttributeAccessorFunctions get_mesh_accessor_functions() +{ + static const ComponentAttributeProviders providers = create_attribute_providers_for_mesh(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const Mesh &mesh = *static_cast<const Mesh *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return mesh.totvert; + case ATTR_DOMAIN_EDGE: + return mesh.totedge; + case ATTR_DOMAIN_FACE: + return mesh.totpoly; + case ATTR_DOMAIN_CORNER: + return mesh.totloop; + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); + }; + fn.adapt_domain = [](const void *owner, + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) -> blender::GVArray { + if (owner == nullptr) { + return {}; + } + const Mesh &mesh = *static_cast<const Mesh *>(owner); + return adapt_mesh_attribute_domain(mesh, varray, from_domain, to_domain); + }; + return fn; +} + +static const AttributeAccessorFunctions &get_mesh_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_mesh_accessor_functions(); + return fn; +} + +AttributeAccessor mesh_attributes(const Mesh &mesh) +{ + return AttributeAccessor(&mesh, get_mesh_accessor_functions_ref()); +} + +MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh) +{ + return MutableAttributeAccessor(&mesh, get_mesh_accessor_functions_ref()); +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const +std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const +{ + return blender::bke::AttributeAccessor(mesh_, blender::bke::get_mesh_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> MeshComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_mesh(); - return &providers; + Mesh *mesh = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(mesh, + blender::bke::get_mesh_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index facdbed265d..4953da8a5ee 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -2,7 +2,6 @@ #include "DNA_pointcloud_types.h" -#include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_pointcloud.h" @@ -104,17 +103,6 @@ void PointCloudComponent::ensure_owns_direct_data() /** \name Attribute Access * \{ */ -int PointCloudComponent::attribute_domain_num(const eAttrDomain domain) const -{ - if (pointcloud_ == nullptr) { - return 0; - } - if (domain != ATTR_DOMAIN_POINT) { - return 0; - } - return pointcloud_->totpoint; -} - namespace blender::bke { /** @@ -123,23 +111,19 @@ namespace blender::bke { */ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() { - static auto update_custom_data_pointers = [](GeometryComponent &component) { - PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component); - if (PointCloud *pointcloud = pointcloud_component.get_for_write()) { - BKE_pointcloud_update_customdata_pointers(pointcloud); - } - }; + static auto update_custom_data_pointers = [](void * /*owner*/) {}; static CustomDataAccessInfo point_access = { - [](GeometryComponent &component) -> CustomData * { - PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component); - PointCloud *pointcloud = pointcloud_component.get_for_write(); - return pointcloud ? &pointcloud->pdata : nullptr; + [](void *owner) -> CustomData * { + PointCloud *pointcloud = static_cast<PointCloud *>(owner); + return &pointcloud->pdata; + }, + [](const void *owner) -> const CustomData * { + const PointCloud *pointcloud = static_cast<const PointCloud *>(owner); + return &pointcloud->pdata; }, - [](const GeometryComponent &component) -> const CustomData * { - const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>( - component); - const PointCloud *pointcloud = pointcloud_component.get_for_read(); - return pointcloud ? &pointcloud->pdata : nullptr; + [](const void *owner) -> int { + const PointCloud *pointcloud = static_cast<const PointCloud *>(owner); + return pointcloud->totpoint; }, update_custom_data_pointers}; @@ -180,14 +164,68 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data}); } +static AttributeAccessorFunctions get_pointcloud_accessor_functions() +{ + static const ComponentAttributeProviders providers = + create_attribute_providers_for_point_cloud(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return pointcloud.totpoint; + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return domain == ATTR_DOMAIN_POINT; + }; + fn.adapt_domain = [](const void *UNUSED(owner), + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { + if (from_domain == to_domain && from_domain == ATTR_DOMAIN_POINT) { + return varray; + } + return blender::GVArray{}; + }; + return fn; +} + +static const AttributeAccessorFunctions &get_pointcloud_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_pointcloud_accessor_functions(); + return fn; +} + +AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud) +{ + return AttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref()); +} + +MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud) +{ + return MutableAttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref()); +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers() - const +std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() const +{ + return blender::bke::AttributeAccessor(pointcloud_, + blender::bke::get_pointcloud_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> PointCloudComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_point_cloud(); - return &providers; + PointCloud *pointcloud = this->get_for_write(); + return blender::bke::MutableAttributeAccessor( + pointcloud, blender::bke::get_pointcloud_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 01e2b841282..72ad93539b3 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -7,7 +7,6 @@ #include "BLT_translation.h" #include "BKE_attribute.h" -#include "BKE_attribute_access.hh" #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" @@ -54,6 +53,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return new VolumeComponent(); case GEO_COMPONENT_TYPE_CURVE: return new CurveComponent(); + case GEO_COMPONENT_TYPE_EDIT: + return new GeometryComponentEditData(); case GEO_COMPONENT_TYPE_SIMULATION: return new SimulationComponent(); } @@ -61,6 +62,27 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return nullptr; } +int GeometryComponent::attribute_domain_size(const eAttrDomain domain) const +{ + if (this->is_empty()) { + return 0; + } + const std::optional<blender::bke::AttributeAccessor> attributes = this->attributes(); + if (attributes.has_value()) { + return attributes->domain_size(domain); + } + return 0; +} + +std::optional<blender::bke::AttributeAccessor> GeometryComponent::attributes() const +{ + return std::nullopt; +}; +std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attributes_for_write() +{ + return std::nullopt; +} + void GeometryComponent::user_add() const { users_.fetch_add(1); @@ -157,6 +179,20 @@ void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component } } +void GeometrySet::keep_only_during_modify( + const blender::Span<GeometryComponentType> component_types) +{ + Vector<GeometryComponentType> extended_types = component_types; + extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_INSTANCES); + extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_EDIT); + this->keep_only(extended_types); +} + +void GeometrySet::remove_geometry_during_modify() +{ + this->keep_only_during_modify({}); +} + void GeometrySet::add(const GeometryComponent &component) { BLI_assert(!components_[component.type()]); @@ -272,6 +308,13 @@ const Curves *GeometrySet::get_curves_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +const blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_read() const +{ + const GeometryComponentEditData *component = + this->get_component_for_read<GeometryComponentEditData>(); + return (component == nullptr) ? nullptr : component->curves_edit_hints_.get(); +} + bool GeometrySet::has_pointcloud() const { const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>(); @@ -440,6 +483,16 @@ Curves *GeometrySet::get_curves_for_write() return component == nullptr ? nullptr : component->get_for_write(); } +blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write() +{ + if (!this->has<GeometryComponentEditData>()) { + return nullptr; + } + GeometryComponentEditData &component = + this->get_component_for_write<GeometryComponentEditData>(); + return component.curves_edit_hints_.get(); +} + void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_types, const bool include_instances, const AttributeForeachCallback callback) const @@ -451,11 +504,14 @@ void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_ continue; } const GeometryComponent &component = *this->get_component_for_read(component_type); - component.attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - callback(attribute_id, meta_data, component); - return true; - }); + const std::optional<AttributeAccessor> attributes = component.attributes(); + if (attributes.has_value()) { + attributes->for_all( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + callback(attribute_id, meta_data, component); + return true; + }); + } } if (include_instances && this->has_instances()) { const InstancesComponent &instances = *this->get_component_for_read<InstancesComponent>(); @@ -469,7 +525,7 @@ void GeometrySet::gather_attributes_for_propagation( const Span<GeometryComponentType> component_types, const GeometryComponentType dst_component_type, bool include_instances, - blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const + blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const { using namespace blender; using namespace blender::bke; @@ -482,8 +538,8 @@ void GeometrySet::gather_attributes_for_propagation( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data, const GeometryComponent &component) { - if (component.attribute_is_builtin(attribute_id)) { - if (!dummy_component->attribute_is_builtin(attribute_id)) { + if (component.attributes()->is_builtin(attribute_id)) { + if (!dummy_component->attributes()->is_builtin(attribute_id)) { /* Don't propagate built-in attributes that are not built-in on the destination * component. */ return; @@ -663,6 +719,8 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) case GEO_COMPONENT_TYPE_CURVE: is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT); break; + case GEO_COMPONENT_TYPE_EDIT: + break; } if (is_instance) { return true; diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 2d6e0e05a97..df48a99f706 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -27,8 +27,8 @@ static void geometry_set_collect_recursive_collection(const Collection &collecti static void add_final_mesh_as_geometry_component(const Object &object, GeometrySet &geometry_set) { - Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(&const_cast<Object &>(object), - false); + Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object( + &const_cast<Object &>(object)); if (mesh != nullptr) { BKE_mesh_wrapper_ensure_mdata(mesh); @@ -71,11 +71,6 @@ GeometrySet object_get_evaluated_geometry_set(const Object &object) return geometry_set; } - /* TODO: Cover the case of point clouds without modifiers-- they may not be covered by the - * #geometry_set_eval case above. */ - - /* TODO: Add volume support. */ - /* Return by value since there is not always an existing geometry set owned elsewhere to use. */ return {}; } @@ -162,41 +157,6 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, geometry_set_collect_recursive(geometry_set, float4x4::identity(), r_instance_groups); } -void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, - Span<GeometryComponentType> component_types, - const Set<std::string> &ignored_attributes, - Map<AttributeIDRef, AttributeKind> &r_attributes) -{ - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - for (const GeometryComponentType component_type : component_types) { - if (!set.has(component_type)) { - continue; - } - const GeometryComponent &component = *set.get_component_for_read(component_type); - - component.attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { - return true; - } - auto add_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; - attribute_kind->data_type = meta_data.data_type; - }; - auto modify_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ - attribute_kind->data_type = bke::attribute_data_type_highest_complexity( - {attribute_kind->data_type, meta_data.data_type}); - }; - - r_attributes.add_or_modify(attribute_id, add_info, modify_info); - return true; - }); - } - } -} - } // namespace blender::bke void InstancesComponent::foreach_referenced_geometry( diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 20b8342f090..d68b322e4c5 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -501,7 +501,7 @@ void BKE_gpencil_convert_curve(Main *bmain, } /* Check if there is an active frame and add if needed. */ - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_COPY); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_COPY); /* Read all splines of the curve and create a stroke for each. */ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 0445a1540c7..d0075a7d161 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -2706,7 +2706,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false); } bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get( - gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); + gpl_fill, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW); int i; for (i = 0; i < mpoly_len; i++) { const MPoly *mp = &mpoly[i]; @@ -2781,7 +2781,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false); } bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get( - gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); + gpl_stroke, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW); gpencil_generate_edgeloops(ob_eval, gpd, diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 8739f2f7082..82899b974bc 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -95,7 +95,7 @@ void BKE_gpencil_cache_data_init(Depsgraph *depsgraph, Object *ob) MEM_SAFE_FREE(mmd->cache_data); } Object *ob_target = DEG_get_evaluated_object(depsgraph, ob); - Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false); + Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target); mmd->cache_data = MEM_callocN(sizeof(ShrinkwrapTreeData), __func__); if (BKE_shrinkwrap_init_tree( mmd->cache_data, target, mmd->shrink_type, mmd->shrink_mode, false)) { diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index e55143d6852..923582dff4c 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -209,7 +209,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) case ID_##_id: \ return FILTER_ID_##_id - switch (idcode) { +#define CASE_IDFILTER_NONE(_id) \ + case ID_##_id: \ + return 0 + + switch ((ID_Type)idcode) { CASE_IDFILTER(AC); CASE_IDFILTER(AR); CASE_IDFILTER(BR); @@ -220,7 +224,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER_NONE(IP); + CASE_IDFILTER(KE); CASE_IDFILTER(LA); + CASE_IDFILTER(LI); + CASE_IDFILTER(LP); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -234,22 +242,25 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(PAL); CASE_IDFILTER(PC); CASE_IDFILTER(PT); - CASE_IDFILTER(LP); CASE_IDFILTER(SCE); + CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); - CASE_IDFILTER(SPK); CASE_IDFILTER(SO); + CASE_IDFILTER(SPK); CASE_IDFILTER(TE); CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(VO); + CASE_IDFILTER(WM); CASE_IDFILTER(WO); CASE_IDFILTER(WS); - default: - return 0; } + BLI_assert_unreachable(); + return 0; + #undef CASE_IDFILTER +#undef CASE_IDFILTER_NONE } short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) @@ -258,6 +269,8 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) case FILTER_ID_##_id: \ return ID_##_id +#define CASE_IDFILTER_NONE(_id) (void)0 + switch (idfilter) { CASE_IDFILTER(AC); CASE_IDFILTER(AR); @@ -269,7 +282,11 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER_NONE(IP); + CASE_IDFILTER(KE); CASE_IDFILTER(LA); + CASE_IDFILTER(LI); + CASE_IDFILTER(LP); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -283,21 +300,25 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(PAL); CASE_IDFILTER(PC); CASE_IDFILTER(PT); - CASE_IDFILTER(LP); CASE_IDFILTER(SCE); + CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); - CASE_IDFILTER(SPK); CASE_IDFILTER(SO); + CASE_IDFILTER(SPK); CASE_IDFILTER(TE); CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(VO); + CASE_IDFILTER(WM); CASE_IDFILTER(WO); - default: - return 0; + CASE_IDFILTER(WS); } + BLI_assert_unreachable(); + return 0; + #undef CASE_IDFILTER +#undef CASE_IDFILTER_NONE } int BKE_idtype_idcode_to_index(const short idcode) diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 0c1f01c3796..c2b8ec95a46 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -863,33 +863,46 @@ void BKE_image_get_tile_uv(const Image *ima, const int tile_number, float r_uv[2 } } -int BKE_image_find_nearest_tile(const Image *image, const float co[2]) +int BKE_image_find_nearest_tile_with_offset(const Image *image, + const float co[2], + float r_uv_offset[2]) { - const float co_floor[2] = {floorf(co[0]), floorf(co[1])}; - /* Distance to the closest UDIM tile. */ + /* Distance squared to the closest UDIM tile. */ float dist_best_sq = FLT_MAX; + float uv_offset_best[2] = {0, 0}; int tile_number_best = -1; + const float co_offset[2] = {co[0] - 0.5f, co[1] - 0.5f}; + LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { float uv_offset[2]; BKE_image_get_tile_uv(image, tile->tile_number, uv_offset); - if (equals_v2v2(co_floor, uv_offset)) { - return tile->tile_number; - } - - /* Distance between co[2] and UDIM tile. */ - const float dist_sq = len_squared_v2v2(uv_offset, co); + /* Distance squared between co[2] and center of UDIM tile. */ + const float dist_sq = len_squared_v2v2(uv_offset, co_offset); if (dist_sq < dist_best_sq) { dist_best_sq = dist_sq; tile_number_best = tile->tile_number; + copy_v2_v2(uv_offset_best, uv_offset); + + if (dist_best_sq < 0.5f * 0.5f) { + break; /* No other tile can be closer. */ + } } } - + if (tile_number_best != -1) { + copy_v2_v2(r_uv_offset, uv_offset_best); + } return tile_number_best; } +int BKE_image_find_nearest_tile(const struct Image *image, const float co[2]) +{ + float uv_offset_dummy[2]; + return BKE_image_find_nearest_tile_with_offset(image, co, uv_offset_dummy); +} + static void image_init_color_management(Image *ima) { ImBuf *ibuf; @@ -1172,24 +1185,83 @@ Image *BKE_image_add_generated(Main *bmain, return ima; } -Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) +static void image_colorspace_from_imbuf(Image *image, const ImBuf *ibuf) { - Image *ima; + const char *colorspace_name = NULL; + if (ibuf->rect_float) { + if (ibuf->float_colorspace) { + colorspace_name = IMB_colormanagement_colorspace_get_name(ibuf->float_colorspace); + } + else { + colorspace_name = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_FLOAT); + } + } + + if (ibuf->rect && !colorspace_name) { + if (ibuf->rect_colorspace) { + colorspace_name = IMB_colormanagement_colorspace_get_name(ibuf->rect_colorspace); + } + else { + colorspace_name = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); + } + } + + if (colorspace_name) { + STRNCPY(image->colorspace_settings.name, colorspace_name); + } +} + +Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) +{ if (name == nullptr) { name = BLI_path_basename(ibuf->name); } - ima = image_alloc(bmain, name, IMA_SRC_FILE, IMA_TYPE_IMAGE); + /* When the image buffer has valid path create a new image with "file" source and copy the path + * from the image buffer. + * Otherwise create "generated" image, avoiding invalid configuration with an empty file path. */ + const eImageSource source = ibuf->name[0] != '\0' ? IMA_SRC_FILE : IMA_SRC_GENERATED; - if (ima) { - STRNCPY(ima->filepath, ibuf->name); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + Image *ima = image_alloc(bmain, name, source, IMA_TYPE_IMAGE); + + if (!ima) { + return nullptr; } + BKE_image_replace_imbuf(ima, ibuf); + return ima; } +void BKE_image_replace_imbuf(Image *image, ImBuf *ibuf) +{ + BLI_assert(image->type == IMA_TYPE_IMAGE && + ELEM(image->source, IMA_SRC_FILE, IMA_SRC_GENERATED)); + + BKE_image_free_buffers(image); + + image_assign_ibuf(image, ibuf, IMA_NO_INDEX, 0); + image_colorspace_from_imbuf(image, ibuf); + + /* Keep generated image type flags consistent with the image buffer. */ + if (image->source == IMA_SRC_GENERATED) { + if (ibuf->rect_float) { + image->gen_flag |= IMA_GEN_FLOAT; + } + else { + image->gen_flag &= ~IMA_GEN_FLOAT; + } + + image->gen_x = ibuf->x; + image->gen_y = ibuf->y; + } + + /* Consider image dirty since its content can not be re-created unless the image is explicitly + * saved. */ + BKE_image_mark_dirty(image, ibuf); +} + /** Pack image buffer to memory as PNG or EXR. */ static bool image_memorypack_imbuf( Image *ima, ImBuf *ibuf, int view, int tile_number, const char *filepath) @@ -1582,7 +1654,7 @@ static void stampdata( } if (use_dynamic && scene->r.stamp & R_STAMP_MARKER) { - const char *name = BKE_scene_find_last_marker_name(scene, CFRA); + const char *name = BKE_scene_find_last_marker_name(scene, scene->r.cfra); if (name) { STRNCPY(text, name); @@ -1950,7 +2022,7 @@ void BKE_image_stamp_buf(Scene *scene, y -= BUFF_MARGIN_Y * 2; } - /* Top left corner, below File, Date, Rendertime */ + /* Top left corner, below File, Date, Render-time */ if (TEXT_SIZE_CHECK(stamp_data.memory, w, h)) { y -= h; @@ -1973,7 +2045,7 @@ void BKE_image_stamp_buf(Scene *scene, y -= BUFF_MARGIN_Y * 2; } - /* Top left corner, below File, Date, Rendertime, Memory */ + /* Top left corner, below: File, Date, Render-time, Memory. */ if (TEXT_SIZE_CHECK(stamp_data.hostname, w, h)) { y -= h; @@ -1996,7 +2068,7 @@ void BKE_image_stamp_buf(Scene *scene, y -= BUFF_MARGIN_Y * 2; } - /* Top left corner, below File, Date, Memory, Rendertime, Hostname */ + /* Top left corner, below: File, Date, Memory, Render-time, Host-name. */ BLF_enable(mono, BLF_WORD_WRAP); if (TEXT_SIZE_CHECK_WORD_WRAP(stamp_data.note, w, h)) { y -= h; @@ -2416,7 +2488,10 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf) return ok; } -int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, const bool save_copy) +int BKE_imbuf_write_as(ImBuf *ibuf, + const char *name, + const ImageFormatData *imf, + const bool save_copy) { ImBuf ibuf_back = *ibuf; int ok; @@ -4998,13 +5073,7 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *r_width, int *r_hei } else if (image != nullptr && image->type == IMA_TYPE_R_RESULT && iuser != nullptr && iuser->scene != nullptr) { - Scene *scene = iuser->scene; - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } + BKE_render_resolution(&iuser->scene->r, true, r_width, r_height); } else { *r_width = IMG_SIZE_FALLBACK; diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 6edb9e1b24c..6506b40b603 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -737,11 +737,11 @@ static void gpu_texture_update_from_ibuf( } else { /* Byte image is in original colorspace from the file, and may need conversion. */ - if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { + if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { /* Non-color data, just store buffer as is. */ } - else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) { + else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */ rect = (uchar *)MEM_mallocN(sizeof(uchar[4]) * w * h, __func__); if (rect == nullptr) { diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 9dda3762553..cd86d3f7087 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -204,7 +204,7 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, return (ibuf != nullptr); } -void BKE_image_save_options_update(ImageSaveOptions *opts, Image *image) +void BKE_image_save_options_update(ImageSaveOptions *opts, const Image *image) { /* Auto update color space when changing save as render and file type. */ if (opts->save_as_render) { @@ -253,11 +253,26 @@ void BKE_image_save_options_free(ImageSaveOptions *opts) BKE_image_format_free(&opts->im_format); } +static void image_save_update_filepath(Image *ima, + const char *filepath, + const ImageSaveOptions *opts) +{ + if (opts->do_newpath) { + BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); + + /* only image path, never ibuf */ + if (opts->relative) { + const char *relbase = ID_BLEND_PATH(opts->bmain, &ima->id); + BLI_path_rel(ima->filepath, relbase); /* only after saving */ + } + } +} + static void image_save_post(ReportList *reports, Image *ima, ImBuf *ibuf, int ok, - ImageSaveOptions *opts, + const ImageSaveOptions *opts, int save_copy, const char *filepath, bool *r_colorspace_changed) @@ -273,13 +288,11 @@ static void image_save_post(ReportList *reports, if (opts->do_newpath) { BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); - BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); + } - /* only image path, never ibuf */ - if (opts->relative) { - const char *relbase = ID_BLEND_PATH(opts->bmain, &ima->id); - BLI_path_rel(ima->filepath, relbase); /* only after saving */ - } + /* The tiled image code-path must call this on its own. */ + if (ima->source != IMA_SRC_TILED) { + image_save_update_filepath(ima, filepath, opts); } ibuf->userflags &= ~IB_BITMAPDIRTY; @@ -346,7 +359,7 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) static bool image_save_single(ReportList *reports, Image *ima, ImageUser *iuser, - ImageSaveOptions *opts, + const ImageSaveOptions *opts, bool *r_colorspace_changed) { void *lock; @@ -362,7 +375,7 @@ static bool image_save_single(ReportList *reports, ImBuf *colormanaged_ibuf = nullptr; const bool save_copy = opts->save_copy; const bool save_as_render = opts->save_as_render; - ImageFormatData *imf = &opts->im_format; + const ImageFormatData *imf = &opts->im_format; if (ima->type == IMA_TYPE_R_RESULT) { /* enforce user setting for RGB or RGBA, but skip BW */ @@ -607,7 +620,7 @@ static bool image_save_single(ReportList *reports, } bool BKE_image_save( - ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) + ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts) { /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ ImageUser save_iuser; @@ -640,22 +653,23 @@ bool BKE_image_save( ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed); } else { - char filepath[FILE_MAX]; - BLI_strncpy(filepath, opts->filepath, sizeof(filepath)); - /* Save all the tiles. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageSaveOptions tile_opts = *opts; BKE_image_set_filepath_from_tile_number( - opts->filepath, udim_pattern, tile_format, tile->tile_number); + tile_opts.filepath, udim_pattern, tile_format, tile->tile_number); iuser->tile = tile->tile_number; - ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed); + ok = image_save_single(reports, ima, iuser, &tile_opts, &colorspace_changed); if (!ok) { break; } } - BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); - BLI_strncpy(opts->filepath, filepath, sizeof(opts->filepath)); + + /* Set the image path only if all tiles were ok. */ + if (ok) { + image_save_update_filepath(ima, opts->filepath, opts); + } MEM_freeN(udim_pattern); } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 594cffe6406..7ef15912567 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -191,7 +191,7 @@ static void shapekey_blend_read_expand(BlendExpander *expander, ID *id) IDTypeInfo IDType_ID_KE = { .id_code = ID_KE, - .id_filter = 0, + .id_filter = FILTER_ID_KE, .main_listbase_index = INDEX_ID_KE, .struct_size = sizeof(Key), .name = "Key", @@ -1501,7 +1501,14 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot) } } -float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t arr_size) +static void keyblock_data_convert_to_mesh(const float (*fp)[3], MVert *mvert, const int totvert); +static void keyblock_data_convert_to_lattice(const float (*fp)[3], + BPoint *bpoint, + const int totpoint); +static void keyblock_data_convert_to_curve(const float *fp, ListBase *nurb, const int totpoint); + +float *BKE_key_evaluate_object_ex( + Object *ob, int *r_totelem, float *arr, size_t arr_size, ID *obdata) { Key *key = BKE_key_from_object(ob); KeyBlock *actkb = BKE_keyblock_from_object(ob); @@ -1576,7 +1583,6 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t } } else { - if (ob->type == OB_MESH) { do_mesh_key(ob, key, out, tot); } @@ -1591,6 +1597,31 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t } } + if (obdata != NULL) { + switch (GS(obdata->name)) { + case ID_ME: { + Mesh *mesh = (Mesh *)obdata; + const int totvert = min_ii(tot, mesh->totvert); + keyblock_data_convert_to_mesh((const float(*)[3])out, mesh->mvert, totvert); + break; + } + case ID_LT: { + Lattice *lattice = (Lattice *)obdata; + const int totpoint = min_ii(tot, lattice->pntsu * lattice->pntsv * lattice->pntsw); + keyblock_data_convert_to_lattice((const float(*)[3])out, lattice->def, totpoint); + break; + } + case ID_CU_LEGACY: { + Curve *curve = (Curve *)obdata; + const int totpoint = min_ii(tot, BKE_keyblock_curve_element_count(&curve->nurb)); + keyblock_data_convert_to_curve((const float *)out, &curve->nurb, totpoint); + break; + } + default: + BLI_assert_unreachable(); + } + } + if (r_totelem) { *r_totelem = tot; } @@ -1599,7 +1630,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t float *BKE_key_evaluate_object(Object *ob, int *r_totelem) { - return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0); + return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0, NULL); } int BKE_keyblock_element_count_from_shape(const Key *key, const int shape_index) @@ -1971,21 +2002,22 @@ void BKE_keyblock_convert_from_lattice(const Lattice *lt, KeyBlock *kb) BKE_keyblock_update_from_lattice(lt, kb); } -void BKE_keyblock_convert_to_lattice(const KeyBlock *kb, Lattice *lt) +static void keyblock_data_convert_to_lattice(const float (*fp)[3], + BPoint *bpoint, + const int totpoint) { - BPoint *bp; - const float(*fp)[3]; - int a, tot; - - bp = lt->def; - fp = kb->data; + for (int i = 0; i < totpoint; i++, fp++, bpoint++) { + copy_v3_v3(bpoint->vec, *fp); + } +} - tot = lt->pntsu * lt->pntsv * lt->pntsw; - tot = min_ii(kb->totelem, tot); +void BKE_keyblock_convert_to_lattice(const KeyBlock *kb, Lattice *lt) +{ + BPoint *bp = lt->def; + const float(*fp)[3] = kb->data; + const int tot = min_ii(kb->totelem, lt->pntsu * lt->pntsv * lt->pntsw); - for (a = 0; a < tot; a++, fp++, bp++) { - copy_v3_v3(bp->vec, *fp); - } + keyblock_data_convert_to_lattice(fp, bp, tot); } /************************* Curve ************************/ @@ -2097,42 +2129,40 @@ void BKE_keyblock_convert_from_curve(const Curve *cu, KeyBlock *kb, const ListBa BKE_keyblock_update_from_curve(cu, kb, nurb); } -void BKE_keyblock_convert_to_curve(KeyBlock *kb, Curve *UNUSED(cu), ListBase *nurb) +static void keyblock_data_convert_to_curve(const float *fp, ListBase *nurb, int totpoint) { - Nurb *nu; - BezTriple *bezt; - BPoint *bp; - const float *fp; - int a, tot; - - tot = BKE_keyblock_curve_element_count(nurb); - tot = min_ii(kb->totelem, tot); - - fp = kb->data; - for (nu = nurb->first; nu && tot > 0; nu = nu->next) { - if (nu->bezt) { - for (a = nu->pntsu, bezt = nu->bezt; a && (tot -= KEYELEM_ELEM_LEN_BEZTRIPLE) >= 0; - a--, bezt++) { - for (int i = 0; i < 3; i++) { - copy_v3_v3(bezt->vec[i], &fp[i * 3]); + for (Nurb *nu = nurb->first; nu && totpoint > 0; nu = nu->next) { + if (nu->bezt != NULL) { + BezTriple *bezt = nu->bezt; + for (int i = nu->pntsu; i && (totpoint -= KEYELEM_ELEM_LEN_BEZTRIPLE) >= 0; + i--, bezt++, fp += KEYELEM_FLOAT_LEN_BEZTRIPLE) { + for (int j = 0; j < 3; j++) { + copy_v3_v3(bezt->vec[j], &fp[j * 3]); } bezt->tilt = fp[9]; bezt->radius = fp[10]; - fp += KEYELEM_FLOAT_LEN_BEZTRIPLE; } } else { - for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a && (tot -= KEYELEM_ELEM_LEN_BPOINT) >= 0; - a--, bp++) { + BPoint *bp = nu->bp; + for (int i = nu->pntsu * nu->pntsv; i && (totpoint -= KEYELEM_ELEM_LEN_BPOINT) >= 0; + i--, bp++, fp += KEYELEM_FLOAT_LEN_BPOINT) { copy_v3_v3(bp->vec, fp); bp->tilt = fp[3]; bp->radius = fp[4]; - fp += KEYELEM_FLOAT_LEN_BPOINT; } } } } +void BKE_keyblock_convert_to_curve(KeyBlock *kb, Curve *UNUSED(cu), ListBase *nurb) +{ + const float *fp = kb->data; + const int tot = min_ii(kb->totelem, BKE_keyblock_curve_element_count(nurb)); + + keyblock_data_convert_to_curve(fp, nurb, tot); +} + /************************* Mesh ************************/ void BKE_keyblock_update_from_mesh(const Mesh *me, KeyBlock *kb) @@ -2171,20 +2201,21 @@ void BKE_keyblock_convert_from_mesh(const Mesh *me, const Key *key, KeyBlock *kb BKE_keyblock_update_from_mesh(me, kb); } -void BKE_keyblock_convert_to_mesh(const KeyBlock *kb, MVert *mvert, const int totvert) +static void keyblock_data_convert_to_mesh(const float (*fp)[3], MVert *mvert, const int totvert) { - const float(*fp)[3]; - int a, tot; - - fp = kb->data; - - tot = min_ii(kb->totelem, totvert); - - for (a = 0; a < tot; a++, fp++, mvert++) { + for (int i = 0; i < totvert; i++, fp++, mvert++) { copy_v3_v3(mvert->co, *fp); } } +void BKE_keyblock_convert_to_mesh(const KeyBlock *kb, MVert *mvert, const int totvert) +{ + const float(*fp)[3] = kb->data; + const int tot = min_ii(kb->totelem, totvert); + + keyblock_data_convert_to_mesh(fp, mvert, tot); +} + void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb, const Mesh *mesh, float (*r_vertnors)[3], diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c index 70f8522aab4..40a9d4befdb 100644 --- a/source/blender/blenkernel/intern/lattice_deform.c +++ b/source/blender/blenkernel/intern/lattice_deform.c @@ -348,7 +348,8 @@ static void lattice_deform_coords_impl(const Object *ob_lattice, * We want either a Mesh/Lattice with no derived data, or derived data with deformverts. */ if (defgrp_name && defgrp_name[0] && ob_target && ELEM(ob_target->type, OB_MESH, OB_LATTICE)) { - defgrp_index = BKE_id_defgroup_name_index((ID *)ob_target->data, defgrp_name); + defgrp_index = BKE_id_defgroup_name_index(me_target ? &me_target->id : (ID *)ob_target->data, + defgrp_name); if (defgrp_index != -1) { /* if there's derived data without deformverts, don't use vgroups */ diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 534ff7f1fbc..dabc76f29ca 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -39,6 +39,7 @@ #include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" #include "DNA_workspace_types.h" +#include "DNA_world_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" @@ -1377,12 +1378,12 @@ void BKE_main_collection_sync_remap(const Main *bmain) if (view_layer->object_bases_hash) { BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL); view_layer->object_bases_hash = NULL; - - /* Directly re-create the mapping here, so that we can also deal with duplicates in - * `view_layer->object_bases` list of bases properly. This is the only place where such - * duplicates should be fixed, and not considered as a critical error. */ - view_layer_bases_hash_create(view_layer, true); } + + /* Directly re-create the mapping here, so that we can also deal with duplicates in + * `view_layer->object_bases` list of bases properly. This is the only place where such + * duplicates should be fixed, and not considered as a critical error. */ + view_layer_bases_hash_create(view_layer, true); } BKE_collection_object_cache_free(scene->master_collection); @@ -2588,12 +2589,36 @@ ViewLayer *BKE_view_layer_find_with_lightgroup(struct Scene *scene, return NULL; } -void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer, +void BKE_view_layer_rename_lightgroup(Scene *scene, + ViewLayer *view_layer, ViewLayerLightgroup *lightgroup, const char *name) { + char old_name[64]; + BLI_strncpy_utf8(old_name, lightgroup->name, sizeof(old_name)); BLI_strncpy_utf8(lightgroup->name, name, sizeof(lightgroup->name)); viewlayer_lightgroup_make_name_unique(view_layer, lightgroup); + + if (scene != NULL) { + /* Update objects in the scene to refer to the new name instead. */ + FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { + if (!ID_IS_LINKED(ob) && ob->lightgroup != NULL) { + LightgroupMembership *lgm = ob->lightgroup; + if (STREQ(lgm->name, old_name)) { + BLI_strncpy_utf8(lgm->name, lightgroup->name, sizeof(lgm->name)); + } + } + } + FOREACH_SCENE_OBJECT_END; + + /* Update the scene's world to refer to the new name instead. */ + if (scene->world != NULL && !ID_IS_LINKED(scene->world) && scene->world->lightgroup != NULL) { + LightgroupMembership *lgm = scene->world->lightgroup; + if (STREQ(lgm->name, old_name)) { + BLI_strncpy_utf8(lgm->name, lightgroup->name, sizeof(lgm->name)); + } + } + } } void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *name) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 90a4853fd3e..affa1e72ad0 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -53,6 +53,7 @@ #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_node.h" #include "BKE_rigidbody.h" @@ -186,7 +187,7 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags) id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); id->flag &= ~LIB_INDIRECT_WEAK_LINK; if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { + if (BKE_id_new_name_validate(bmain, which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -842,7 +843,7 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) BLI_addtail(lb, id); /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new * overrides for recursive resync. */ - BKE_id_new_name_validate(lb, id, NULL, true); + BKE_id_new_name_validate(bmain, lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -865,6 +866,7 @@ void BKE_libblock_management_main_remove(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_remlink(lb, id); + BKE_main_namemap_remove_name(bmain, id, id->name + 2); id->tag |= LIB_TAG_NO_MAIN; bmain->is_memfile_undo_written = false; BKE_main_unlock(bmain); @@ -958,7 +960,7 @@ void BKE_main_id_flag_all(Main *bmain, const int flag, const bool value) } } -void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) +void BKE_main_id_repair_duplicate_names_listbase(Main *bmain, ListBase *lb) { int lb_len = 0; LISTBASE_FOREACH (ID *, id, lb) { @@ -982,7 +984,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } for (i = 0; i < lb_len; i++) { if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL, false); + BKE_id_new_name_validate(bmain, lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1073,7 +1075,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name, false); + BKE_id_new_name_validate(bmain, lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1415,255 +1417,8 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint) #undef ID_SORT_STEP_SIZE } -/* NOTE: this code assumes and ensures that the suffix number can never go beyond 1 billion. */ -#define MAX_NUMBER 1000000000 -/* We do not want to get "name.000", so minimal number is 1. */ -#define MIN_NUMBER 1 -/* The maximum value up to which we search for the actual smallest unused number. Beyond that - * value, we will only use the first biggest unused number, without trying to 'fill the gaps' - * in-between already used numbers... */ -#define MAX_NUMBERS_IN_USE 1024 - -/** - * Helper building final ID name from given base_name and number. - * - * If everything goes well and we do generate a valid final ID name in given name, we return - * true. In case the final name would overflow the allowed ID name length, or given number is - * bigger than maximum allowed value, we truncate further the base_name (and given name, which is - * assumed to have the same 'base_name' part), and return false. - */ -static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number) -{ - char number_str[11]; /* Dot + nine digits + NULL terminator. */ - size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number); - - /* If the number would lead to an overflow of the maximum ID name length, we need to truncate - * the base name part and do all the number checks again. */ - if (base_name_len + number_str_len >= MAX_ID_NAME - 2 || number >= MAX_NUMBER) { - if (base_name_len + number_str_len >= MAX_ID_NAME - 2) { - base_name_len = MAX_ID_NAME - 2 - number_str_len - 1; - } - else { - base_name_len--; - } - base_name[base_name_len] = '\0'; - - /* Code above may have generated invalid utf-8 string, due to raw truncation. - * Ensure we get a valid one now. */ - base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len); - - /* Also truncate orig name, and start the whole check again. */ - name[base_name_len] = '\0'; - return false; - } - - /* We have our final number, we can put it in name and exit the function. */ - BLI_strncpy(name + base_name_len, number_str, number_str_len + 1); - return true; -} - -/** - * Check to see if an ID name is already used, and find a new one if so. - * Return true if a new name was created (returned in name). - * - * Normally the ID that's being checked is already in the ListBase, so ID *id points at the new - * entry. The Python Library module needs to know what the name of a data-block will be before it - * is appended, in this case ID *id is NULL. - */ -static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_hint) -{ - BLI_assert(strlen(name) < MAX_ID_NAME - 2); - - *r_id_sorting_hint = NULL; - - ID *id_test = lb->first; - bool is_name_changed = false; - - if (id_test == NULL) { - return is_name_changed; - } - - const short id_type = (short)GS(id_test->name); - - /* Static storage of previous handled ID/name info, used to perform a quicker test and optimize - * creation of huge number of IDs using the same given base name. */ - static char prev_orig_base_name[MAX_ID_NAME - 2] = {0}; - static char prev_final_base_name[MAX_ID_NAME - 2] = {0}; - static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */ - static int prev_number = MIN_NUMBER - 1; - - /* Initial test to check whether we can 'shortcut' the more complex loop of the main code - * below. Note that we do not do that for low numbers, as that would prevent using actual - * smallest available number in some cases, and benefits of this special case handling mostly - * show up with high numbers anyway. */ - if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE && - prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) { - - /* Get the name and number parts ("name.number"). */ - char base_name[MAX_ID_NAME - 2]; - int number = MIN_NUMBER; - size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); - size_t prev_final_base_name_len = strlen(prev_final_base_name); - size_t prev_orig_base_name_len = strlen(prev_orig_base_name); - - if (base_name_len == prev_orig_base_name_len && - STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) { - /* Once we have ensured given base_name and original previous one are the same, we can - * check that previously used number is actually used, and that next one is free. */ - /* Note that from now on, we only used previous final base name, as it might have been - * truncated from original one due to number suffix length. */ - char final_name[MAX_ID_NAME - 2]; - char prev_final_name[MAX_ID_NAME - 2]; - BLI_strncpy(final_name, prev_final_base_name, prev_final_base_name_len + 1); - BLI_strncpy(prev_final_name, prev_final_base_name, prev_final_base_name_len + 1); - - if (id_name_final_build(final_name, base_name, prev_final_base_name_len, prev_number + 1) && - id_name_final_build(prev_final_name, base_name, prev_final_base_name_len, prev_number)) { - /* We successfully built valid final names of previous and current iterations, - * now we have to ensure that previous final name is indeed used in current ID list, - * and that current one is not. */ - bool is_valid = false; - for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && id_test->lib == id->lib) { - if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { - /* We expect final_name to not be already used, so this is a failure. */ - is_valid = false; - break; - } - /* Previous final name should only be found once in the list, so if it was found - * already, no need to do a string comparison again. */ - if (!is_valid && id_test->name[2] == prev_final_name[0] && - STREQ(prev_final_name, id_test->name + 2)) { - is_valid = true; - *r_id_sorting_hint = id_test; - } - } - } - - if (is_valid) { - /* Only the number changed, prev_orig_base_name, prev_final_base_name and prev_id_type - * remain the same. */ - prev_number++; - - strcpy(name, final_name); - return true; - } - } - } - } - - /* To speed up finding smallest unused number within [0 .. MAX_NUMBERS_IN_USE - 1]. - * We do not bother beyond that point. */ - ID *ids_in_use[MAX_NUMBERS_IN_USE] = {NULL}; - - bool is_first_run = true; - while (true) { - /* Get the name and number parts ("name.number"). */ - char base_name[MAX_ID_NAME - 2]; - int number = MIN_NUMBER; - size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); - - /* Store previous original given base name now, as we might alter it later in code below. */ - if (is_first_run) { - strcpy(prev_orig_base_name, base_name); - is_first_run = false; - } - - /* In case we get an insane initial number suffix in given name. */ - /* NOTE: BLI_split_name_num() cannot return negative numbers, so we do not have to check for - * that here. */ - if (number >= MAX_NUMBER || number < MIN_NUMBER) { - number = MIN_NUMBER; - } - - bool is_orig_name_used = false; - for (id_test = lb->first; id_test; id_test = id_test->next) { - char base_name_test[MAX_ID_NAME - 2]; - int number_test; - if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && - (ELEM(id_test->name[base_name_len + 2], '.', '\0')) && - STREQLEN(name, id_test->name + 2, base_name_len) && - (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == - base_name_len)) { - /* If we did not yet encounter exact same name as the given one, check the remaining - * parts of the strings. */ - if (!is_orig_name_used) { - is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len); - } - /* Mark number of current id_test name as used, if possible. */ - if (number_test < MAX_NUMBERS_IN_USE) { - ids_in_use[number_test] = id_test; - } - /* Keep track of first largest unused number. */ - if (number <= number_test) { - *r_id_sorting_hint = id_test; - number = number_test + 1; - } - } - } - - /* If there is no double, we are done. - * Note however that name might have been changed (truncated) in a previous iteration - * already. - */ - if (!is_orig_name_used) { - /* Don't bother updating `prev_*` static variables here, this case is not supposed to happen - * that often, and is not straight-forward here, so just ignore and reset them to default. */ - prev_id_type = ID_LINK_PLACEHOLDER; - prev_final_base_name[0] = '\0'; - prev_number = MIN_NUMBER - 1; - - /* Value set previously is meaningless in that case. */ - *r_id_sorting_hint = NULL; - - return is_name_changed; - } - - /* Decide which value of number to use, either the smallest unused one if possible, or - * default to the first largest unused one we got from previous loop. */ - for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) { - if (ids_in_use[i] == NULL) { - number = i; - if (i > 0) { - *r_id_sorting_hint = ids_in_use[i - 1]; - } - break; - } - } - /* At this point, number is either the lowest unused number within - * [MIN_NUMBER .. MAX_NUMBERS_IN_USE - 1], or 1 greater than the largest used number if all - * those low ones are taken. - * We can't be bothered to look for the lowest unused number beyond - * (MAX_NUMBERS_IN_USE - 1). - */ - /* We know for wure that name will be changed. */ - is_name_changed = true; - - /* If id_name_final_build helper returns false, it had to truncate further given name, hence - * we have to go over the whole check again. */ - if (!id_name_final_build(name, base_name, base_name_len, number)) { - /* We have to clear our list of small used numbers before we do the whole check again. */ - memset(ids_in_use, 0, sizeof(ids_in_use)); - - continue; - } - - /* Update `prev_*` static variables, in case next call is for the same type of IDs and with the - * same initial base name, we can skip a lot of above process. */ - prev_id_type = id_type; - strcpy(prev_final_base_name, base_name); - prev_number = number; - - return is_name_changed; - } - -#undef MAX_NUMBERS_IN_USE -} - -#undef MIN_NUMBER -#undef MAX_NUMBER - -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) +bool BKE_id_new_name_validate( + struct Main *bmain, ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; @@ -1693,22 +1448,10 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo BLI_str_utf8_invalid_strip(name, strlen(name)); } - ID *id_sorting_hint = NULL; - result = check_for_dupid(lb, id, name, &id_sorting_hint); - strcpy(id->name + 2, name); - - /* This was in 2.43 and previous releases - * however all data in blender should be sorted, not just duplicate names - * sorting should not hurt, but noting just in case it alters the way other - * functions work, so sort every time. */ -#if 0 - if (result) { - id_sort_by_name(lb, id, id_sorting_hint); - } -#endif - - id_sort_by_name(lb, id, id_sorting_hint); + result = BKE_main_namemap_get_name(bmain, id, name); + strcpy(id->name + 2, name); + id_sort_by_name(lb, id, NULL); return result; } @@ -2074,7 +1817,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); if (idtest != NULL && !ID_IS_LINKED(idtest)) { /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL, false); + BKE_id_new_name_validate(bmain, lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2082,8 +1825,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { BLI_assert(!ID_IS_LINKED(id)); + BKE_main_namemap_remove_name(bmain, id, id->name + 2); ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name, false)) { + if (BKE_id_new_name_validate(bmain, lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index f14c11a949e..405b0be70f9 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -28,6 +28,7 @@ #include "BKE_lib_remap.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "lib_intern.h" @@ -151,6 +152,7 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { ListBase *lb = which_libbase(bmain, type); BLI_remlink(lb, id); + BKE_main_namemap_remove_name(bmain, id, id->name + 2); } BKE_libblock_free_data(id, (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0); @@ -237,6 +239,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) /* NOTE: in case we delete a library, we also delete all its datablocks! */ if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) { BLI_remlink(lb, id); + BKE_main_namemap_remove_name(bmain, id, id->name + 2); BLI_addtail(&tagged_deleted_ids, id); /* Do not tag as no_main now, we want to unlink it first (lower-level ID management * code has some specific handling of 'no main' IDs that would be a problem in that diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc index 7e75e0f5d93..98ac9110a48 100644 --- a/source/blender/blenkernel/intern/lib_id_remapper.cc +++ b/source/blender/blenkernel/intern/lib_id_remapper.cc @@ -36,6 +36,7 @@ struct IDRemapper { BLI_assert(old_id != nullptr); BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name))); mappings.add(old_id, new_id); + BLI_assert(BKE_idtype_idcode_to_idfilter(GS(old_id->name)) != 0); source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name)); } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index d6101d71be5..ea3f5395f1f 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -10,6 +10,7 @@ #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "DNA_ID.h" #include "DNA_mesh_types.h" @@ -18,19 +19,18 @@ namespace blender::bke::tests { struct LibIDMainSortTestContext { - Main *bmain; -}; - -static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx) -{ - BKE_idtype_init(); - ctx->bmain = BKE_main_new(); -} + Main *bmain = nullptr; -static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx) -{ - BKE_main_free(ctx->bmain); -} + LibIDMainSortTestContext() + { + BKE_idtype_init(); + bmain = BKE_main_new(); + } + ~LibIDMainSortTestContext() + { + BKE_main_free(bmain); + } +}; static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list) { @@ -47,8 +47,7 @@ static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list) TEST(lib_id_main_sort, local_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext 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")); @@ -57,14 +56,27 @@ TEST(lib_id_main_sort, local_ids_1) 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}); +} + +static void change_lib(Main *bmain, ID *id, Library *lib) +{ + if (id->lib == lib) { + return; + } + BKE_main_namemap_remove_name(bmain, id, id->name + 2); + id->lib = lib; +} - test_lib_id_main_sort_free(&ctx); +static void change_name(Main *bmain, ID *id, const char *name) +{ + BKE_main_namemap_remove_name(bmain, id, id->name + 2); + BLI_strncpy(id->name + 2, name, MAX_NAME); + BKE_id_new_name_validate(bmain, &bmain->objects, id, nullptr, true); } TEST(lib_id_main_sort, linked_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext 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")); @@ -92,14 +104,11 @@ TEST(lib_id_main_sort, linked_ids_1) 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); } TEST(lib_id_main_unique_name, local_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext 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")); @@ -107,21 +116,18 @@ TEST(lib_id_main_unique_name, local_ids_1) ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); test_lib_id_main_sort_check_order({id_a, id_b, id_c}); - BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_c, nullptr, false); - EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + change_name(ctx.bmain, id_c, "OB_A"); + + EXPECT_STREQ(id_c->name + 2, "OB_A.001"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_a); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_a, id_c, id_b}); - - test_lib_id_main_sort_free(&ctx); } TEST(lib_id_main_unique_name, linked_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext 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")); @@ -130,29 +136,276 @@ TEST(lib_id_main_unique_name, linked_ids_1) 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; + change_lib(ctx.bmain, id_a, lib_a); id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); - id_b->lib = lib_a; + change_lib(ctx.bmain, id_b, lib_a); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); - BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); - EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + + change_name(ctx.bmain, id_b, "OB_A"); + EXPECT_STREQ(id_b->name + 2, "OB_A.001"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); 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_b->lib = lib_b; + change_lib(ctx.bmain, id_b, lib_b); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); - BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); - EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + change_name(ctx.bmain, id_b, "OB_A"); + EXPECT_STREQ(id_b->name + 2, "OB_A"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); 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_unique_name, ids_sorted_by_default) +{ + LibIDMainSortTestContext ctx; + + ID *id_foo = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes")); + test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes}); +} + +static ID *add_id_in_library(Main *bmain, const char *name, Library *lib) +{ + ID *id = static_cast<ID *>(BKE_id_new(bmain, ID_OB, name)); + id->lib = lib; + id_sort_by_name(&bmain->objects, id, nullptr); + return id; +} + +TEST(lib_id_main_unique_name, ids_sorted_by_default_with_libraries) +{ + LibIDMainSortTestContext ctx; + + Library *lib_one = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LibOne")); + Library *lib_two = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LibTwo")); + + ID *id_foo = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + + ID *id_l1c = add_id_in_library(ctx.bmain, "C", lib_one); + ID *id_l2b = add_id_in_library(ctx.bmain, "B", lib_two); + ID *id_l1a = add_id_in_library(ctx.bmain, "A", lib_one); + + ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes")); + + test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes, id_l1a, id_l1c, id_l2b}); +} + +TEST(lib_id_main_unique_name, name_too_long_handling) +{ + LibIDMainSortTestContext ctx; + const char *name_a = "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_Truncated"; + const char *name_b = "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123456"; + const char *name_c = "Name_That_Has_Too_Long_Number_Suffix.1234567890"; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_a)); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_b)); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_c)); + + EXPECT_STREQ(id_a->name + 2, "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_"); + EXPECT_STREQ(id_b->name + 2, "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123"); + EXPECT_STREQ(id_c->name + 2, "Name_That_Has_Too_Long_Number_Suffix.1234567890"); /* Unchanged */ +} + +TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes) +{ + LibIDMainSortTestContext ctx; + + /* Create names where many of their numeric suffixes are + * the same number, yet the names are different and thus + * should be allowed as-is. */ + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + ID *id_d = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + ID *id_e = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + ID *id_f = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + ID *id_g = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + ID *id_h = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_i = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + ID *id_j = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + ID *id_k = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..000")); + + EXPECT_STREQ(id_a->name + 2, "Foo.123"); + EXPECT_STREQ(id_b->name + 2, "Foo.000"); + EXPECT_STREQ(id_c->name + 2, "Foo.003"); + EXPECT_STREQ(id_d->name + 2, "Foo.3"); + EXPECT_STREQ(id_e->name + 2, "Foo.0"); + EXPECT_STREQ(id_f->name + 2, "Foo."); + EXPECT_STREQ(id_g->name + 2, "Foo.0123"); + EXPECT_STREQ(id_h->name + 2, "Foo"); + EXPECT_STREQ(id_i->name + 2, "Foo.."); + EXPECT_STREQ(id_j->name + 2, "Foo..001"); + EXPECT_STREQ(id_k->name + 2, "Foo..000"); + + /* Now create their exact duplicates again, and check what happens. */ + id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + id_d = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + id_e = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + id_f = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + id_g = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + id_h = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + id_i = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + id_j = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + id_k = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..000")); + + EXPECT_STREQ(id_a->name + 2, "Foo.001"); + EXPECT_STREQ(id_b->name + 2, "Foo.002"); + EXPECT_STREQ(id_c->name + 2, "Foo.004"); + EXPECT_STREQ(id_d->name + 2, "Foo.005"); + EXPECT_STREQ(id_e->name + 2, "Foo.006"); + EXPECT_STREQ(id_f->name + 2, "Foo..002"); + EXPECT_STREQ(id_g->name + 2, "Foo.007"); + EXPECT_STREQ(id_h->name + 2, "Foo.008"); + EXPECT_STREQ(id_i->name + 2, "Foo...001"); + EXPECT_STREQ(id_j->name + 2, "Foo..003"); + EXPECT_STREQ(id_k->name + 2, "Foo..004"); +} + +TEST(lib_id_main_unique_name, zero_suffix_is_never_assigned) +{ + LibIDMainSortTestContext ctx; + + /* Creating these should assign 002 to the first one, but the next + * ones should start numbers starting from 1: 001 and 003. */ + ID *id_002 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_001 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_003 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + + EXPECT_STREQ(id_002->name + 2, "Foo.002"); + EXPECT_STREQ(id_001->name + 2, "Foo.001"); + EXPECT_STREQ(id_003->name + 2, "Foo.003"); +} + +TEST(lib_id_main_unique_name, remove_after_dup_get_original_name) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo.001"); + BKE_id_free(ctx.bmain, id_a); + + id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_a->name + 2, "Foo"); +} + +TEST(lib_id_main_unique_name, name_number_suffix_assignment) +{ + LibIDMainSortTestContext ctx; + + /* Create <1k objects first. */ + const int total_object_count = 1200; + ID *ids[total_object_count] = {}; + for (int i = 0; i < total_object_count / 2; ++i) { + ids[i] = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + } + + /* They should get assigned sequential numeric suffixes. */ + EXPECT_STREQ(ids[0]->name + 2, "Foo"); + EXPECT_STREQ(ids[1]->name + 2, "Foo.001"); + EXPECT_STREQ(ids[total_object_count / 2 - 1]->name + 2, "Foo.599"); + + /* Free some of the objects. */ + BKE_id_free(ctx.bmain, ids[10]); + BKE_id_free(ctx.bmain, ids[20]); + BKE_id_free(ctx.bmain, ids[30]); + + /* Create objects again; they should get suffixes that were just free'd up. */ + ID *id_010 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_010->name + 2, "Foo.010"); + ID *id_020 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + EXPECT_STREQ(id_020->name + 2, "Foo.020"); + /* Suffixes >1k do not get the "use the most proper free one" treatment. */ + ID *id_2000 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_2000->name + 2, "Foo.2000"); + /* But smaller than 1k suffixes do get proper empty spots. */ + ID *id_030 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_030->name + 2, "Foo.030"); + ID *id_600 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_600->name + 2, "Foo.600"); + + /* Max possible numeric suffix. */ + ID *id_max = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_max->name + 2, "Foo.999999999"); + /* Try with max. possible suffix again: will assign free suffix under 1k. */ + ID *id_max1 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_max1->name + 2, "Foo.601"); + + /* Now create the rest of objects, to use all the suffixes up to 1k. + * Once all the ones up to 1k are used, the logic will fall back to + * "use largest number seen + 1", but the largest one is already the max + * possible. So it will shorten the name part and restart the counter, + * i.e. "Fo.001". */ + for (int i = total_object_count / 2; i < total_object_count; ++i) { + ids[i] = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + } + /* At this point creating "Foo" based objects will fall always + * result in shortened name to "Fo". */ + ID *id_fo178 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_fo178->name + 2, "Fo.178"); + ID *id_fo179 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_fo179->name + 2, "Fo.179"); + ID *id_fo180 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_fo180->name + 2, "Fo.180"); +} + +TEST(lib_id_main_unique_name, renames_with_duplicates) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo.001"); + EXPECT_STREQ(id_c->name + 2, "Bar"); + + BKE_libblock_rename(ctx.bmain, id_a, "Foo.002"); + EXPECT_STREQ(id_a->name + 2, "Foo.002"); + BKE_libblock_rename(ctx.bmain, id_b, "Bar"); + EXPECT_STREQ(id_b->name + 2, "Bar.001"); + BKE_libblock_rename(ctx.bmain, id_c, "Foo"); + EXPECT_STREQ(id_c->name + 2, "Foo"); + BKE_libblock_rename(ctx.bmain, id_b, "Bar"); + EXPECT_STREQ(id_b->name + 2, "Bar"); +} + +TEST(lib_id_main_unique_name, names_are_unique_per_id_type) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_CA, "Foo")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo"); /* Different types (OB & CA) can have the same name. */ + EXPECT_STREQ(id_c->name + 2, "Foo.001"); +} + +TEST(lib_id_main_unique_name, name_huge_number_suffix) +{ + LibIDMainSortTestContext ctx; - test_lib_id_main_sort_free(&ctx); + /* Use numeric suffix that is really large: should come through + * fine, since no duplicates with other names. */ + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "SuperLong.1234567890")); + EXPECT_STREQ(id_a->name + 2, "SuperLong.1234567890"); + /* Now create with the same name again: should get 001 suffix. */ + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "SuperLong.1234567890")); + EXPECT_STREQ(id_b->name + 2, "SuperLong.001"); } } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 22012662180..0c5f59be768 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -428,14 +428,42 @@ static void lib_override_prefill_newid_from_existing_overrides(Main *bmain, ID * { ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) && - id_iter->override_library->hierarchy_root == id_hierarchy_root) { - id_iter->override_library->reference->newid = id_iter; + ID *id = id_iter; + if (GS(id_iter->name) == ID_KE) { + id = reinterpret_cast<Key *>(id_iter)->from; + BLI_assert(id != nullptr); + } + if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && + id->override_library->hierarchy_root == id_hierarchy_root) { + id->override_library->reference->newid = id; + if (GS(id_iter->name) == ID_KE) { + Key *reference_key = BKE_key_from_id(id->override_library->reference); + if (reference_key != nullptr) { + reference_key->id.newid = id_iter; + } + } } } FOREACH_MAIN_ID_END; } +static void lib_override_remapper_overrides_add(IDRemapper *id_remapper, + ID *reference_id, + ID *local_id) +{ + BKE_id_remapper_add(id_remapper, reference_id, local_id); + + Key *reference_key, *local_key = nullptr; + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { + if (reference_id->newid != nullptr) { + local_key = BKE_key_from_id(reference_id->newid); + BLI_assert(local_key != nullptr); + } + + BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); + } +} + /* TODO: Make this static local function instead? API is becoming complex, and it's not used * outside of this file anyway. */ bool BKE_lib_override_library_create_from_tag(Main *bmain, @@ -544,6 +572,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, BLI_assert(id_hierarchy_root != nullptr); LinkNode *relinked_ids = nullptr; + IDRemapper *id_remapper = BKE_id_remapper_create(); /* Still checking the whole Main, that way we can tag other local IDs as needing to be * remapped to use newly created overriding IDs, if needed. */ ID *id; @@ -568,6 +597,14 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, * consider we should also relink it, as part of recursive resync. */ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != id_root_reference->lib) { BLI_linklist_prepend(&relinked_ids, other_id); + if (ID_IS_OVERRIDE_LIBRARY_REAL(other_id) && + other_id->override_library->hierarchy_root == id_hierarchy_root) { + reference_id = other_id->override_library->reference; + ID *local_id = reference_id->newid; + if (other_id == local_id) { + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); + } + } } if (other_id != id) { other_id->lib = id_root_reference->lib; @@ -575,7 +612,6 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, } FOREACH_MAIN_ID_END; - IDRemapper *id_remapper = BKE_id_remapper_create(); for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr; todo_id_iter = todo_id_iter->next) { reference_id = static_cast<ID *>(todo_id_iter->data); @@ -587,15 +623,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, local_id->override_library->hierarchy_root = id_hierarchy_root; - BKE_id_remapper_add(id_remapper, reference_id, local_id); - - Key *reference_key, *local_key = nullptr; - if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { - local_key = BKE_key_from_id(reference_id->newid); - BLI_assert(local_key != nullptr); - - BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); - } + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); } BKE_libblock_relink_multiple(bmain, @@ -687,6 +715,51 @@ static void lib_override_group_tag_data_clear(LibOverrideGroupTagData *data) memset(data, 0, sizeof(*data)); } +static void lib_override_hierarchy_dependencies_recursive_tag_from(LibOverrideGroupTagData *data) +{ + Main *bmain = data->bmain; + ID *id = data->id_root; + const bool is_override = data->is_override; + + if ((*(uint *)&id->tag & data->tag) == 0) { + /* This ID is not tagged, no reason to proceed further to its parents. */ + return; + } + + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); + BLI_assert(entry != nullptr); + + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_FROM) { + /* This ID has already been processed. */ + return; + } + /* This way we won't process again that ID, should we encounter it again through another + * relationship hierarchy. */ + entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_FROM; + + for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != nullptr; + from_id_entry = from_id_entry->next) { + if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { + /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) + * as actual dependencies. */ + continue; + } + /* We only consider IDs from the same library. */ + ID *from_id = from_id_entry->id_pointer.from; + if (from_id == nullptr || from_id->lib != id->lib || + (is_override && !ID_IS_OVERRIDE_LIBRARY(from_id))) { + /* IDs from different libraries, or non-override IDs in case we are processing overrides, + * are both barriers of dependency. */ + continue; + } + from_id->tag |= data->tag; + LibOverrideGroupTagData sub_data = *data; + sub_data.id_root = from_id; + lib_override_hierarchy_dependencies_recursive_tag_from(&sub_data); + } +} + /* Tag all IDs in dependency relationships within an override hierarchy/group. * * Requires existing `Main.relations`. @@ -698,18 +771,19 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa Main *bmain = data->bmain; ID *id = data->id_root; const bool is_override = data->is_override; + const bool is_resync = data->is_resync; MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); BLI_assert(entry != nullptr); - if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_TO) { /* This ID has already been processed. */ return (*(uint *)&id->tag & data->tag) != 0; } /* This way we won't process again that ID, should we encounter it again through another * relationship hierarchy. */ - entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; + entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_TO; for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr; to_id_entry = to_id_entry->next) { @@ -733,6 +807,15 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } } + /* If the current ID is/has been tagged for override above, then check its reversed dependencies + * (i.e. IDs that depend on the current one). + * + * This will cover e.g. the case where user override an armature, and would expect the mesh + * object deformed by that armature to also be overridden. */ + if ((*(uint *)&id->tag & data->tag) != 0 && !is_resync) { + lib_override_hierarchy_dependencies_recursive_tag_from(data); + } + return (*(uint *)&id->tag & data->tag) != 0; } @@ -882,11 +965,6 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) id_root->tag |= data->tag; } - /* Only objects and groups are currently considered as 'keys' in override hierarchies. */ - if (!ELEM(GS(id_root->name), ID_OB, ID_GR)) { - return; - } - /* Tag all collections and objects recursively. */ lib_override_linked_group_tag_recursive(data); @@ -1071,14 +1149,26 @@ static bool lib_override_library_create_do(Main *bmain, BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); + /* In case the operation is on an already partially overridden hierarchy, all existing overrides + * in that hierarchy need to be tagged for remapping from linked reference ID usages to newly + * created overrides ones. */ + if (id_hierarchy_root_reference->lib != id_root_reference->lib) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib); + + BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); + data.hierarchy_root_id = id_hierarchy_root_reference; + data.id_root = id_hierarchy_root_reference; + data.is_override = true; + lib_override_overrides_group_tag(&data); + } + BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); bool success = false; if (id_hierarchy_root_reference->lib != id_root_reference->lib) { - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); - BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == - id_root_reference->lib); success = BKE_lib_override_library_create_from_tag(bmain, owner_library, id_root_reference, @@ -1694,11 +1784,16 @@ static bool lib_override_library_resync(Main *bmain, id->tag |= LIB_TAG_MISSING; } - if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) { + if ((id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) { /* While this should not happen in typical cases (and won't be properly supported here), * user is free to do all kind of very bad things, including having different local * overrides of a same linked ID in a same hierarchy. */ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, nullptr); + + if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) { + continue; + } + ID *reference_id = id_override_library->reference; if (GS(reference_id->name) != GS(id->name)) { switch (GS(id->name)) { @@ -1716,11 +1811,16 @@ static bool lib_override_library_resync(Main *bmain, break; } } + 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. */ + continue; + } BLI_assert(GS(reference_id->name) == GS(id->name)); if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) { BLI_ghash_insert(linkedref_to_old_override, reference_id, id); - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || (id->tag & LIB_TAG_DOIT) == 0) { continue; } if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) { diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 5de8704e13b..a869bf4c4b0 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -391,8 +391,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) switch ((ID_Type)id_type_owner) { case ID_LI: - /* ID_LI doesn't exist as filter_id. */ - return 0; + return FILTER_ID_LI; case ID_SCE: return FILTER_ID_OB | FILTER_ID_WO | FILTER_ID_SCE | FILTER_ID_MC | FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_TXT | FILTER_ID_LS | FILTER_ID_MSK | FILTER_ID_SO | @@ -472,6 +471,8 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) /* Deprecated... */ return 0; } + + BLI_assert_unreachable(); return 0; } @@ -693,6 +694,13 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, * First recursively check all its valid users, if all of them can be tagged as * unused, then we can tag this ID as such too. */ bool has_valid_from_users = false; + /* Preemptively consider this ID as unused. That way if there is a loop of dependency leading + * back to it, it won't create a fake 'valid user' detection. + * NOTE: This can only only be done for a subset of IDs, some types are never 'indirectly + * unused', same for IDs with a fake user. */ + if ((id->flag & LIB_FAKEUSER) == 0 && !ELEM(GS(id->name), ID_SCE, ID_WM, ID_SCR, ID_WS, ID_LI)) { + id->tag |= tag; + } for (MainIDRelationsEntryItem *id_from_item = id_relations->from_ids; id_from_item != NULL; id_from_item = id_from_item->next) { if ((id_from_item->usage_flag & ignored_usages) != 0 || @@ -715,7 +723,11 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, break; } } - if (!has_valid_from_users) { + if (has_valid_from_users) { + /* This ID has 'valid' users, clear the 'tag as unused' preemptively set above. */ + id->tag &= ~tag; + } + else { /* This ID has no 'valid' users, tag it as unused. */ id->tag |= tag; if (r_num_tagged != NULL) { diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 246999a1179..28b0337d9a2 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -428,10 +428,13 @@ static void libblock_remap_data_update_tags(ID *old_id, ID *new_id, void *user_d } static void libblock_remap_reset_remapping_status_callback(ID *old_id, - ID *UNUSED(new_id), + ID *new_id, void *UNUSED(user_data)) { BKE_libblock_runtime_reset_remapping_status(old_id); + if (new_id != NULL) { + BKE_libblock_runtime_reset_remapping_status(new_id); + } } /** diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 03a17b2ecc5..dd58c9cc4fe 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -26,14 +26,26 @@ #include "BKE_lib_query.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_packedFile.h" /* Unused currently. */ // static CLG_LogRef LOG = {.identifier = "bke.library"}; +struct BlendWriter; +struct BlendDataReader; + +static void library_runtime_reset(Library *lib) +{ + if (lib->runtime.name_map) { + BKE_main_namemap_destroy(&lib->runtime.name_map); + } +} + static void library_free_data(ID *id) { Library *library = (Library *)id; + library_runtime_reset(library); if (library->packedfile) { BKE_packedfile_free(library->packedfile); } @@ -61,9 +73,15 @@ static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data) } } +static void library_blend_read_data(struct BlendDataReader *UNUSED(reader), ID *id) +{ + Library *lib = (Library *)id; + lib->runtime.name_map = NULL; +} + IDTypeInfo IDType_ID_LI = { .id_code = ID_LI, - .id_filter = 0, + .id_filter = FILTER_ID_LI, .main_listbase_index = INDEX_ID_LI, .struct_size = sizeof(Library), .name = "Library", @@ -82,7 +100,7 @@ IDTypeInfo IDType_ID_LI = { .owner_get = NULL, .blend_write = NULL, - .blend_read_data = NULL, + .blend_read_data = library_blend_read_data, .blend_read_lib = NULL, .blend_read_expand = NULL, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index b9ed783fa8c..239aacf28d6 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -24,6 +24,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_main_idmap.h" +#include "BKE_main_namemap.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -184,6 +185,10 @@ void BKE_main_free(Main *mainvar) BKE_main_idmap_destroy(mainvar->id_map); } + if (mainvar->name_map) { + BKE_main_namemap_destroy(&mainvar->name_map); + } + BLI_spin_end((SpinLock *)mainvar->lock); MEM_freeN(mainvar->lock); MEM_freeN(mainvar); diff --git a/source/blender/blenkernel/intern/main_namemap.cc b/source/blender/blenkernel/intern/main_namemap.cc new file mode 100644 index 00000000000..00115d2a0be --- /dev/null +++ b/source/blender/blenkernel/intern/main_namemap.cc @@ -0,0 +1,365 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_idtype.h" +#include "BKE_main.h" +#include "BKE_main_namemap.h" + +#include "BLI_assert.h" +#include "BLI_bitmap.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_math_base.hh" +#include "BLI_set.hh" +#include "BLI_string_utf8.h" +#include "BLI_string_utils.h" + +#include "DNA_ID.h" + +#include "MEM_guardedalloc.h" + +//#define DEBUG_PRINT_MEMORY_USAGE + +using namespace blender; + +/* Assumes and ensure that the suffix number can never go beyond 1 billion. */ +#define MAX_NUMBER 1000000000 +/* We do not want to get "name.000", so minimal number is 1. */ +#define MIN_NUMBER 1 + +/** + * Helper building final ID name from given base_name and number. + * + * If everything goes well and we do generate a valid final ID name in given name, we return + * true. In case the final name would overflow the allowed ID name length, or given number is + * bigger than maximum allowed value, we truncate further the base_name (and given name, which is + * assumed to have the same 'base_name' part), and return false. + */ +static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number) +{ + char number_str[11]; /* Dot + nine digits + NULL terminator. */ + size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number); + + /* If the number would lead to an overflow of the maximum ID name length, we need to truncate + * the base name part and do all the number checks again. */ + if (base_name_len + number_str_len >= MAX_NAME || number >= MAX_NUMBER) { + if (base_name_len + number_str_len >= MAX_NAME) { + base_name_len = MAX_NAME - number_str_len - 1; + } + else { + base_name_len--; + } + base_name[base_name_len] = '\0'; + + /* Code above may have generated invalid utf-8 string, due to raw truncation. + * Ensure we get a valid one now. */ + base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len); + + /* Also truncate orig name, and start the whole check again. */ + name[base_name_len] = '\0'; + return false; + } + + /* We have our final number, we can put it in name and exit the function. */ + BLI_strncpy(name + base_name_len, number_str, number_str_len + 1); + return true; +} + +/* Key used in set/map lookups: just a string name. */ +struct UniqueName_Key { + char name[MAX_NAME]; + uint64_t hash() const + { + return BLI_ghashutil_strhash_n(name, MAX_NAME); + } + bool operator==(const UniqueName_Key &o) const + { + return !BLI_ghashutil_strcmp(name, o.name); + } +}; + +/* Tracking of used numeric suffixes. For each base name: + * + * - Exactly track which of the lowest 1024 suffixes are in use, + * whenever there is a name collision we pick the lowest "unused" + * one. This is done with a bit map. + * - Above 1024, do not track them exactly, just track the maximum + * suffix value seen so far. Upon collision, assign number that is + * one larger. + */ +struct UniqueName_Value { + static constexpr unsigned max_exact_tracking = 1024; + BLI_BITMAP_DECLARE(mask, max_exact_tracking); + int max_value = 0; + + void mark_used(int number) + { + if (number >= 0 && number < max_exact_tracking) { + BLI_BITMAP_ENABLE(mask, number); + } + if (number < MAX_NUMBER) { + math::max_inplace(max_value, number); + } + } + + void mark_unused(int number) + { + if (number >= 0 && number < max_exact_tracking) { + BLI_BITMAP_DISABLE(mask, number); + } + if (number > 0 && number == max_value) { + --max_value; + } + } + + bool use_if_unused(int number) + { + if (number >= 0 && number < max_exact_tracking) { + if (!BLI_BITMAP_TEST_BOOL(mask, number)) { + BLI_BITMAP_ENABLE(mask, number); + math::max_inplace(max_value, number); + return true; + } + } + return false; + } + + int use_smallest_unused() + { + /* Find the smallest available one <1k. + * However we never want to pick zero ("none") suffix, even if it is + * available, e.g. if Foo.001 was used and we want to create another + * Foo.001, we should return Foo.002 and not Foo. + * So while searching, mark #0 as "used" to make sure we don't find it, + * and restore the value afterwards. */ + + BLI_bitmap prev_first = mask[0]; + mask[0] |= 1; + int result = BLI_bitmap_find_first_unset(mask, max_exact_tracking); + if (result >= 0) { + BLI_BITMAP_ENABLE(mask, result); + math::max_inplace(max_value, result); + } + mask[0] |= prev_first & 1; /* Restore previous value of #0 bit. */ + return result; + } +}; + +/* Tracking of names for a single ID type. */ +struct UniqueName_TypeMap { + /* Set of full names that are in use. */ + Set<UniqueName_Key> full_names; + /* For each base name (i.e. without numeric suffix), track the + * numeric suffixes that are in use. */ + Map<UniqueName_Key, UniqueName_Value> base_name_to_num_suffix; +}; + +struct UniqueName_Map { + UniqueName_TypeMap type_maps[INDEX_ID_MAX]; + + UniqueName_TypeMap *find_by_type(short id_type) + { + int index = BKE_idtype_idcode_to_index(id_type); + return index >= 0 ? &type_maps[index] : nullptr; + } +}; + +struct UniqueName_Map *BKE_main_namemap_create() +{ + struct UniqueName_Map *map = MEM_new<UniqueName_Map>(__func__); + return map; +} + +void BKE_main_namemap_destroy(struct UniqueName_Map **r_name_map) +{ +#ifdef DEBUG_PRINT_MEMORY_USAGE + int64_t size_sets = 0; + int64_t size_maps = 0; + for (const UniqueName_TypeMap &type_map : (*r_name_map)->type_maps) { + size_sets += type_map.full_names.size_in_bytes(); + size_maps += type_map.base_name_to_num_suffix.size_in_bytes(); + } + printf( + "NameMap memory usage: sets %.1fKB, maps %.1fKB\n", size_sets / 1024.0, size_maps / 1024.0); +#endif + MEM_delete<UniqueName_Map>(*r_name_map); + *r_name_map = nullptr; +} + +static void main_namemap_populate(UniqueName_Map *name_map, struct Main *bmain, ID *ignore_id) +{ + BLI_assert_msg(name_map != nullptr, "name_map should not be null"); + for (UniqueName_TypeMap &type_map : name_map->type_maps) { + type_map.base_name_to_num_suffix.clear(); + } + Library *library = ignore_id->lib; + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if ((id == ignore_id) || (id->lib != library)) { + continue; + } + UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id->name)); + BLI_assert(type_map != nullptr); + + /* Insert the full name into the set. */ + UniqueName_Key key; + BLI_strncpy(key.name, id->name + 2, MAX_NAME); + type_map->full_names.add(key); + + /* Get the name and number parts ("name.number"). */ + int number = MIN_NUMBER; + BLI_split_name_num(key.name, &number, id->name + 2, '.'); + + /* Get and update the entry for this base name. */ + UniqueName_Value &val = type_map->base_name_to_num_suffix.lookup_or_add_default(key); + val.mark_used(number); + } + FOREACH_MAIN_ID_END; +} + +/* Get the name map object used for the given Main/ID. + * Lazily creates and populates the contents of the name map, if ensure_created is true. + * Note: if the contents are populated, the name of the given ID itself is not added. */ +static UniqueName_Map *get_namemap_for(Main *bmain, ID *id, bool ensure_created) +{ + if (id->lib != nullptr) { + if (ensure_created && id->lib->runtime.name_map == nullptr) { + id->lib->runtime.name_map = BKE_main_namemap_create(); + main_namemap_populate(id->lib->runtime.name_map, bmain, id); + } + return id->lib->runtime.name_map; + } + if (ensure_created && bmain->name_map == nullptr) { + bmain->name_map = BKE_main_namemap_create(); + main_namemap_populate(bmain->name_map, bmain, id); + } + return bmain->name_map; +} + +bool BKE_main_namemap_get_name(struct Main *bmain, struct ID *id, char *name) +{ +#ifndef __GNUC__ /* GCC warns with `nonull-compare`. */ + BLI_assert(bmain != nullptr); + BLI_assert(id != nullptr); +#endif + UniqueName_Map *name_map = get_namemap_for(bmain, id, true); + BLI_assert(name_map != nullptr); + BLI_assert(strlen(name) < MAX_NAME); + UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id->name)); + BLI_assert(type_map != nullptr); + + bool is_name_changed = false; + + UniqueName_Key key; + while (true) { + /* Check if the full original name has a duplicate. */ + BLI_strncpy(key.name, name, MAX_NAME); + const bool has_dup = type_map->full_names.contains(key); + + /* Get the name and number parts ("name.number"). */ + int number = MIN_NUMBER; + size_t base_name_len = BLI_split_name_num(key.name, &number, name, '.'); + + bool added_new = false; + UniqueName_Value &val = type_map->base_name_to_num_suffix.lookup_or_add_cb(key, [&]() { + added_new = true; + return UniqueName_Value(); + }); + if (added_new || !has_dup) { + /* This base name is not used at all yet, or the full original + * name has no duplicates. The latter could happen if splitting + * by number would produce the same values, for different name + * strings (e.g. Foo.001 and Foo.1). */ + val.mark_used(number); + + if (!has_dup) { + BLI_strncpy(key.name, name, MAX_NAME); + type_map->full_names.add(key); + } + return is_name_changed; + } + + /* The base name is already used. But our number suffix might not be used yet. */ + int number_to_use = -1; + if (val.use_if_unused(number)) { + /* Our particular number suffix is not used yet: use it. */ + number_to_use = number; + } + else { + /* Find lowest free under 1k and use it. */ + number_to_use = val.use_smallest_unused(); + + /* Did not find one under 1k. */ + if (number_to_use == -1) { + if (number >= MIN_NUMBER && number > val.max_value) { + val.max_value = number; + number_to_use = number; + } + else { + val.max_value++; + number_to_use = val.max_value; + } + } + } + + /* Try to build final name from the current base name and the number. + * Note that this can fail due to too long base name, or a too large number, + * in which case it will shorten the base name, and we'll start again. */ + BLI_assert(number_to_use >= MIN_NUMBER); + if (id_name_final_build(name, key.name, base_name_len, number_to_use)) { + /* All good, add final name to the set. */ + BLI_strncpy(key.name, name, MAX_NAME); + type_map->full_names.add(key); + break; + } + + /* Name had to be truncated, or number too large: mark + * the output name as definitely changed, and proceed with the + * truncated name again. */ + is_name_changed = true; + } + return is_name_changed; +} + +void BKE_main_namemap_remove_name(struct Main *bmain, struct ID *id, const char *name) +{ +#ifndef __GNUC__ /* GCC warns with `nonull-compare`. */ + BLI_assert(bmain != nullptr); + BLI_assert(id != nullptr); + BLI_assert(name != nullptr); +#endif + /* Name is empty or not initialized yet, nothing to remove. */ + if (name[0] == '\0') { + return; + } + + struct UniqueName_Map *name_map = get_namemap_for(bmain, id, false); + if (name_map == nullptr) { + return; + } + BLI_assert(strlen(name) < MAX_NAME); + UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id->name)); + BLI_assert(type_map != nullptr); + + UniqueName_Key key; + /* Remove full name from the set. */ + BLI_strncpy(key.name, name, MAX_NAME); + type_map->full_names.remove(key); + + int number = MIN_NUMBER; + BLI_split_name_num(key.name, &number, name, '.'); + UniqueName_Value *val = type_map->base_name_to_num_suffix.lookup_ptr(key); + if (val == nullptr) { + return; + } + if (number == 0 && val->max_value == 0) { + /* This was the only base name usage, remove whole key. */ + type_map->base_name_to_num_suffix.remove(key); + return; + } + val->mark_unused(number); +} diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 04a07fb42be..f899901b54e 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -954,7 +954,8 @@ void BKE_id_material_assign(Main *bmain, ID *id, Material *ma, short act) BKE_objects_materials_test_all(bmain, id); } -void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type) +static void object_material_assign( + Main *bmain, Object *ob, Material *ma, short act, int assign_type, bool do_test_all) { Material *mao, **matar, ***matarar; short *totcolp; @@ -1037,7 +1038,10 @@ void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act id_us_min(&mao->id); } (*matarar)[act - 1] = ma; - BKE_objects_materials_test_all(bmain, ob->data); /* Data may be used by several objects... */ + /* Data may be used by several objects. */ + if (do_test_all) { + BKE_objects_materials_test_all(bmain, ob->data); + } } if (ma) { @@ -1045,6 +1049,19 @@ void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act } } +void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type) +{ + object_material_assign(bmain, ob, ma, act, assign_type, true); +} + +void BKE_object_material_assign_single_obdata(struct Main *bmain, + struct Object *ob, + struct Material *ma, + short act) +{ + object_material_assign(bmain, ob, ma, act, BKE_MAT_ASSIGN_OBDATA, false); +} + void BKE_object_material_remap(Object *ob, const unsigned int *remap) { Material ***matar = BKE_object_material_array_p(ob); diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 1340e53f06e..2a1c940493c 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -347,7 +347,7 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase) return orcodata; } -bool BKE_mball_is_basis(Object *ob) +bool BKE_mball_is_basis(const Object *ob) { /* Meta-Ball Basis Notes from Blender-2.5x * ======================================= @@ -370,7 +370,7 @@ bool BKE_mball_is_basis(Object *ob) return (!isdigit(ob->id.name[len - 1])); } -bool BKE_mball_is_basis_for(Object *ob1, Object *ob2) +bool BKE_mball_is_same_group(const Object *ob1, const Object *ob2) { int basis1nr, basis2nr; char basis1name[MAX_ID_NAME], basis2name[MAX_ID_NAME]; @@ -383,11 +383,12 @@ bool BKE_mball_is_basis_for(Object *ob1, Object *ob2) BLI_split_name_num(basis1name, &basis1nr, ob1->id.name + 2, '.'); BLI_split_name_num(basis2name, &basis2nr, ob2->id.name + 2, '.'); - if (STREQ(basis1name, basis2name)) { - return BKE_mball_is_basis(ob1); - } + return STREQ(basis1name, basis2name); +} - return false; +bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2) +{ + return BKE_mball_is_same_group(ob1, ob2) && BKE_mball_is_basis(ob1); } bool BKE_mball_is_any_selected(const MetaBall *mb) @@ -422,41 +423,85 @@ bool BKE_mball_is_any_unselected(const MetaBall *mb) return false; } -void BKE_mball_properties_copy(Scene *scene, Object *active_object) +static void mball_data_properties_copy(MetaBall *mb_dst, MetaBall *mb_src) { - Scene *sce_iter = scene; - Base *base; - Object *ob; - MetaBall *active_mball = (MetaBall *)active_object->data; - int basisnr, obnr; - char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; - SceneBaseIter iter; - - BLI_split_name_num(basisname, &basisnr, active_object->id.name + 2, '.'); - - /* Pass depsgraph as NULL, which means we will not expand into - * duplis unlike when we generate the meta-ball. Expanding duplis - * would not be compatible when editing multiple view layers. */ - BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 0, NULL, NULL); - while (BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 1, &base, &ob)) { - if (ob->type == OB_MBALL) { - if (ob != active_object) { - BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); - - /* Object ob has to be in same "group" ... it means, that it has to have - * same base of its name */ - if (STREQ(obname, basisname)) { - MetaBall *mb = ob->data; - - /* Copy properties from selected/edited metaball */ - mb->wiresize = active_mball->wiresize; - mb->rendersize = active_mball->rendersize; - mb->thresh = active_mball->thresh; - mb->flag = active_mball->flag; - DEG_id_tag_update(&mb->id, 0); - } + mb_dst->wiresize = mb_src->wiresize; + mb_dst->rendersize = mb_src->rendersize; + mb_dst->thresh = mb_src->thresh; + mb_dst->flag = mb_src->flag; + DEG_id_tag_update(&mb_dst->id, 0); +} + +void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) +{ + /** + * WARNING: This code does not cover all potential corner-cases. E.g. if: + * <pre> + * | Object | ObData | + * | ---------- | ---------- | + * | Meta_A | Meta_A | + * | Meta_A.001 | Meta_A.001 | + * | Meta_B | Meta_A | + * | Meta_B.001 | Meta_B.001 | + * </pre> + * + * Calling this function with `metaball_src` being `Meta_A.001` will update `Meta_A`, but NOT + * `Meta_B.001`. So in the 'Meta_B' family, the two metaballs will have unmatching settings now. + * + * Solving this case would drastically increase the complexity of this code though, so don't + * think it would be worth it. + */ + for (Object *ob_src = bmain->objects.first; ob_src != NULL && !ID_IS_LINKED(ob_src);) { + if (ob_src->data != metaball_src) { + ob_src = ob_src->id.next; + continue; + } + + /* In this code we take advantage of two facts: + * - MetaBalls of the same family have the same basis name, + * - IDs are sorted by name in their Main listbase. + * So, all MetaBall objects of the same family are contiguous in bmain list (potentially mixed + * with non-meta-ball objects with same basis names). + * + * Using this, it is possible to process the whole set of meta-balls with a single loop on the + * whole list of Objects, though additionally going backward on part of the list in some cases. + */ + Object *ob_iter = NULL; + int obactive_nr, ob_nr; + char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME]; + BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.'); + + for (ob_iter = ob_src->id.prev; ob_iter != NULL; ob_iter = ob_iter->id.prev) { + if (ob_iter->id.name[2] != obactive_name[0]) { + break; + } + if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { + continue; + } + BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); + if (!STREQ(obactive_name, ob_name)) { + break; + } + + mball_data_properties_copy(ob_iter->data, metaball_src); + } + + for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) { + if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) { + break; + } + if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { + continue; } + BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); + if (!STREQ(obactive_name, ob_name)) { + break; + } + + mball_data_properties_copy(ob_iter->data, metaball_src); } + + ob_src = ob_iter; } } diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index ffbd824712a..cf05dc0404e 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -47,6 +47,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" @@ -755,7 +756,7 @@ static void mesh_ensure_tessellation_customdata(Mesh *me) if (tottex_tessface != tottex_original || totcol_tessface != totcol_original) { BKE_mesh_tessface_clear(me); - CustomData_from_bmeshpoly(&me->fdata, &me->ldata, me->totface); + BKE_mesh_add_mface_layers(&me->fdata, &me->ldata, me->totface); /* TODO: add some `--debug-mesh` option. */ if (G.debug & G_DEBUG) { @@ -1189,6 +1190,11 @@ static void ensure_orig_index_layer(CustomData &data, const int size) void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) { BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + BKE_mesh_ensure_default_orig_index_customdata_no_check(mesh); +} + +void BKE_mesh_ensure_default_orig_index_customdata_no_check(Mesh *mesh) +{ ensure_orig_index_layer(mesh->vdata, mesh->totvert); ensure_orig_index_layer(mesh->edata, mesh->totedge); ensure_orig_index_layer(mesh->pdata, mesh->totpoly); @@ -1352,74 +1358,6 @@ void BKE_mesh_orco_ensure(Object *ob, Mesh *mesh) CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_ASSIGN, orcodata, mesh->totvert); } -int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata, int mfindex, int nr) -{ - /* first test if the face is legal */ - if ((mface->v3 || nr == 4) && mface->v3 == mface->v4) { - mface->v4 = 0; - nr--; - } - if ((mface->v2 || mface->v4) && mface->v2 == mface->v3) { - mface->v3 = mface->v4; - mface->v4 = 0; - nr--; - } - if (mface->v1 == mface->v2) { - mface->v2 = mface->v3; - mface->v3 = mface->v4; - mface->v4 = 0; - nr--; - } - - /* Check corrupt cases, bow-tie geometry, - * can't handle these because edge data won't exist so just return 0. */ - if (nr == 3) { - if ( - /* real edges */ - mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v1) { - return 0; - } - } - else if (nr == 4) { - if ( - /* real edges */ - mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v4 || - mface->v4 == mface->v1 || - /* across the face */ - mface->v1 == mface->v3 || mface->v2 == mface->v4) { - return 0; - } - } - - /* prevent a zero at wrong index location */ - if (nr == 3) { - if (mface->v3 == 0) { - static int corner_indices[4] = {1, 2, 0, 3}; - - SWAP(uint, mface->v1, mface->v2); - SWAP(uint, mface->v2, mface->v3); - - if (fdata) { - CustomData_swap_corners(fdata, mfindex, corner_indices); - } - } - } - else if (nr == 4) { - if (mface->v3 == 0 || mface->v4 == 0) { - static int corner_indices[4] = {2, 3, 0, 1}; - - SWAP(uint, mface->v1, mface->v3); - SWAP(uint, mface->v2, mface->v4); - - if (fdata) { - CustomData_swap_corners(fdata, mfindex, corner_indices); - } - } - } - - return nr; -} - Mesh *BKE_mesh_from_object(Object *ob) { if (ob == nullptr) { @@ -1709,13 +1647,6 @@ void BKE_mesh_translate(Mesh *me, const float offset[3], const bool do_keys) BKE_mesh_tag_coords_changed_uniformly(me); } -void BKE_mesh_tessface_ensure(Mesh *mesh) -{ - if (mesh->totpoly && mesh->totface == 0) { - BKE_mesh_tessface_calc(mesh); - } -} - void BKE_mesh_tessface_clear(Mesh *mesh) { mesh_tessface_clear_intern(mesh, true); @@ -1922,9 +1853,25 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh, BKE_mesh_tag_coords_changed(mesh); } -void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spacearr) +static float (*ensure_corner_normal_layer(Mesh &mesh))[3] { float(*r_loopnors)[3]; + if (CustomData_has_layer(&mesh.ldata, CD_NORMAL)) { + r_loopnors = (float(*)[3])CustomData_get_layer(&mesh.ldata, CD_NORMAL); + memset(r_loopnors, 0, sizeof(float[3]) * mesh.totloop); + } + else { + r_loopnors = (float(*)[3])CustomData_add_layer( + &mesh.ldata, CD_NORMAL, CD_CALLOC, nullptr, mesh.totloop); + CustomData_set_layer_flag(&mesh.ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + } + return r_loopnors; +} + +void BKE_mesh_calc_normals_split_ex(Mesh *mesh, + MLoopNorSpaceArray *r_lnors_spacearr, + float (*r_corner_normals)[3]) +{ short(*clnors)[2] = nullptr; /* Note that we enforce computing clnors when the clnor space array is requested by caller here. @@ -1934,16 +1881,6 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac ((mesh->flag & ME_AUTOSMOOTH) != 0); const float split_angle = (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : (float)M_PI; - if (CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { - r_loopnors = (float(*)[3])CustomData_get_layer(&mesh->ldata, CD_NORMAL); - memset(r_loopnors, 0, sizeof(float[3]) * mesh->totloop); - } - else { - r_loopnors = (float(*)[3])CustomData_add_layer( - &mesh->ldata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totloop); - CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); - } - /* may be nullptr */ clnors = (short(*)[2])CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); @@ -1953,7 +1890,7 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac mesh->medge, mesh->totedge, mesh->mloop, - r_loopnors, + r_corner_normals, mesh->totloop, mesh->mpoly, BKE_mesh_poly_normals_ensure(mesh), @@ -1969,7 +1906,7 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac void BKE_mesh_calc_normals_split(Mesh *mesh) { - BKE_mesh_calc_normals_split_ex(mesh, nullptr); + BKE_mesh_calc_normals_split_ex(mesh, nullptr, ensure_corner_normal_layer(*mesh)); } /* Split faces helper functions. */ @@ -2188,7 +2125,7 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) MLoopNorSpaceArray lnors_spacearr = {nullptr}; /* Compute loop normals and loop normal spaces (a.k.a. smooth fans of faces around vertices). */ - BKE_mesh_calc_normals_split_ex(mesh, &lnors_spacearr); + BKE_mesh_calc_normals_split_ex(mesh, &lnors_spacearr, ensure_corner_normal_layer(*mesh)); /* Stealing memarena from loop normals space array. */ MemArena *memarena = lnors_spacearr.mem; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 9cb3e684667..923d2703960 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -751,6 +751,8 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) { + using namespace blender; + BLI_assert(me != nullptr); pointcloud->totpoint = me->totvert; @@ -758,14 +760,17 @@ void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) /* Copy over all attributes. */ CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert); - BKE_pointcloud_update_customdata_pointers(pointcloud); - CustomData_update_typemap(&pointcloud->pdata); - MVert *mvert; - mvert = me->mvert; - for (int i = 0; i < me->totvert; i++, mvert++) { - copy_v3_v3(pointcloud->co[i], mvert->co); - } + bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(*me); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); + + const VArray<float3> mesh_positions = mesh_attributes.lookup_or_default<float3>( + "position", ATTR_DOMAIN_POINT, float3(0)); + bke::SpanAttributeWriter<float3> point_positions = + point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); + mesh_positions.materialize(point_positions.span); + point_positions.finish(); } void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob) @@ -1209,9 +1214,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); /* Anonymous attributes shouldn't exist on original data. */ - MeshComponent component; - component.replace(mesh_in_bmain, GeometryOwnershipType::Editable); - component.attributes_remove_anonymous(); + blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous(); /* User-count is required because so far mesh was in a limbo, where library management does * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index de0489d668f..7d26262a504 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -632,278 +632,6 @@ void BKE_mesh_calc_volume(const MVert *mverts, /** \} */ -/* -------------------------------------------------------------------- */ -/** \name NGon Tessellation (NGon to MFace Conversion) - * \{ */ - -static void bm_corners_to_loops_ex(ID *id, - CustomData *fdata, - CustomData *ldata, - MFace *mface, - int totloop, - int findex, - int loopstart, - int numTex, - int numCol) -{ - MFace *mf = mface + findex; - - for (int i = 0; i < numTex; i++) { - const MTFace *texface = (const MTFace *)CustomData_get_n(fdata, CD_MTFACE, findex, i); - - MLoopUV *mloopuv = (MLoopUV *)CustomData_get_n(ldata, CD_MLOOPUV, loopstart, i); - copy_v2_v2(mloopuv->uv, texface->uv[0]); - mloopuv++; - copy_v2_v2(mloopuv->uv, texface->uv[1]); - mloopuv++; - copy_v2_v2(mloopuv->uv, texface->uv[2]); - mloopuv++; - - if (mf->v4) { - copy_v2_v2(mloopuv->uv, texface->uv[3]); - mloopuv++; - } - } - - for (int i = 0; i < numCol; i++) { - MLoopCol *mloopcol = (MLoopCol *)CustomData_get_n(ldata, CD_PROP_BYTE_COLOR, loopstart, i); - const MCol *mcol = (const MCol *)CustomData_get_n(fdata, CD_MCOL, findex, i); - - MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]); - mloopcol++; - MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[1]); - mloopcol++; - MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[2]); - mloopcol++; - if (mf->v4) { - MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[3]); - mloopcol++; - } - } - - if (CustomData_has_layer(fdata, CD_TESSLOOPNORMAL)) { - float(*lnors)[3] = (float(*)[3])CustomData_get(ldata, loopstart, CD_NORMAL); - const short(*tlnors)[3] = (short(*)[3])CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); - const int max = mf->v4 ? 4 : 3; - - for (int i = 0; i < max; i++, lnors++, tlnors++) { - normal_short_to_float_v3(*lnors, *tlnors); - } - } - - if (CustomData_has_layer(fdata, CD_MDISPS)) { - MDisps *ld = (MDisps *)CustomData_get(ldata, loopstart, CD_MDISPS); - const MDisps *fd = (const MDisps *)CustomData_get(fdata, findex, CD_MDISPS); - const float(*disps)[3] = fd->disps; - int tot = mf->v4 ? 4 : 3; - int corners; - - if (CustomData_external_test(fdata, CD_MDISPS)) { - if (id && fdata->external) { - CustomData_external_add(ldata, id, CD_MDISPS, totloop, fdata->external->filepath); - } - } - - corners = multires_mdisp_corners(fd); - - if (corners == 0) { - /* Empty #MDisp layers appear in at least one of the `sintel.blend` files. - * Not sure why this happens, but it seems fine to just ignore them here. - * If `corners == 0` for a non-empty layer though, something went wrong. */ - BLI_assert(fd->totdisp == 0); - } - else { - const int side = (int)sqrtf((float)(fd->totdisp / corners)); - const int side_sq = side * side; - - for (int i = 0; i < tot; i++, disps += side_sq, ld++) { - ld->totdisp = side_sq; - ld->level = (int)(logf((float)side - 1.0f) / (float)M_LN2) + 1; - - if (ld->disps) { - MEM_freeN(ld->disps); - } - - ld->disps = (float(*)[3])MEM_malloc_arrayN( - (size_t)side_sq, sizeof(float[3]), "converted loop mdisps"); - if (fd->disps) { - memcpy(ld->disps, disps, (size_t)side_sq * sizeof(float[3])); - } - else { - memset(ld->disps, 0, (size_t)side_sq * sizeof(float[3])); - } - } - } - } -} - -void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh) -{ - BKE_mesh_convert_mfaces_to_mpolys_ex(&mesh->id, - &mesh->fdata, - &mesh->ldata, - &mesh->pdata, - mesh->totedge, - mesh->totface, - mesh->totloop, - mesh->totpoly, - mesh->medge, - mesh->mface, - &mesh->totloop, - &mesh->totpoly, - &mesh->mloop, - &mesh->mpoly); - - BKE_mesh_update_customdata_pointers(mesh, true); -} - -void BKE_mesh_do_versions_convert_mfaces_to_mpolys(Mesh *mesh) -{ - BKE_mesh_convert_mfaces_to_mpolys_ex(&mesh->id, - &mesh->fdata, - &mesh->ldata, - &mesh->pdata, - mesh->totedge, - mesh->totface, - mesh->totloop, - mesh->totpoly, - mesh->medge, - mesh->mface, - &mesh->totloop, - &mesh->totpoly, - &mesh->mloop, - &mesh->mpoly); - - CustomData_bmesh_do_versions_update_active_layers(&mesh->fdata, &mesh->ldata); - - BKE_mesh_update_customdata_pointers(mesh, true); -} - -void BKE_mesh_convert_mfaces_to_mpolys_ex(ID *id, - CustomData *fdata, - CustomData *ldata, - CustomData *pdata, - int totedge_i, - int totface_i, - int totloop_i, - int totpoly_i, - MEdge *medge, - MFace *mface, - int *r_totloop, - int *r_totpoly, - MLoop **r_mloop, - MPoly **r_mpoly) -{ - MFace *mf; - MLoop *ml, *mloop; - MPoly *mp, *mpoly; - MEdge *me; - EdgeHash *eh; - int numTex, numCol; - int i, j, totloop, totpoly, *polyindex; - - /* old flag, clear to allow for reuse */ -#define ME_FGON (1 << 3) - - /* just in case some of these layers are filled in (can happen with python created meshes) */ - CustomData_free(ldata, totloop_i); - CustomData_free(pdata, totpoly_i); - - totpoly = totface_i; - mpoly = (MPoly *)MEM_calloc_arrayN((size_t)totpoly, sizeof(MPoly), "mpoly converted"); - CustomData_add_layer(pdata, CD_MPOLY, CD_ASSIGN, mpoly, totpoly); - - numTex = CustomData_number_of_layers(fdata, CD_MTFACE); - numCol = CustomData_number_of_layers(fdata, CD_MCOL); - - totloop = 0; - mf = mface; - for (i = 0; i < totface_i; i++, mf++) { - totloop += mf->v4 ? 4 : 3; - } - - mloop = (MLoop *)MEM_calloc_arrayN((size_t)totloop, sizeof(MLoop), "mloop converted"); - - CustomData_add_layer(ldata, CD_MLOOP, CD_ASSIGN, mloop, totloop); - - CustomData_to_bmeshpoly(fdata, ldata, totloop); - - if (id) { - /* ensure external data is transferred */ - /* TODO(sergey): Use multiresModifier_ensure_external_read(). */ - CustomData_external_read(fdata, id, CD_MASK_MDISPS, totface_i); - } - - eh = BLI_edgehash_new_ex(__func__, (uint)totedge_i); - - /* build edge hash */ - me = medge; - for (i = 0; i < totedge_i; i++, me++) { - BLI_edgehash_insert(eh, me->v1, me->v2, POINTER_FROM_UINT(i)); - - /* unrelated but avoid having the FGON flag enabled, - * so we can reuse it later for something else */ - me->flag &= ~ME_FGON; - } - - polyindex = (int *)CustomData_get_layer(fdata, CD_ORIGINDEX); - - j = 0; /* current loop index */ - ml = mloop; - mf = mface; - mp = mpoly; - for (i = 0; i < totface_i; i++, mf++, mp++) { - mp->loopstart = j; - - mp->totloop = mf->v4 ? 4 : 3; - - mp->mat_nr = mf->mat_nr; - mp->flag = mf->flag; - -#define ML(v1, v2) \ - { \ - ml->v = mf->v1; \ - ml->e = POINTER_AS_UINT(BLI_edgehash_lookup(eh, mf->v1, mf->v2)); \ - ml++; \ - j++; \ - } \ - (void)0 - - ML(v1, v2); - ML(v2, v3); - if (mf->v4) { - ML(v3, v4); - ML(v4, v1); - } - else { - ML(v3, v1); - } - -#undef ML - - bm_corners_to_loops_ex(id, fdata, ldata, mface, totloop, i, mp->loopstart, numTex, numCol); - - if (polyindex) { - *polyindex = i; - polyindex++; - } - } - - /* NOTE: we don't convert NGons at all, these are not even real ngons, - * they have their own UV's, colors etc - its more an editing feature. */ - - BLI_edgehash_free(eh, nullptr); - - *r_totpoly = totpoly; - *r_totloop = totloop; - *r_mpoly = mpoly; - *r_mloop = mloop; - -#undef ME_FGON -} - -/** \} */ - void BKE_mesh_mdisp_flip(MDisps *md, const bool use_loop_mdisp_flip) { if (UNLIKELY(!md->totdisp || !md->disps)) { diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc new file mode 100644 index 00000000000..479dd6a012a --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -0,0 +1,876 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + * + * Functions to convert mesh data to and from legacy formats like #MFace. + */ + +// #include <climits> + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_edgehash.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" +#include "BKE_multires.h" + +/* -------------------------------------------------------------------- */ +/** \name NGon Tessellation (NGon to MFace Conversion) + * \{ */ + +static void bm_corners_to_loops_ex(ID *id, + CustomData *fdata, + CustomData *ldata, + MFace *mface, + int totloop, + int findex, + int loopstart, + int numTex, + int numCol) +{ + MFace *mf = mface + findex; + + for (int i = 0; i < numTex; i++) { + const MTFace *texface = (const MTFace *)CustomData_get_n(fdata, CD_MTFACE, findex, i); + + MLoopUV *mloopuv = (MLoopUV *)CustomData_get_n(ldata, CD_MLOOPUV, loopstart, i); + copy_v2_v2(mloopuv->uv, texface->uv[0]); + mloopuv++; + copy_v2_v2(mloopuv->uv, texface->uv[1]); + mloopuv++; + copy_v2_v2(mloopuv->uv, texface->uv[2]); + mloopuv++; + + if (mf->v4) { + copy_v2_v2(mloopuv->uv, texface->uv[3]); + mloopuv++; + } + } + + for (int i = 0; i < numCol; i++) { + MLoopCol *mloopcol = (MLoopCol *)CustomData_get_n(ldata, CD_PROP_BYTE_COLOR, loopstart, i); + const MCol *mcol = (const MCol *)CustomData_get_n(fdata, CD_MCOL, findex, i); + + MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]); + mloopcol++; + MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[1]); + mloopcol++; + MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[2]); + mloopcol++; + if (mf->v4) { + MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[3]); + mloopcol++; + } + } + + if (CustomData_has_layer(fdata, CD_TESSLOOPNORMAL)) { + float(*lnors)[3] = (float(*)[3])CustomData_get(ldata, loopstart, CD_NORMAL); + const short(*tlnors)[3] = (short(*)[3])CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); + const int max = mf->v4 ? 4 : 3; + + for (int i = 0; i < max; i++, lnors++, tlnors++) { + normal_short_to_float_v3(*lnors, *tlnors); + } + } + + if (CustomData_has_layer(fdata, CD_MDISPS)) { + MDisps *ld = (MDisps *)CustomData_get(ldata, loopstart, CD_MDISPS); + const MDisps *fd = (const MDisps *)CustomData_get(fdata, findex, CD_MDISPS); + const float(*disps)[3] = fd->disps; + int tot = mf->v4 ? 4 : 3; + int corners; + + if (CustomData_external_test(fdata, CD_MDISPS)) { + if (id && fdata->external) { + CustomData_external_add(ldata, id, CD_MDISPS, totloop, fdata->external->filepath); + } + } + + corners = multires_mdisp_corners(fd); + + if (corners == 0) { + /* Empty #MDisp layers appear in at least one of the `sintel.blend` files. + * Not sure why this happens, but it seems fine to just ignore them here. + * If `corners == 0` for a non-empty layer though, something went wrong. */ + BLI_assert(fd->totdisp == 0); + } + else { + const int side = (int)sqrtf((float)(fd->totdisp / corners)); + const int side_sq = side * side; + + for (int i = 0; i < tot; i++, disps += side_sq, ld++) { + ld->totdisp = side_sq; + ld->level = (int)(logf((float)side - 1.0f) / (float)M_LN2) + 1; + + if (ld->disps) { + MEM_freeN(ld->disps); + } + + ld->disps = (float(*)[3])MEM_malloc_arrayN( + (size_t)side_sq, sizeof(float[3]), "converted loop mdisps"); + if (fd->disps) { + memcpy(ld->disps, disps, (size_t)side_sq * sizeof(float[3])); + } + else { + memset(ld->disps, 0, (size_t)side_sq * sizeof(float[3])); + } + } + } + } +} + +static void CustomData_to_bmeshpoly(CustomData *fdata, CustomData *ldata, int totloop) +{ + for (int i = 0; i < fdata->totlayer; i++) { + if (fdata->layers[i].type == CD_MTFACE) { + CustomData_add_layer_named( + ldata, CD_MLOOPUV, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); + } + else if (fdata->layers[i].type == CD_MCOL) { + CustomData_add_layer_named( + ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); + } + else if (fdata->layers[i].type == CD_MDISPS) { + CustomData_add_layer_named( + ldata, CD_MDISPS, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); + } + else if (fdata->layers[i].type == CD_TESSLOOPNORMAL) { + CustomData_add_layer_named( + ldata, CD_NORMAL, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); + } + } +} + +static void convert_mfaces_to_mpolys(ID *id, + CustomData *fdata, + CustomData *ldata, + CustomData *pdata, + int totedge_i, + int totface_i, + int totloop_i, + int totpoly_i, + MEdge *medge, + MFace *mface, + int *r_totloop, + int *r_totpoly, + MLoop **r_mloop, + MPoly **r_mpoly) +{ + MFace *mf; + MLoop *ml, *mloop; + MPoly *mp, *mpoly; + MEdge *me; + EdgeHash *eh; + int numTex, numCol; + int i, j, totloop, totpoly, *polyindex; + + /* old flag, clear to allow for reuse */ +#define ME_FGON (1 << 3) + + /* just in case some of these layers are filled in (can happen with python created meshes) */ + CustomData_free(ldata, totloop_i); + CustomData_free(pdata, totpoly_i); + + totpoly = totface_i; + mpoly = (MPoly *)MEM_calloc_arrayN((size_t)totpoly, sizeof(MPoly), "mpoly converted"); + CustomData_add_layer(pdata, CD_MPOLY, CD_ASSIGN, mpoly, totpoly); + + numTex = CustomData_number_of_layers(fdata, CD_MTFACE); + numCol = CustomData_number_of_layers(fdata, CD_MCOL); + + totloop = 0; + mf = mface; + for (i = 0; i < totface_i; i++, mf++) { + totloop += mf->v4 ? 4 : 3; + } + + mloop = (MLoop *)MEM_calloc_arrayN((size_t)totloop, sizeof(MLoop), "mloop converted"); + + CustomData_add_layer(ldata, CD_MLOOP, CD_ASSIGN, mloop, totloop); + + CustomData_to_bmeshpoly(fdata, ldata, totloop); + + if (id) { + /* ensure external data is transferred */ + /* TODO(sergey): Use multiresModifier_ensure_external_read(). */ + CustomData_external_read(fdata, id, CD_MASK_MDISPS, totface_i); + } + + eh = BLI_edgehash_new_ex(__func__, (uint)totedge_i); + + /* build edge hash */ + me = medge; + for (i = 0; i < totedge_i; i++, me++) { + BLI_edgehash_insert(eh, me->v1, me->v2, POINTER_FROM_UINT(i)); + + /* unrelated but avoid having the FGON flag enabled, + * so we can reuse it later for something else */ + me->flag &= ~ME_FGON; + } + + polyindex = (int *)CustomData_get_layer(fdata, CD_ORIGINDEX); + + j = 0; /* current loop index */ + ml = mloop; + mf = mface; + mp = mpoly; + for (i = 0; i < totface_i; i++, mf++, mp++) { + mp->loopstart = j; + + mp->totloop = mf->v4 ? 4 : 3; + + mp->mat_nr = mf->mat_nr; + mp->flag = mf->flag; + +#define ML(v1, v2) \ + { \ + ml->v = mf->v1; \ + ml->e = POINTER_AS_UINT(BLI_edgehash_lookup(eh, mf->v1, mf->v2)); \ + ml++; \ + j++; \ + } \ + (void)0 + + ML(v1, v2); + ML(v2, v3); + if (mf->v4) { + ML(v3, v4); + ML(v4, v1); + } + else { + ML(v3, v1); + } + +#undef ML + + bm_corners_to_loops_ex(id, fdata, ldata, mface, totloop, i, mp->loopstart, numTex, numCol); + + if (polyindex) { + *polyindex = i; + polyindex++; + } + } + + /* NOTE: we don't convert NGons at all, these are not even real ngons, + * they have their own UV's, colors etc - its more an editing feature. */ + + BLI_edgehash_free(eh, nullptr); + + *r_totpoly = totpoly; + *r_totloop = totloop; + *r_mpoly = mpoly; + *r_mloop = mloop; + +#undef ME_FGON +} + +void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh) +{ + convert_mfaces_to_mpolys(&mesh->id, + &mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->totedge, + mesh->totface, + mesh->totloop, + mesh->totpoly, + mesh->medge, + mesh->mface, + &mesh->totloop, + &mesh->totpoly, + &mesh->mloop, + &mesh->mpoly); + + BKE_mesh_update_customdata_pointers(mesh, true); +} + +/** + * Update active indices for active/render/clone/stencil custom data layers + * based on indices from fdata layers + * used when creating pdata and ldata for pre-bmesh + * meshes and needed to preserve active/render/clone/stencil flags set in pre-bmesh files. + */ +static void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, CustomData *ldata) +{ + int act; + + if (CustomData_has_layer(fdata, CD_MTFACE)) { + act = CustomData_get_active_layer(fdata, CD_MTFACE); + CustomData_set_layer_active(ldata, CD_MLOOPUV, act); + + act = CustomData_get_render_layer(fdata, CD_MTFACE); + CustomData_set_layer_render(ldata, CD_MLOOPUV, act); + + act = CustomData_get_clone_layer(fdata, CD_MTFACE); + CustomData_set_layer_clone(ldata, CD_MLOOPUV, act); + + act = CustomData_get_stencil_layer(fdata, CD_MTFACE); + CustomData_set_layer_stencil(ldata, CD_MLOOPUV, act); + } + + if (CustomData_has_layer(fdata, CD_MCOL)) { + act = CustomData_get_active_layer(fdata, CD_MCOL); + CustomData_set_layer_active(ldata, CD_PROP_BYTE_COLOR, act); + + act = CustomData_get_render_layer(fdata, CD_MCOL); + CustomData_set_layer_render(ldata, CD_PROP_BYTE_COLOR, act); + + act = CustomData_get_clone_layer(fdata, CD_MCOL); + CustomData_set_layer_clone(ldata, CD_PROP_BYTE_COLOR, act); + + act = CustomData_get_stencil_layer(fdata, CD_MCOL); + CustomData_set_layer_stencil(ldata, CD_PROP_BYTE_COLOR, act); + } +} + +void BKE_mesh_do_versions_convert_mfaces_to_mpolys(Mesh *mesh) +{ + convert_mfaces_to_mpolys(&mesh->id, + &mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->totedge, + mesh->totface, + mesh->totloop, + mesh->totpoly, + mesh->medge, + mesh->mface, + &mesh->totloop, + &mesh->totpoly, + &mesh->mloop, + &mesh->mpoly); + + CustomData_bmesh_do_versions_update_active_layers(&mesh->fdata, &mesh->ldata); + + BKE_mesh_update_customdata_pointers(mesh, true); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name MFace Tessellation + * + * #MFace is a legacy data-structure that should be avoided, use #MLoopTri instead. + * \{ */ + +/** + * Convert all CD layers from loop/poly to tessface data. + * + * \param loopindices: is an array of an int[4] per tessface, + * mapping tessface's verts to loops indices. + * + * \note when mface is not null, mface[face_index].v4 + * is used to test quads, else, loopindices[face_index][3] is used. + */ +static void mesh_loops_to_tessdata(CustomData *fdata, + CustomData *ldata, + MFace *mface, + const int *polyindices, + uint (*loopindices)[4], + const int num_faces) +{ + /* NOTE(mont29): performances are sub-optimal when we get a null #MFace, + * we could be ~25% quicker with dedicated code. + * The issue is, unless having two different functions with nearly the same code, + * there's not much ways to solve this. Better IMHO to live with it for now (sigh). */ + const int numUV = CustomData_number_of_layers(ldata, CD_MLOOPUV); + const int numCol = CustomData_number_of_layers(ldata, CD_PROP_BYTE_COLOR); + const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL); + const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP); + const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL); + const bool hasLoopTangent = CustomData_has_layer(ldata, CD_TANGENT); + int findex, i, j; + const int *pidx; + uint(*lidx)[4]; + + for (i = 0; i < numUV; i++) { + MTFace *texface = (MTFace *)CustomData_get_layer_n(fdata, CD_MTFACE, i); + const MLoopUV *mloopuv = (const MLoopUV *)CustomData_get_layer_n(ldata, CD_MLOOPUV, i); + + for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; + pidx++, lidx++, findex++, texface++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + copy_v2_v2(texface->uv[j], mloopuv[(*lidx)[j]].uv); + } + } + } + + for (i = 0; i < numCol; i++) { + MCol(*mcol)[4] = (MCol(*)[4])CustomData_get_layer_n(fdata, CD_MCOL, i); + const MLoopCol *mloopcol = (const MLoopCol *)CustomData_get_layer_n( + ldata, CD_PROP_BYTE_COLOR, i); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); + } + } + } + + if (hasPCol) { + MCol(*mcol)[4] = (MCol(*)[4])CustomData_get_layer(fdata, CD_PREVIEW_MCOL); + const MLoopCol *mloopcol = (const MLoopCol *)CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); + } + } + } + + if (hasOrigSpace) { + OrigSpaceFace *of = (OrigSpaceFace *)CustomData_get_layer(fdata, CD_ORIGSPACE); + const OrigSpaceLoop *lof = (const OrigSpaceLoop *)CustomData_get_layer(ldata, + CD_ORIGSPACE_MLOOP); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv); + } + } + } + + if (hasLoopNormal) { + short(*fnors)[4][3] = (short(*)[4][3])CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); + const float(*lnors)[3] = (const float(*)[3])CustomData_get_layer(ldata, CD_NORMAL); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + normal_float_to_short_v3((*fnors)[j], lnors[(*lidx)[j]]); + } + } + } + + if (hasLoopTangent) { + /* Need to do for all UV maps at some point. */ + float(*ftangents)[4] = (float(*)[4])CustomData_get_layer(fdata, CD_TANGENT); + const float(*ltangents)[4] = (const float(*)[4])CustomData_get_layer(ldata, CD_TANGENT); + + for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; + pidx++, lidx++, findex++) { + int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; + for (j = nverts; j--;) { + copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); + } + } + } +} + +int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata, int mfindex, int nr) +{ + /* first test if the face is legal */ + if ((mface->v3 || nr == 4) && mface->v3 == mface->v4) { + mface->v4 = 0; + nr--; + } + if ((mface->v2 || mface->v4) && mface->v2 == mface->v3) { + mface->v3 = mface->v4; + mface->v4 = 0; + nr--; + } + if (mface->v1 == mface->v2) { + mface->v2 = mface->v3; + mface->v3 = mface->v4; + mface->v4 = 0; + nr--; + } + + /* Check corrupt cases, bow-tie geometry, + * can't handle these because edge data won't exist so just return 0. */ + if (nr == 3) { + if ( + /* real edges */ + mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v1) { + return 0; + } + } + else if (nr == 4) { + if ( + /* real edges */ + mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v4 || + mface->v4 == mface->v1 || + /* across the face */ + mface->v1 == mface->v3 || mface->v2 == mface->v4) { + return 0; + } + } + + /* prevent a zero at wrong index location */ + if (nr == 3) { + if (mface->v3 == 0) { + static int corner_indices[4] = {1, 2, 0, 3}; + + SWAP(uint, mface->v1, mface->v2); + SWAP(uint, mface->v2, mface->v3); + + if (fdata) { + CustomData_swap_corners(fdata, mfindex, corner_indices); + } + } + } + else if (nr == 4) { + if (mface->v3 == 0 || mface->v4 == 0) { + static int corner_indices[4] = {2, 3, 0, 1}; + + SWAP(uint, mface->v1, mface->v3); + SWAP(uint, mface->v2, mface->v4); + + if (fdata) { + CustomData_swap_corners(fdata, mfindex, corner_indices); + } + } + } + + return nr; +} + +static int mesh_tessface_calc(CustomData *fdata, + CustomData *ldata, + CustomData *pdata, + MVert *mvert, + int totface, + int totloop, + int totpoly) +{ +#define USE_TESSFACE_SPEEDUP +#define USE_TESSFACE_QUADS + +/* We abuse #MFace.edcode to tag quad faces. See below for details. */ +#define TESSFACE_IS_QUAD 1 + + const int looptri_num = poly_to_tri_count(totpoly, totloop); + + const MPoly *mp, *mpoly; + const MLoop *ml, *mloop; + MFace *mface, *mf; + MemArena *arena = nullptr; + int *mface_to_poly_map; + uint(*lindices)[4]; + int poly_index, mface_index; + uint j; + + mpoly = (const MPoly *)CustomData_get_layer(pdata, CD_MPOLY); + mloop = (const MLoop *)CustomData_get_layer(ldata, CD_MLOOP); + + /* Allocate the length of `totfaces`, avoid many small reallocation's, + * if all faces are triangles it will be correct, `quads == 2x` allocations. */ + /* Take care since memory is _not_ zeroed so be sure to initialize each field. */ + mface_to_poly_map = (int *)MEM_malloc_arrayN( + (size_t)looptri_num, sizeof(*mface_to_poly_map), __func__); + mface = (MFace *)MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface), __func__); + lindices = (uint(*)[4])MEM_malloc_arrayN((size_t)looptri_num, sizeof(*lindices), __func__); + + mface_index = 0; + mp = mpoly; + for (poly_index = 0; poly_index < totpoly; poly_index++, mp++) { + const uint mp_loopstart = (uint)mp->loopstart; + const uint mp_totloop = (uint)mp->totloop; + uint l1, l2, l3, l4; + uint *lidx; + if (mp_totloop < 3) { + /* Do nothing. */ + } + +#ifdef USE_TESSFACE_SPEEDUP + +# define ML_TO_MF(i1, i2, i3) \ + mface_to_poly_map[mface_index] = poly_index; \ + mf = &mface[mface_index]; \ + lidx = lindices[mface_index]; \ + /* Set loop indices, transformed to vert indices later. */ \ + l1 = mp_loopstart + i1; \ + l2 = mp_loopstart + i2; \ + l3 = mp_loopstart + i3; \ + mf->v1 = mloop[l1].v; \ + mf->v2 = mloop[l2].v; \ + mf->v3 = mloop[l3].v; \ + mf->v4 = 0; \ + lidx[0] = l1; \ + lidx[1] = l2; \ + lidx[2] = l3; \ + lidx[3] = 0; \ + mf->mat_nr = mp->mat_nr; \ + mf->flag = mp->flag; \ + mf->edcode = 0; \ + (void)0 + +/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */ +# define ML_TO_MF_QUAD() \ + mface_to_poly_map[mface_index] = poly_index; \ + mf = &mface[mface_index]; \ + lidx = lindices[mface_index]; \ + /* Set loop indices, transformed to vert indices later. */ \ + l1 = mp_loopstart + 0; /* EXCEPTION */ \ + l2 = mp_loopstart + 1; /* EXCEPTION */ \ + l3 = mp_loopstart + 2; /* EXCEPTION */ \ + l4 = mp_loopstart + 3; /* EXCEPTION */ \ + mf->v1 = mloop[l1].v; \ + mf->v2 = mloop[l2].v; \ + mf->v3 = mloop[l3].v; \ + mf->v4 = mloop[l4].v; \ + lidx[0] = l1; \ + lidx[1] = l2; \ + lidx[2] = l3; \ + lidx[3] = l4; \ + mf->mat_nr = mp->mat_nr; \ + mf->flag = mp->flag; \ + mf->edcode = TESSFACE_IS_QUAD; \ + (void)0 + + else if (mp_totloop == 3) { + ML_TO_MF(0, 1, 2); + mface_index++; + } + else if (mp_totloop == 4) { +# ifdef USE_TESSFACE_QUADS + ML_TO_MF_QUAD(); + mface_index++; +# else + ML_TO_MF(0, 1, 2); + mface_index++; + ML_TO_MF(0, 2, 3); + mface_index++; +# endif + } +#endif /* USE_TESSFACE_SPEEDUP */ + else { + const float *co_curr, *co_prev; + + float normal[3]; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const uint totfilltri = mp_totloop - 2; + + if (UNLIKELY(arena == nullptr)) { + arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = (uint(*)[3])BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)totfilltri); + projverts = (float(*)[2])BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)mp_totloop); + + zero_v3(normal); + + /* Calculate the normal, flipped: to get a positive 2D cross product. */ + ml = mloop + mp_loopstart; + co_prev = mvert[ml[mp_totloop - 1].v].co; + for (j = 0; j < mp_totloop; j++, ml++) { + co_curr = mvert[ml->v].co; + add_newell_cross_v3_v3v3(normal, co_prev, co_curr); + co_prev = co_curr; + } + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } + + /* Project verts to 2D. */ + axis_dominant_v3_to_m3_negate(axis_mat, normal); + + ml = mloop + mp_loopstart; + for (j = 0; j < mp_totloop; j++, ml++) { + mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); + } + + BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena); + + /* Apply fill. */ + for (j = 0; j < totfilltri; j++) { + uint *tri = tris[j]; + lidx = lindices[mface_index]; + + mface_to_poly_map[mface_index] = poly_index; + mf = &mface[mface_index]; + + /* Set loop indices, transformed to vert indices later. */ + l1 = mp_loopstart + tri[0]; + l2 = mp_loopstart + tri[1]; + l3 = mp_loopstart + tri[2]; + + mf->v1 = mloop[l1].v; + mf->v2 = mloop[l2].v; + mf->v3 = mloop[l3].v; + mf->v4 = 0; + + lidx[0] = l1; + lidx[1] = l2; + lidx[2] = l3; + lidx[3] = 0; + + mf->mat_nr = mp->mat_nr; + mf->flag = mp->flag; + mf->edcode = 0; + + mface_index++; + } + + BLI_memarena_clear(arena); + } + } + + if (arena) { + BLI_memarena_free(arena); + arena = nullptr; + } + + CustomData_free(fdata, totface); + totface = mface_index; + + BLI_assert(totface <= looptri_num); + + /* Not essential but without this we store over-allocated memory in the #CustomData layers. */ + if (LIKELY(looptri_num != totface)) { + mface = (MFace *)MEM_reallocN(mface, sizeof(*mface) * (size_t)totface); + mface_to_poly_map = (int *)MEM_reallocN(mface_to_poly_map, + sizeof(*mface_to_poly_map) * (size_t)totface); + } + + CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mface, totface); + + /* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons + * they are directly tessellated from. */ + CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); + BKE_mesh_add_mface_layers(fdata, ldata, totface); + + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * Polygons take care of their loops ordering, hence not of their vertices ordering. + * Currently, our tfaces' fourth vertex index might be 0 even for a quad. + * However, we know our fourth loop index is never 0 for quads + * (because they are sorted for polygons, and our quads are still mere copies of their polygons). + * So we pass nullptr as MFace pointer, and #mesh_loops_to_tessdata + * will use the fourth loop index as quad test. */ + mesh_loops_to_tessdata(fdata, ldata, nullptr, mface_to_poly_map, lindices, totface); + + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * ...However, most TFace code uses 'MFace->v4 == 0' test to check whether it is a tri or quad. + * BKE_mesh_mface_index_validate() will check this and rotate the tessellated face if needed. + */ +#ifdef USE_TESSFACE_QUADS + mf = mface; + for (mface_index = 0; mface_index < totface; mface_index++, mf++) { + if (mf->edcode == TESSFACE_IS_QUAD) { + BKE_mesh_mface_index_validate(mf, fdata, mface_index, 4); + mf->edcode = 0; + } + } +#endif + + MEM_freeN(lindices); + + return totface; + +#undef USE_TESSFACE_SPEEDUP +#undef USE_TESSFACE_QUADS + +#undef ML_TO_MF +#undef ML_TO_MF_QUAD +} + +void BKE_mesh_tessface_calc(Mesh *mesh) +{ + mesh->totface = mesh_tessface_calc(&mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->mvert, + mesh->totface, + mesh->totloop, + mesh->totpoly); + + BKE_mesh_update_customdata_pointers(mesh, true); +} + +void BKE_mesh_tessface_ensure(struct Mesh *mesh) +{ + if (mesh->totpoly && mesh->totface == 0) { + BKE_mesh_tessface_calc(mesh); + } +} + +#ifndef NDEBUG +/** + * Debug check, used to assert when we expect layers to be in/out of sync. + * + * \param fallback: Use when there are no layers to handle, + * since callers may expect success or failure. + */ +static bool check_matching_legacy_layer_counts(CustomData *fdata, CustomData *ldata, bool fallback) +{ + int a_num = 0, b_num = 0; +# define LAYER_CMP(l_a, t_a, l_b, t_b) \ + ((a_num += CustomData_number_of_layers(l_a, t_a)) == \ + (b_num += CustomData_number_of_layers(l_b, t_b))) + + if (!LAYER_CMP(ldata, CD_MLOOPUV, fdata, CD_MTFACE)) { + return false; + } + if (!LAYER_CMP(ldata, CD_PROP_BYTE_COLOR, fdata, CD_MCOL)) { + return false; + } + if (!LAYER_CMP(ldata, CD_PREVIEW_MLOOPCOL, fdata, CD_PREVIEW_MCOL)) { + return false; + } + if (!LAYER_CMP(ldata, CD_ORIGSPACE_MLOOP, fdata, CD_ORIGSPACE)) { + return false; + } + if (!LAYER_CMP(ldata, CD_NORMAL, fdata, CD_TESSLOOPNORMAL)) { + return false; + } + if (!LAYER_CMP(ldata, CD_TANGENT, fdata, CD_TANGENT)) { + return false; + } + +# undef LAYER_CMP + + /* if no layers are on either CustomData's, + * then there was nothing to do... */ + return a_num ? true : fallback; +} +#endif + +void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total) +{ + /* avoid accumulating extra layers */ + BLI_assert(!check_matching_legacy_layer_counts(fdata, ldata, false)); + + for (int i = 0; i < ldata->totlayer; i++) { + if (ldata->layers[i].type == CD_MLOOPUV) { + CustomData_add_layer_named( + fdata, CD_MTFACE, CD_CALLOC, nullptr, total, ldata->layers[i].name); + } + if (ldata->layers[i].type == CD_PROP_BYTE_COLOR) { + CustomData_add_layer_named(fdata, CD_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name); + } + else if (ldata->layers[i].type == CD_PREVIEW_MLOOPCOL) { + CustomData_add_layer_named( + fdata, CD_PREVIEW_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name); + } + else if (ldata->layers[i].type == CD_ORIGSPACE_MLOOP) { + CustomData_add_layer_named( + fdata, CD_ORIGSPACE, CD_CALLOC, nullptr, total, ldata->layers[i].name); + } + else if (ldata->layers[i].type == CD_NORMAL) { + CustomData_add_layer_named( + fdata, CD_TESSLOOPNORMAL, CD_CALLOC, nullptr, total, ldata->layers[i].name); + } + else if (ldata->layers[i].type == CD_TANGENT) { + CustomData_add_layer_named( + fdata, CD_TANGENT, CD_CALLOC, nullptr, total, ldata->layers[i].name); + } + } + + CustomData_bmesh_update_active_layers(fdata, ldata); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc index adaf378ed27..7bc429954b0 100644 --- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -33,11 +33,11 @@ static int compare_v2_classify(const float uv_a[2], const float uv_b[2]) if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) { return CMP_EQUAL; } - /* Note that the ULP value is the primary value used to compare relative values - * as the absolute value doesn't account for float precision at difference scales. + /* NOTE(@campbellbarton): that the ULP value is the primary value used to compare relative + * values as the absolute value doesn't account for float precision at difference scales. * - For subdivision-surface ULP of 3 is sufficient, * although this value is extremely small. - * - For bevel the URL of 12 is sufficient to merge UV's that appear to be connected + * - For bevel the ULP of 12 is sufficient to merge UV's that appear to be connected * with bevel on Suzanne beveled 15% with 6 segments. * * These values could be tweaked but should be kept on the small side to prevent diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index b792c5c98b9..dd09a3d6917 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" #include "BKE_mesh_runtime.h" @@ -253,12 +252,12 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src, } } -void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, - OutputAttribute &dst_attribute, +void MeshAttributeInterpolator::sample_attribute(const GAttributeReader &src_attribute, + GSpanAttributeWriter &dst_attribute, eAttributeMapMode mode) { if (src_attribute && dst_attribute) { - this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span()); + this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.span); } } diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c index 71d5722cb0e..173fb98912b 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -32,370 +32,6 @@ #define MESH_FACE_TESSELLATE_THREADED_LIMIT 4096 /* -------------------------------------------------------------------- */ -/** \name MFace Tessellation - * - * #MFace is a legacy data-structure that should be avoided, use #MLoopTri instead. - * \{ */ - -/** - * Convert all CD layers from loop/poly to tessface data. - * - * \param loopindices: is an array of an int[4] per tessface, - * mapping tessface's verts to loops indices. - * - * \note when mface is not NULL, mface[face_index].v4 - * is used to test quads, else, loopindices[face_index][3] is used. - */ -static void mesh_loops_to_tessdata(CustomData *fdata, - CustomData *ldata, - MFace *mface, - const int *polyindices, - uint (*loopindices)[4], - const int num_faces) -{ - /* NOTE(mont29): performances are sub-optimal when we get a NULL #MFace, - * we could be ~25% quicker with dedicated code. - * The issue is, unless having two different functions with nearly the same code, - * there's not much ways to solve this. Better IMHO to live with it for now (sigh). */ - const int numUV = CustomData_number_of_layers(ldata, CD_MLOOPUV); - const int numCol = CustomData_number_of_layers(ldata, CD_PROP_BYTE_COLOR); - const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL); - const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP); - const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL); - const bool hasLoopTangent = CustomData_has_layer(ldata, CD_TANGENT); - int findex, i, j; - const int *pidx; - uint(*lidx)[4]; - - for (i = 0; i < numUV; i++) { - MTFace *texface = CustomData_get_layer_n(fdata, CD_MTFACE, i); - const MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); - - for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; - pidx++, lidx++, findex++, texface++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - copy_v2_v2(texface->uv[j], mloopuv[(*lidx)[j]].uv); - } - } - } - - for (i = 0; i < numCol; i++) { - MCol(*mcol)[4] = CustomData_get_layer_n(fdata, CD_MCOL, i); - const MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_PROP_BYTE_COLOR, i); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); - } - } - } - - if (hasPCol) { - MCol(*mcol)[4] = CustomData_get_layer(fdata, CD_PREVIEW_MCOL); - const MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); - } - } - } - - if (hasOrigSpace) { - OrigSpaceFace *of = CustomData_get_layer(fdata, CD_ORIGSPACE); - const OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv); - } - } - } - - if (hasLoopNormal) { - short(*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); - const float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); - - for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { - for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { - normal_float_to_short_v3((*fnors)[j], lnors[(*lidx)[j]]); - } - } - } - - if (hasLoopTangent) { - /* Need to do for all UV maps at some point. */ - float(*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); - const float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); - - for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; - pidx++, lidx++, findex++) { - int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; - for (j = nverts; j--;) { - copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); - } - } - } -} - -static int mesh_tessface_calc(CustomData *fdata, - CustomData *ldata, - CustomData *pdata, - MVert *mvert, - int totface, - int totloop, - int totpoly) -{ -#define USE_TESSFACE_SPEEDUP -#define USE_TESSFACE_QUADS - -/* We abuse #MFace.edcode to tag quad faces. See below for details. */ -#define TESSFACE_IS_QUAD 1 - - const int looptri_num = poly_to_tri_count(totpoly, totloop); - - const MPoly *mp, *mpoly; - const MLoop *ml, *mloop; - MFace *mface, *mf; - MemArena *arena = NULL; - int *mface_to_poly_map; - uint(*lindices)[4]; - int poly_index, mface_index; - uint j; - - mpoly = CustomData_get_layer(pdata, CD_MPOLY); - mloop = CustomData_get_layer(ldata, CD_MLOOP); - - /* Allocate the length of `totfaces`, avoid many small reallocation's, - * if all faces are triangles it will be correct, `quads == 2x` allocations. */ - /* Take care since memory is _not_ zeroed so be sure to initialize each field. */ - mface_to_poly_map = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface_to_poly_map), __func__); - mface = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface), __func__); - lindices = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*lindices), __func__); - - mface_index = 0; - mp = mpoly; - for (poly_index = 0; poly_index < totpoly; poly_index++, mp++) { - const uint mp_loopstart = (uint)mp->loopstart; - const uint mp_totloop = (uint)mp->totloop; - uint l1, l2, l3, l4; - uint *lidx; - if (mp_totloop < 3) { - /* Do nothing. */ - } - -#ifdef USE_TESSFACE_SPEEDUP - -# define ML_TO_MF(i1, i2, i3) \ - mface_to_poly_map[mface_index] = poly_index; \ - mf = &mface[mface_index]; \ - lidx = lindices[mface_index]; \ - /* Set loop indices, transformed to vert indices later. */ \ - l1 = mp_loopstart + i1; \ - l2 = mp_loopstart + i2; \ - l3 = mp_loopstart + i3; \ - mf->v1 = mloop[l1].v; \ - mf->v2 = mloop[l2].v; \ - mf->v3 = mloop[l3].v; \ - mf->v4 = 0; \ - lidx[0] = l1; \ - lidx[1] = l2; \ - lidx[2] = l3; \ - lidx[3] = 0; \ - mf->mat_nr = mp->mat_nr; \ - mf->flag = mp->flag; \ - mf->edcode = 0; \ - (void)0 - -/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */ -# define ML_TO_MF_QUAD() \ - mface_to_poly_map[mface_index] = poly_index; \ - mf = &mface[mface_index]; \ - lidx = lindices[mface_index]; \ - /* Set loop indices, transformed to vert indices later. */ \ - l1 = mp_loopstart + 0; /* EXCEPTION */ \ - l2 = mp_loopstart + 1; /* EXCEPTION */ \ - l3 = mp_loopstart + 2; /* EXCEPTION */ \ - l4 = mp_loopstart + 3; /* EXCEPTION */ \ - mf->v1 = mloop[l1].v; \ - mf->v2 = mloop[l2].v; \ - mf->v3 = mloop[l3].v; \ - mf->v4 = mloop[l4].v; \ - lidx[0] = l1; \ - lidx[1] = l2; \ - lidx[2] = l3; \ - lidx[3] = l4; \ - mf->mat_nr = mp->mat_nr; \ - mf->flag = mp->flag; \ - mf->edcode = TESSFACE_IS_QUAD; \ - (void)0 - - else if (mp_totloop == 3) { - ML_TO_MF(0, 1, 2); - mface_index++; - } - else if (mp_totloop == 4) { -# ifdef USE_TESSFACE_QUADS - ML_TO_MF_QUAD(); - mface_index++; -# else - ML_TO_MF(0, 1, 2); - mface_index++; - ML_TO_MF(0, 2, 3); - mface_index++; -# endif - } -#endif /* USE_TESSFACE_SPEEDUP */ - else { - const float *co_curr, *co_prev; - - float normal[3]; - - float axis_mat[3][3]; - float(*projverts)[2]; - uint(*tris)[3]; - - const uint totfilltri = mp_totloop - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)totfilltri); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)mp_totloop); - - zero_v3(normal); - - /* Calculate the normal, flipped: to get a positive 2D cross product. */ - ml = mloop + mp_loopstart; - co_prev = mvert[ml[mp_totloop - 1].v].co; - for (j = 0; j < mp_totloop; j++, ml++) { - co_curr = mvert[ml->v].co; - add_newell_cross_v3_v3v3(normal, co_prev, co_curr); - co_prev = co_curr; - } - if (UNLIKELY(normalize_v3(normal) == 0.0f)) { - normal[2] = 1.0f; - } - - /* Project verts to 2D. */ - axis_dominant_v3_to_m3_negate(axis_mat, normal); - - ml = mloop + mp_loopstart; - for (j = 0; j < mp_totloop; j++, ml++) { - mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); - } - - BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena); - - /* Apply fill. */ - for (j = 0; j < totfilltri; j++) { - uint *tri = tris[j]; - lidx = lindices[mface_index]; - - mface_to_poly_map[mface_index] = poly_index; - mf = &mface[mface_index]; - - /* Set loop indices, transformed to vert indices later. */ - l1 = mp_loopstart + tri[0]; - l2 = mp_loopstart + tri[1]; - l3 = mp_loopstart + tri[2]; - - mf->v1 = mloop[l1].v; - mf->v2 = mloop[l2].v; - mf->v3 = mloop[l3].v; - mf->v4 = 0; - - lidx[0] = l1; - lidx[1] = l2; - lidx[2] = l3; - lidx[3] = 0; - - mf->mat_nr = mp->mat_nr; - mf->flag = mp->flag; - mf->edcode = 0; - - mface_index++; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - CustomData_free(fdata, totface); - totface = mface_index; - - BLI_assert(totface <= looptri_num); - - /* Not essential but without this we store over-allocated memory in the #CustomData layers. */ - if (LIKELY(looptri_num != totface)) { - mface = MEM_reallocN(mface, sizeof(*mface) * (size_t)totface); - mface_to_poly_map = MEM_reallocN(mface_to_poly_map, - sizeof(*mface_to_poly_map) * (size_t)totface); - } - - CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mface, totface); - - /* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons - * they are directly tessellated from. */ - CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); - CustomData_from_bmeshpoly(fdata, ldata, totface); - - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: - * Polygons take care of their loops ordering, hence not of their vertices ordering. - * Currently, our tfaces' fourth vertex index might be 0 even for a quad. - * However, we know our fourth loop index is never 0 for quads - * (because they are sorted for polygons, and our quads are still mere copies of their polygons). - * So we pass NULL as MFace pointer, and #mesh_loops_to_tessdata - * will use the fourth loop index as quad test. */ - mesh_loops_to_tessdata(fdata, ldata, NULL, mface_to_poly_map, lindices, totface); - - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: - * ...However, most TFace code uses 'MFace->v4 == 0' test to check whether it is a tri or quad. - * BKE_mesh_mface_index_validate() will check this and rotate the tessellated face if needed. - */ -#ifdef USE_TESSFACE_QUADS - mf = mface; - for (mface_index = 0; mface_index < totface; mface_index++, mf++) { - if (mf->edcode == TESSFACE_IS_QUAD) { - BKE_mesh_mface_index_validate(mf, fdata, mface_index, 4); - mf->edcode = 0; - } - } -#endif - - MEM_freeN(lindices); - - return totface; - -#undef USE_TESSFACE_SPEEDUP -#undef USE_TESSFACE_QUADS - -#undef ML_TO_MF -#undef ML_TO_MF_QUAD -} - -void BKE_mesh_tessface_calc(Mesh *mesh) -{ - mesh->totface = mesh_tessface_calc(&mesh->fdata, - &mesh->ldata, - &mesh->pdata, - mesh->mvert, - mesh->totface, - mesh->totloop, - mesh->totpoly); - - BKE_mesh_update_customdata_pointers(mesh, true); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Loop Tessellation * * Fill in #MLoopTri data-structure. diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 0362e4866e3..0b61b876abe 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -103,11 +103,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) /* Must isolate multithreaded tasks while holding a mutex lock. */ blender::threading::isolate_task([&]() { - const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>( - me->runtime.wrapper_type); - me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; - - switch (geom_type_orig) { + switch (static_cast<eMeshWrapperType>(me->runtime.wrapper_type)) { case ME_WRAPPER_TYPE_MDATA: case ME_WRAPPER_TYPE_SUBD: { break; /* Quiet warning. */ @@ -132,7 +128,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) * There is also a performance aspect, where this also assumes that original indices are * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not * harmful. */ - BKE_mesh_ensure_default_orig_index_customdata(me); + BKE_mesh_ensure_default_orig_index_customdata_no_check(me); EditMeshData *edit_data = me->runtime.edit_data; if (edit_data->vertexCos) { @@ -144,8 +140,12 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) } if (me->runtime.wrapper_type_finalize) { - BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); + BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime.cd_mask_extra); } + + /* Keep type assignment last, so that read-only access only uses the mdata code paths after all + * the underlying data has been initialized. */ + me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; }); BLI_mutex_unlock(mesh_eval_mutex); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 6348d83362e..01eb4970f7e 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -456,6 +456,40 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for CLOG_ERROR(&LOG, "Object: \"%s\", Modifier: \"%s\", %s", ob->id.name + 2, md->name, md->error); } +void BKE_modifier_set_warning(const struct Object *ob, + struct ModifierData *md, + const char *_format, + ...) +{ + char buffer[512]; + va_list ap; + const char *format = TIP_(_format); + + va_start(ap, _format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + buffer[sizeof(buffer) - 1] = '\0'; + + /* Store the warning in the same field as the error. + * It is not expected to have both error and warning and having a single place to store the + * message simplifies interface code. */ + + if (md->error) { + MEM_freeN(md->error); + } + + md->error = BLI_strdup(buffer); + +#ifndef NDEBUG + if ((md->mode & eModifierMode_Virtual) == 0) { + /* Ensure correct object is passed in. */ + BLI_assert(BKE_modifier_get_original(ob, md) != NULL); + } +#endif + + UNUSED_VARS_NDEBUG(ob); +} + int BKE_modifiers_get_cage_index(const Scene *scene, Object *ob, int *r_lastPossibleCageIndex, @@ -1001,8 +1035,7 @@ void BKE_modifier_deform_vertsEM(ModifierData *md, /* end modifier callback wrappers */ -Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, - const bool get_cage_mesh) +Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval) { Mesh *me = NULL; @@ -1011,17 +1044,11 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, BMEditMesh *em = BKE_editmesh_from_object(ob_eval); /* 'em' might not exist yet in some cases, just after loading a .blend file, see T57878. */ if (em != NULL) { - Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval); - Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); - - me = (get_cage_mesh && editmesh_eval_cage != NULL) ? editmesh_eval_cage : - editmesh_eval_final; + me = BKE_object_get_editmesh_eval_final(ob_eval); } } if (me == NULL) { - me = (get_cage_mesh && ob_eval->runtime.mesh_deform_eval != NULL) ? - ob_eval->runtime.mesh_deform_eval : - BKE_object_get_evaluated_mesh(ob_eval); + me = BKE_object_get_evaluated_mesh(ob_eval); } return me; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 10abb8f20df..9457c20eb7d 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1239,6 +1239,40 @@ static NlaStrip *nlastrip_find_active(ListBase /* NlaStrip */ *strips) return NULL; } +float BKE_nlastrip_compute_frame_from_previous_strip(NlaStrip *strip) +{ + float limit_prev = MINFRAMEF; + + /* Find the previous end frame, with a special case if the previous strip was a transition : */ + if (strip->prev) { + if (strip->prev->type == NLASTRIP_TYPE_TRANSITION) { + limit_prev = strip->prev->start + NLASTRIP_MIN_LEN_THRESH; + } + else { + limit_prev = strip->prev->end; + } + } + + return limit_prev; +} + +float BKE_nlastrip_compute_frame_to_next_strip(NlaStrip *strip) +{ + float limit_next = MAXFRAMEF; + + /* Find the next begin frame, with a special case if the next strip's a transition : */ + if (strip->next) { + if (strip->next->type == NLASTRIP_TYPE_TRANSITION) { + limit_next = strip->next->end - NLASTRIP_MIN_LEN_THRESH; + } + else { + limit_next = strip->next->start; + } + } + + return limit_next; +} + NlaStrip *BKE_nlastrip_find_active(NlaTrack *nlt) { if (nlt == NULL) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 5ab31ca8f3f..9e97f742525 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4750,6 +4750,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); + register_node_type_geo_deform_curves_on_surface(); register_node_type_geo_delete_geometry(); register_node_type_geo_duplicate_elements(); register_node_type_geo_distribute_points_on_faces(); diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 46d0d156e9f..572aced6096 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1404,6 +1404,7 @@ bool BKE_object_supports_modifiers(const Object *ob) { return (ELEM(ob->type, OB_MESH, + OB_CURVES, OB_CURVES_LEGACY, OB_SURF, OB_FONT, @@ -3997,6 +3998,70 @@ bool BKE_object_empty_image_data_is_visible_in_view3d(const Object *ob, const Re return true; } +bool BKE_object_minmax_empty_drawtype(const struct Object *ob, float r_min[3], float r_max[3]) +{ + BLI_assert(ob->type == OB_EMPTY); + float3 min(0), max(0); + + bool ok = false; + const float radius = ob->empty_drawsize; + + switch (ob->empty_drawtype) { + case OB_ARROWS: { + max = float3(radius); + ok = true; + break; + } + case OB_PLAINAXES: + case OB_CUBE: + case OB_EMPTY_SPHERE: { + min = float3(-radius); + max = float3(radius); + ok = true; + break; + } + case OB_CIRCLE: { + max[0] = max[2] = radius; + min[0] = min[2] = -radius; + ok = true; + break; + } + case OB_SINGLE_ARROW: { + max[2] = radius; + ok = true; + break; + } + case OB_EMPTY_CONE: { + min = float3(-radius, 0.0f, -radius); + max = float3(radius, radius * 2.0f, radius); + ok = true; + break; + } + case OB_EMPTY_IMAGE: { + const float *ofs = ob->ima_ofs; + /* NOTE: this is the best approximation that can be calculated without loading the image. */ + min[0] = ofs[0] * radius; + min[1] = ofs[1] * radius; + max[0] = radius + (ofs[0] * radius); + max[1] = radius + (ofs[1] * radius); + /* Since the image aspect can shrink the bounds towards the object origin, + * adjust the min/max to account for that. */ + for (int i = 0; i < 2; i++) { + CLAMP_MAX(min[i], 0.0f); + CLAMP_MIN(max[i], 0.0f); + } + ok = true; + break; + } + } + + if (ok) { + copy_v3_v3(r_min, min); + copy_v3_v3(r_max, max); + } + return ok; +} + bool BKE_object_minmax_dupli(Depsgraph *depsgraph, Scene *scene, Object *ob, @@ -5339,126 +5404,6 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) /** \name Object Modifier Utilities * \{ */ -bool BKE_object_modifier_use_time(Scene *scene, Object *ob, ModifierData *md) -{ - if (BKE_modifier_depends_ontime(scene, md)) { - return true; - } - - /* Check whether modifier is animated. */ - /* TODO: this should be handled as part of build_animdata() -- Aligorith */ - if (ob->adt) { - AnimData *adt = ob->adt; - FCurve *fcu; - - char md_name_esc[sizeof(md->name) * 2]; - BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); - - char pattern[sizeof(md_name_esc) + 16]; - BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md_name_esc); - - /* action - check for F-Curves with paths containing 'modifiers[' */ - if (adt->action) { - for (fcu = (FCurve *)adt->action->curves.first; fcu != nullptr; fcu = (FCurve *)fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - /* This here allows modifier properties to get driven and still update properly - * - * Workaround to get T26764 (e.g. subsurf levels not updating when animated/driven) - * working, without the updating problems (T28525 T28690 T28774 T28777) caused - * by the RNA updates cache introduced in r.38649 - */ - for (fcu = (FCurve *)adt->drivers.first; fcu != nullptr; fcu = (FCurve *)fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - - /* XXX: also, should check NLA strips, though for now assume that nobody uses - * that and we can omit that for performance reasons... */ - } - - return false; -} - -bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) -{ - if (BKE_gpencil_modifier_depends_ontime(md)) { - return true; - } - - /* Check whether modifier is animated. */ - /* TODO(Aligorith): this should be handled as part of build_animdata() */ - if (ob->adt) { - AnimData *adt = ob->adt; - - char md_name_esc[sizeof(md->name) * 2]; - BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); - - char pattern[sizeof(md_name_esc) + 32]; - BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md_name_esc); - - /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ - if (adt->action) { - LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - /* This here allows modifier properties to get driven and still update properly */ - LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - return false; -} - -bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) -{ - if (BKE_shaderfx_depends_ontime(fx)) { - return true; - } - - /* Check whether effect is animated. */ - /* TODO(Aligorith): this should be handled as part of build_animdata() */ - if (ob->adt) { - AnimData *adt = ob->adt; - - char fx_name_esc[sizeof(fx->name) * 2]; - BLI_str_escape(fx_name_esc, fx->name, sizeof(fx_name_esc)); - - char pattern[sizeof(fx_name_esc) + 32]; - BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx_name_esc); - - /* action - check for F-Curves with paths containing string[' */ - if (adt->action) { - LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - /* This here allows properties to get driven and still update properly */ - LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - return false; -} - /** * Set "ignore cache" flag for all caches on this object. */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 9b72b939f77..9b0d15ac702 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -996,6 +996,12 @@ bool BKE_paint_select_elem_test(Object *ob) return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob)); } +bool BKE_paint_always_hide_test(Object *ob) +{ + return ((ob != NULL) && (ob->type == OB_MESH) && (ob->data != NULL) && + (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT)); +} + void BKE_paint_cavity_curve_preset(Paint *p, int preset) { CurveMapping *cumap = NULL; @@ -1613,7 +1619,7 @@ static void sculpt_update_object(Depsgraph *depsgraph, Mesh *me_eval, bool need_pmap, bool need_mask, - bool UNUSED(need_colors)) + bool is_paint_tool) { Scene *scene = DEG_get_input_scene(depsgraph); Sculpt *sd = scene->toolsettings->sculpt; @@ -1773,28 +1779,31 @@ static void sculpt_update_object(Depsgraph *depsgraph, } } - /* - * We should rebuild the PBVH_pixels when painting canvas changes. - * - * The relevant changes are stored/encoded in the paint canvas key. - * These include the active uv map, and resolutions. - */ - if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { - char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); - if (ss->last_paint_canvas_key == NULL || !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { - MEM_SAFE_FREE(ss->last_paint_canvas_key); - ss->last_paint_canvas_key = paint_canvas_key; - BKE_pbvh_mark_rebuild_pixels(ss->pbvh); - } - else { - MEM_freeN(paint_canvas_key); + if (is_paint_tool) { + /* + * We should rebuild the PBVH_pixels when painting canvas changes. + * + * The relevant changes are stored/encoded in the paint canvas key. + * These include the active uv map, and resolutions. + */ + if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { + char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); + if (ss->last_paint_canvas_key == NULL || + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + MEM_SAFE_FREE(ss->last_paint_canvas_key); + ss->last_paint_canvas_key = paint_canvas_key; + BKE_pbvh_mark_rebuild_pixels(ss->pbvh); + } + else { + MEM_freeN(paint_canvas_key); + } } - } - /* We could be more precise when we have access to the active tool. */ - const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; - if (use_paint_slots) { - BKE_texpaint_slots_refresh_object(scene, ob); + /* We could be more precise when we have access to the active tool. */ + const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; + if (use_paint_slots) { + BKE_texpaint_slots_refresh_object(scene, ob); + } } } @@ -1903,7 +1912,7 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) } void BKE_sculpt_update_object_for_edit( - Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool need_colors) + Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool is_paint_tool) { BLI_assert(ob_orig == DEG_get_original_object(ob_orig)); @@ -1911,7 +1920,7 @@ void BKE_sculpt_update_object_for_edit( Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); BLI_assert(me_eval != NULL); - sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask, need_colors); + sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask, is_paint_tool); } int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) @@ -2252,12 +2261,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) return NULL; } - bool respect_hide = true; - if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { - if (!(BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob))) { - respect_hide = false; - } - } + const bool respect_hide = true; PBVH *pbvh = ob->sculpt->pbvh; if (pbvh != NULL) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index b44b70bcd55..2471d3baa59 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -58,6 +58,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index c461b7f108d..da769515f08 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -28,6 +28,7 @@ #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_object.h" #include "BKE_particle.h" diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 3ad770c5429..4a8f029beee 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -47,6 +47,7 @@ #include "BKE_effect.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_particle.h" #include "BKE_bvhutils.h" diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 5e5443f48ca..6cebcdfea4e 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1316,9 +1316,14 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, if (node->flag & PBVH_RebuildDrawBuffers) { switch (pbvh->type) { - case PBVH_GRIDS: - node->draw_buffers = GPU_pbvh_grid_buffers_build(node->totprim, pbvh->grid_hidden); + case PBVH_GRIDS: { + bool smooth = node->totprim > 0 ? + pbvh->grid_flag_mats[node->prim_indices[0]].flag & ME_SMOOTH : + false; + + node->draw_buffers = GPU_pbvh_grid_buffers_build(node->totprim, pbvh->grid_hidden, smooth); break; + } case PBVH_FACES: node->draw_buffers = GPU_pbvh_mesh_buffers_build( pbvh->mpoly, @@ -2419,7 +2424,7 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); const int x_it[4] = {0, 1, 1, 0}; - const int y_it[4] = {0, 0, 1, 1}; + const int y_it[4] = {1, 1, 0, 0}; for (int j = 0; j < 4; j++) { /* Always assign nearest_vertex_co in the first iteration to avoid comparison against diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 21217a7f9bf..5d8025dce8a 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2920,7 +2920,7 @@ int BKE_ptcache_id_reset(Scene *scene, PTCacheID *pid, int mode) BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); } else if (after) { - BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, CFRA); + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, scene->r.cfra); } return (reset || clear || after); @@ -3162,8 +3162,8 @@ void BKE_ptcache_bake(PTCacheBaker *baker) PTCacheID *pid = &baker->pid; PointCache *cache = NULL; float frameleno = scene->r.framelen; - int cfrao = CFRA; - int startframe = MAXFRAME, endframe = baker->anim_init ? scene->r.sfra : CFRA; + int cfrao = scene->r.cfra; + int startframe = MAXFRAME, endframe = baker->anim_init ? scene->r.sfra : scene->r.cfra; int bake = baker->bake; int render = baker->render; @@ -3270,7 +3270,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) } } - CFRA = startframe; + scene->r.cfra = startframe; scene->r.framelen = 1.0; /* bake */ @@ -3282,21 +3282,21 @@ void BKE_ptcache_bake(PTCacheBaker *baker) stime = ptime = PIL_check_seconds_timer(); - for (int fr = CFRA; fr <= endframe; fr += baker->quick_step, CFRA = fr) { + for (int fr = scene->r.cfra; fr <= endframe; fr += baker->quick_step, scene->r.cfra = fr) { BKE_scene_graph_update_for_newframe(depsgraph); if (baker->update_progress) { - float progress = ((float)(CFRA - startframe) / (float)(endframe - startframe)); + float progress = ((float)(scene->r.cfra - startframe) / (float)(endframe - startframe)); baker->update_progress(baker->bake_job, progress, &cancel); } if (G.background) { - printf("bake: frame %d :: %d\n", CFRA, endframe); + printf("bake: frame %d :: %d\n", scene->r.cfra, endframe); } else { ctime = PIL_check_seconds_timer(); - fetd = (ctime - ptime) * (endframe - CFRA) / baker->quick_step; + fetd = (ctime - ptime) * (endframe - scene->r.cfra) / baker->quick_step; if (use_timer || fetd > 60.0) { use_timer = true; @@ -3307,7 +3307,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) printf("Baked for %s, current frame: %i/%i (%.3fs), ETC: %s\r", run, - CFRA - startframe + 1, + scene->r.cfra - startframe + 1, endframe - startframe + 1, ctime - ptime, etd); @@ -3321,7 +3321,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) break; } - CFRA += 1; + scene->r.cfra += 1; } if (use_timer) { @@ -3330,7 +3330,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) printf("\nBake %s %s (%i frames simulated).\n", (cancel ? "canceled after" : "finished in"), run, - CFRA - startframe); + scene->r.cfra - startframe); } /* clear baking flag */ @@ -3379,7 +3379,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) } scene->r.framelen = frameleno; - CFRA = cfrao; + scene->r.cfra = cfrao; if (bake) { /* already on cfra unless baking */ BKE_scene_graph_update_for_newframe(depsgraph); diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 5d8a543488d..1bb0d2d2aeb 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -68,7 +68,6 @@ static void pointcloud_init_data(ID *id) nullptr, pointcloud->totpoint, POINTCLOUD_ATTR_POSITION); - BKE_pointcloud_update_customdata_pointers(pointcloud); } static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) @@ -83,7 +82,6 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s CD_MASK_ALL, alloc_type, pointcloud_dst->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud_dst); pointcloud_dst->batch_cache = nullptr; } @@ -138,7 +136,6 @@ static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); /* Materials */ BLO_read_pointer_array(reader, (void **)&pointcloud->mat); @@ -194,17 +191,28 @@ static void pointcloud_random(PointCloud *pointcloud) { pointcloud->totpoint = 400; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); RNG *rng = BLI_rng_new(0); - for (int i = 0; i < pointcloud->totpoint; i++) { - pointcloud->co[i][0] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->co[i][1] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->co[i][2] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->radius[i] = 0.05f * BLI_rng_get_float(rng); + blender::bke::MutableAttributeAccessor attributes = + blender::bke::pointcloud_attributes_for_write(*pointcloud); + blender::bke::SpanAttributeWriter positions = + attributes.lookup_or_add_for_write_only_span<float3>(POINTCLOUD_ATTR_POSITION, + ATTR_DOMAIN_POINT); + blender::bke::SpanAttributeWriter<float> radii = + attributes.lookup_or_add_for_write_only_span<float>(POINTCLOUD_ATTR_RADIUS, + ATTR_DOMAIN_POINT); + + for (const int i : positions.span.index_range()) { + positions.span[i] = + float3(BLI_rng_get_float(rng), BLI_rng_get_float(rng), BLI_rng_get_float(rng)) * 2.0f - + 1.0f; + radii.span[i] = 0.05f * BLI_rng_get_float(rng); } + positions.finish(); + radii.finish(); + BLI_rng_free(rng); } @@ -250,7 +258,6 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) pointcloud->totpoint = totpoint; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); return pointcloud; } @@ -258,10 +265,14 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds( const PointCloud &pointcloud) { - Span<float3> positions{reinterpret_cast<float3 *>(pointcloud.co), pointcloud.totpoint}; - if (pointcloud.radius) { - Span<float> radii{pointcloud.radius, pointcloud.totpoint}; - return blender::bounds::min_max_with_radii(positions, radii); + blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(pointcloud); + blender::VArraySpan<float3> positions = attributes.lookup_or_default<float3>( + POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0)); + blender::VArray<float> radii = attributes.lookup_or_default<float>( + POINTCLOUD_ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f); + + if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) { + return blender::bounds::min_max_with_radii(positions, radii.get_internal_span()); } return blender::bounds::min_max(positions); } @@ -307,14 +318,6 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_pointcloud_update_customdata_pointers(PointCloud *pointcloud) -{ - pointcloud->co = static_cast<float(*)[3]>( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION)); - pointcloud->radius = static_cast<float *>( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, POINTCLOUD_ATTR_RADIUS)); -} - bool BKE_pointcloud_customdata_required(const PointCloud *UNUSED(pointcloud), const char *name) { return STREQ(name, POINTCLOUD_ATTR_POSITION); @@ -334,7 +337,6 @@ PointCloud *BKE_pointcloud_new_for_eval(const PointCloud *pointcloud_src, int to pointcloud_dst->totpoint = totpoint; CustomData_copy( &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud_dst); return pointcloud_dst; } diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index e203d32a658..e2da27fc840 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1173,6 +1173,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) BKE_curveprofile_blend_read(reader, sce->toolsettings->custom_bevel_profile_preset); } + BLO_read_data_address(reader, &sce->toolsettings->paint_mode.canvas_image); BLO_read_data_address(reader, &sce->toolsettings->sequencer_tool_settings); } @@ -2951,6 +2952,20 @@ int BKE_scene_num_threads(const Scene *scene) return BKE_render_num_threads(&scene->r); } +void BKE_render_resolution(const struct RenderData *r, + const bool use_crop, + int *r_width, + int *r_height) +{ + *r_width = (r->xsch * r->size) / 100; + *r_height = (r->ysch * r->size) / 100; + + if (use_crop && (r->mode & R_BORDER) && (r->mode & R_CROP)) { + *r_width *= BLI_rctf_size_x(&r->border); + *r_height *= BLI_rctf_size_y(&r->border); + } +} + int BKE_render_preview_pixel_size(const RenderData *r) { if (r->preview_pixel_size == 0) { diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 12dc1b6d1fa..c16e5ce5655 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -277,7 +277,7 @@ static void screen_blend_read_lib(BlendLibReader *reader, ID *id) IDTypeInfo IDType_ID_SCR = { .id_code = ID_SCR, - .id_filter = 0, + .id_filter = FILTER_ID_SCR, .main_listbase_index = INDEX_ID_SCR, .struct_size = sizeof(bScreen), .name = "Screen", diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index 7c7aa80402d..82cc250c6d1 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -666,7 +666,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) } if (calc->aux_target) { - auxMesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(calc->aux_target, false); + auxMesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(calc->aux_target); if (!auxMesh) { return; } @@ -1397,7 +1397,7 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, if (smd->target != NULL) { Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target); - calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false); + calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target); /* TODO: there might be several "bugs" with non-uniform scales matrices * because it will no longer be nearest surface, not sphere projection diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 343a829cf76..f459b5a82ac 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -804,7 +804,7 @@ void BKE_sound_set_scene_volume(Scene *scene, float volume) } AUD_Sequence_setAnimationData(scene->sound_scene, AUD_AP_VOLUME, - CFRA, + scene->r.cfra, &volume, (scene->audio.flag & AUDIO_VOLUME_ANIMATED) != 0); } @@ -855,7 +855,7 @@ static double get_cur_time(Scene *scene) /* We divide by the current framelen to take into account time remapping. * Otherwise we will get the wrong starting time which will break A/V sync. * See T74111 for further details. */ - return FRA2TIME((CFRA + SUBFRA) / (double)scene->r.framelen); + return FRA2TIME((scene->r.cfra + scene->r.subframe) / (double)scene->r.framelen); } void BKE_sound_play_scene(Scene *scene) @@ -911,7 +911,7 @@ void BKE_sound_seek_scene(Main *bmain, Scene *scene) int animation_playing; const double one_frame = 1.0 / FPS; - const double cur_time = FRA2TIME(CFRA); + const double cur_time = FRA2TIME(scene->r.cfra); AUD_Device_lock(sound_device); @@ -1131,13 +1131,13 @@ static void sound_update_base(Scene *scene, Object *object, void *new_set) mat4_to_quat(quat, object->obmat); AUD_SequenceEntry_setAnimationData( - strip->speaker_handle, AUD_AP_LOCATION, CFRA, object->obmat[3], 1); + strip->speaker_handle, AUD_AP_LOCATION, scene->r.cfra, object->obmat[3], 1); AUD_SequenceEntry_setAnimationData( - strip->speaker_handle, AUD_AP_ORIENTATION, CFRA, quat, 1); + strip->speaker_handle, AUD_AP_ORIENTATION, scene->r.cfra, quat, 1); AUD_SequenceEntry_setAnimationData( - strip->speaker_handle, AUD_AP_VOLUME, CFRA, &speaker->volume, 1); + strip->speaker_handle, AUD_AP_VOLUME, scene->r.cfra, &speaker->volume, 1); AUD_SequenceEntry_setAnimationData( - strip->speaker_handle, AUD_AP_PITCH, CFRA, &speaker->pitch, 1); + strip->speaker_handle, AUD_AP_PITCH, scene->r.cfra, &speaker->pitch, 1); AUD_SequenceEntry_setSound(strip->speaker_handle, speaker->sound->playback_handle); AUD_SequenceEntry_setMuted(strip->speaker_handle, mute); } @@ -1172,8 +1172,8 @@ void BKE_sound_update_scene(Depsgraph *depsgraph, Scene *scene) if (scene->camera) { mat4_to_quat(quat, scene->camera->obmat); AUD_Sequence_setAnimationData( - scene->sound_scene, AUD_AP_LOCATION, CFRA, scene->camera->obmat[3], 1); - AUD_Sequence_setAnimationData(scene->sound_scene, AUD_AP_ORIENTATION, CFRA, quat, 1); + scene->sound_scene, AUD_AP_LOCATION, scene->r.cfra, scene->camera->obmat[3], 1); + AUD_Sequence_setAnimationData(scene->sound_scene, AUD_AP_ORIENTATION, scene->r.cfra, quat, 1); } AUD_destroySet(scene->speaker_handles); diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index e8c7aff75d1..a674bf7800a 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -6,7 +6,6 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_spline.hh" @@ -21,6 +20,7 @@ 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 { diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index fda833ffd27..841b47818f1 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -34,8 +34,8 @@ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( case SUBDIV_EVALUATOR_TYPE_CPU: { return OPENSUBDIV_EVALUATOR_CPU; } - case SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE: { - return OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; + case SUBDIV_EVALUATOR_TYPE_GPU: { + return OPENSUBDIV_EVALUATOR_GPU; } } BLI_assert_msg(0, "Unknown evaluator type"); diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 57af0192d59..2271fd90bda 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -98,11 +98,6 @@ static bool is_subdivision_evaluation_possible_on_gpu(void) return false; } - const int available_evaluators = openSubdiv_getAvailableEvaluators(); - if ((available_evaluators & OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) == 0) { - return false; - } - return true; } diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 8b462cba7ed..cd1af5a8de4 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -2798,6 +2798,96 @@ ImBuf *BKE_tracking_get_search_imbuf(ImBuf *ibuf, return searchibuf; } +BLI_INLINE int plane_marker_size_len_in_pixels(const float a[2], + const float b[2], + const int frame_width, + const int frame_height) +{ + const float a_px[2] = {a[0] * frame_width, a[1] * frame_height}; + const float b_px[2] = {b[0] * frame_width, b[1] * frame_height}; + + return ceilf(len_v2v2(a_px, b_px)); +} + +ImBuf *BKE_tracking_get_plane_imbuf(const ImBuf *frame_ibuf, + const MovieTrackingPlaneMarker *plane_marker) +{ + /* Alias for corners, allowing shorter access to coordinates. */ + const float(*corners)[2] = plane_marker->corners; + + /* Dimensions of the frame image in pixels. */ + const int frame_width = frame_ibuf->x; + const int frame_height = frame_ibuf->y; + + /* Lengths of left and right edges of the plane marker, in pixels. */ + const int left_side_len_px = plane_marker_size_len_in_pixels( + corners[0], corners[3], frame_width, frame_height); + const int right_side_len_px = plane_marker_size_len_in_pixels( + corners[1], corners[2], frame_width, frame_height); + + /* Lengths of top and bottom edges of the plane marker, in pixels. */ + const int top_side_len_px = plane_marker_size_len_in_pixels( + corners[3], corners[2], frame_width, frame_height); + const int bottom_side_len_px = plane_marker_size_len_in_pixels( + corners[0], corners[1], frame_width, frame_height); + + /* Choose the number of samples as a maximum of the corresponding sides in pixels. */ + const int num_samples_x = max_ii(top_side_len_px, bottom_side_len_px); + const int num_samples_y = max_ii(left_side_len_px, right_side_len_px); + + /* Create new result image with the same type of content as the original. */ + ImBuf *plane_ibuf = IMB_allocImBuf( + num_samples_x, num_samples_y, 32, frame_ibuf->rect_float ? IB_rectfloat : IB_rect); + + /* Calculate corner coordinates in pixel space, as separate X/Y arrays. */ + const double src_pixel_x[4] = {corners[0][0] * frame_width, + corners[1][0] * frame_width, + corners[2][0] * frame_width, + corners[3][0] * frame_width}; + const double src_pixel_y[4] = {corners[0][1] * frame_height, + corners[1][1] * frame_height, + corners[2][1] * frame_height, + corners[3][1] * frame_height}; + + /* Warped Position is unused but is expected to be provided by the API. */ + double warped_position_x, warped_position_y; + + /* Actual sampling. */ + if (frame_ibuf->rect_float != NULL) { + libmv_samplePlanarPatchFloat(frame_ibuf->rect_float, + frame_ibuf->x, + frame_ibuf->y, + 4, + src_pixel_x, + src_pixel_y, + num_samples_x, + num_samples_y, + NULL, + plane_ibuf->rect_float, + &warped_position_x, + &warped_position_y); + } + else { + libmv_samplePlanarPatchByte((unsigned char *)frame_ibuf->rect, + frame_ibuf->x, + frame_ibuf->y, + 4, + src_pixel_x, + src_pixel_y, + num_samples_x, + num_samples_y, + NULL, + (unsigned char *)plane_ibuf->rect, + &warped_position_x, + &warped_position_y); + } + + plane_ibuf->rect_colorspace = frame_ibuf->rect_colorspace; + plane_ibuf->float_colorspace = frame_ibuf->float_colorspace; + + return plane_ibuf; +} + void BKE_tracking_disable_channels( ImBuf *ibuf, bool disable_red, bool disable_green, bool disable_blue, bool grayscale) { diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index ef0a3069815..0265dd037b1 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -67,6 +67,8 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) { WorkSpace *workspace = (WorkSpace *)id; + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->pin_scene, IDWALK_CB_NOP); + LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); } @@ -120,6 +122,15 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) WorkSpace *workspace = (WorkSpace *)id; Main *bmain = BLO_read_lib_get_main(reader); + /* 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; + } + else { + BLO_read_id_address(reader, NULL, &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; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 5e11cd0703a..883591e3e87 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -1485,7 +1485,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) } } -void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) +void BKE_ffmpeg_image_type_verify(RenderData *rd, const ImageFormatData *imf) { int audio = 0; |