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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern')
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc50
-rw-r--r--source/blender/blenkernel/intern/action_mirror.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c30
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c45
-rw-r--r--source/blender/blenkernel/intern/attribute.cc145
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc942
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh272
-rw-r--r--source/blender/blenkernel/intern/brush.cc (renamed from source/blender/blenkernel/intern/brush.c)176
-rw-r--r--source/blender/blenkernel/intern/bvhutils.cc11
-rw-r--r--source/blender/blenkernel/intern/camera.c87
-rw-r--r--source/blender/blenkernel/intern/constraint.c43
-rw-r--r--source/blender/blenkernel/intern/crazyspace.cc (renamed from source/blender/blenkernel/intern/crazyspace.c)160
-rw-r--r--source/blender/blenkernel/intern/curve_bezier.cc31
-rw-r--r--source/blender/blenkernel/intern/curve_catmull_rom.cc103
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc139
-rw-r--r--source/blender/blenkernel/intern/curve_legacy_convert.cc29
-rw-r--r--source/blender/blenkernel/intern/curve_nurbs.cc4
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc92
-rw-r--r--source/blender/blenkernel/intern/curves.cc65
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc209
-rw-r--r--source/blender/blenkernel/intern/curves_geometry_test.cc2
-rw-r--r--source/blender/blenkernel/intern/curves_utils.cc32
-rw-r--r--source/blender/blenkernel/intern/customdata.cc185
-rw-r--r--source/blender/blenkernel/intern/data_transfer.c2
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c2
-rw-r--r--source/blender/blenkernel/intern/fcurve.c79
-rw-r--r--source/blender/blenkernel/intern/fluid.c7
-rw-r--r--source/blender/blenkernel/intern/fmodifier.c4
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc239
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curves.cc190
-rw-r--r--source/blender/blenkernel/intern/geometry_component_edit_data.cc58
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc101
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc236
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc102
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc76
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc44
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c2
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc4
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c2
-rw-r--r--source/blender/blenkernel/intern/idtype.c39
-rw-r--r--source/blender/blenkernel/intern/image.cc125
-rw-r--r--source/blender/blenkernel/intern/image_gpu.cc6
-rw-r--r--source/blender/blenkernel/intern/image_save.cc50
-rw-r--r--source/blender/blenkernel/intern/key.c123
-rw-r--r--source/blender/blenkernel/intern/lattice_deform.c3
-rw-r--r--source/blender/blenkernel/intern/layer.c37
-rw-r--r--source/blender/blenkernel/intern/lib_id.c286
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c3
-rw-r--r--source/blender/blenkernel/intern/lib_id_remapper.cc1
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc335
-rw-r--r--source/blender/blenkernel/intern/lib_override.cc150
-rw-r--r--source/blender/blenkernel/intern/lib_query.c18
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c5
-rw-r--r--source/blender/blenkernel/intern/library.c22
-rw-r--r--source/blender/blenkernel/intern/main.c5
-rw-r--r--source/blender/blenkernel/intern/main_namemap.cc365
-rw-r--r--source/blender/blenkernel/intern/material.c21
-rw-r--r--source/blender/blenkernel/intern/mball.c121
-rw-r--r--source/blender/blenkernel/intern/mesh.cc117
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc23
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.cc272
-rw-r--r--source/blender/blenkernel/intern/mesh_legacy_convert.cc876
-rw-r--r--source/blender/blenkernel/intern/mesh_merge_customdata.cc6
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc7
-rw-r--r--source/blender/blenkernel/intern/mesh_tessellate.c364
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.cc14
-rw-r--r--source/blender/blenkernel/intern/modifier.c47
-rw-r--r--source/blender/blenkernel/intern/nla.c34
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/blenkernel/intern/object.cc185
-rw-r--r--source/blender/blenkernel/intern/paint.c62
-rw-r--r--source/blender/blenkernel/intern/particle.c1
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c1
-rw-r--r--source/blender/blenkernel/intern/particle_system.c1
-rw-r--r--source/blender/blenkernel/intern/pbvh.c11
-rw-r--r--source/blender/blenkernel/intern/pointcache.c24
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc48
-rw-r--r--source/blender/blenkernel/intern/scene.cc15
-rw-r--r--source/blender/blenkernel/intern/screen.c2
-rw-r--r--source/blender/blenkernel/intern/shrinkwrap.c4
-rw-r--r--source/blender/blenkernel/intern/sound.c18
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc2
-rw-r--r--source/blender/blenkernel/intern/subdiv_eval.c4
-rw-r--r--source/blender/blenkernel/intern/subdiv_modifier.c5
-rw-r--r--source/blender/blenkernel/intern/tracking.c90
-rw-r--r--source/blender/blenkernel/intern/workspace.c11
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c2
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 &current_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(&params->viewplane) /
+ BLI_rctf_size_y(&params->viewplane))) :
+ (dist_span_y * (BLI_rctf_size_y(&params->viewplane) /
+ BLI_rctf_size_x(&params->viewplane)));
- if ((dists[0] + dists[2]) > (dists[1] + dists[3])) {
- scale_diff = (dists[1] + dists[3]) *
- (BLI_rctf_size_x(&params->viewplane) / BLI_rctf_size_y(&params->viewplane));
- }
- else {
- scale_diff = (dists[0] + dists[2]) *
- (BLI_rctf_size_y(&params->viewplane) / BLI_rctf_size_x(&params->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;