From c9574412c7e3eeb83eccf4819567e88e42747b28 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Apr 2022 12:25:54 +0200 Subject: Curves: fix some issues with operator to convert to particle system Ref T97171. Differential Revision: https://developer.blender.org/D14637 --- source/blender/editors/curves/intern/curves_ops.cc | 268 +++++++++++---------- 1 file changed, 146 insertions(+), 122 deletions(-) (limited to 'source') diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 9823a1f18ce..7d07c211542 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -121,147 +121,171 @@ static float4 compute_mface_weights_for_position(const Mesh &mesh, return mface_weights; } -static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *UNUSED(op)) +static void try_convert_single_object(Object &curves_ob, + Main &bmain, + Scene &scene, + bool *r_could_not_convert_some_curves) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - - CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { - if (curves_ob->type != OB_CURVES) { - continue; - } - Curves &curves_id = *static_cast(curves_ob->data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - if (curves_id.surface == nullptr) { - continue; - } - Object &surface_ob = *curves_id.surface; - if (surface_ob.type != OB_MESH) { - continue; + if (curves_ob.type != OB_CURVES) { + return; + } + Curves &curves_id = *static_cast(curves_ob.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + if (curves_id.surface == nullptr) { + return; + } + Object &surface_ob = *curves_id.surface; + if (surface_ob.type != OB_MESH) { + return; + } + Mesh &surface_me = *static_cast(surface_ob.data); + + const Span positions_cu = curves.positions(); + const VArray looptri_indices = curves.surface_triangle_indices(); + const Span looptris{BKE_mesh_runtime_looptri_ensure(&surface_me), + BKE_mesh_runtime_looptri_len(&surface_me)}; + + /* Find indices of curves that can be transferred to the old hair system. */ + Vector curves_indices_to_transfer; + for (const int curve_i : curves.curves_range()) { + const int looptri_i = looptri_indices[curve_i]; + if (looptri_i >= 0 && looptri_i < looptris.size()) { + curves_indices_to_transfer.append(curve_i); } - Mesh &surface_me = *static_cast(surface_ob.data); - - const Span positions_cu = curves.positions(); - const VArray looptri_indices = curves.surface_triangle_indices(); - const Span looptris{BKE_mesh_runtime_looptri_ensure(&surface_me), - BKE_mesh_runtime_looptri_len(&surface_me)}; - - /* Find indices of curves that can be transferred to the old hair system. */ - Vector curves_indices_to_transfer; - for (const int curve_i : curves.curves_range()) { - const int looptri_i = looptri_indices[curve_i]; - if (looptri_i >= 0 && looptri_i < looptris.size()) { - curves_indices_to_transfer.append(curve_i); - } + else { + *r_could_not_convert_some_curves = true; } + } - const int hairs_num = curves_indices_to_transfer.size(); - if (hairs_num == 0) { - continue; - } + const int hairs_num = curves_indices_to_transfer.size(); + if (hairs_num == 0) { + return; + } - ParticleSystem *particle_system = nullptr; - LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) { - if (STREQ(psys->name, curves_ob->id.name + 2)) { - particle_system = psys; - break; - } - } - if (particle_system == nullptr) { - ParticleSystemModifierData &psmd = *reinterpret_cast( - object_add_particle_system(bmain, scene, &surface_ob, curves_ob->id.name + 2)); - particle_system = psmd.psys; + ParticleSystem *particle_system = nullptr; + LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) { + if (STREQ(psys->name, curves_ob.id.name + 2)) { + particle_system = psys; + break; } + } + if (particle_system == nullptr) { + ParticleSystemModifierData &psmd = *reinterpret_cast( + object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2)); + particle_system = psmd.psys; + particle_system->part->draw_step = 3; + } + + ParticleSettings &settings = *particle_system->part; - ParticleSettings &settings = *particle_system->part; + psys_free_particles(particle_system); + settings.type = PART_HAIR; + settings.totpart = 0; + psys_changed_type(&surface_ob, particle_system); - psys_free_particles(particle_system); - settings.type = PART_HAIR; - settings.totpart = 0; - psys_changed_type(&surface_ob, particle_system); + MutableSpan particles{ + static_cast(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)), + hairs_num}; - MutableSpan particles{ - static_cast(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)), - hairs_num}; + /* The old hair system still uses #MFace, so make sure those are available on the mesh. */ + BKE_mesh_tessface_calc(&surface_me); - /* The old hair system still uses #MFace, so make sure those are available on the mesh. */ - BKE_mesh_tessface_calc(&surface_me); + /* Prepare utility data structure to map hair roots to mfaces. */ + const Span mface_to_poly_map{ + static_cast(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), + surface_me.totface}; + Array> poly_to_mface_map(surface_me.totpoly); + for (const int mface_i : mface_to_poly_map.index_range()) { + const int poly_i = mface_to_poly_map[mface_i]; + poly_to_mface_map[poly_i].append(mface_i); + } - /* Prepare utility data structure to map hair roots to mfaces. */ - const Span mface_to_poly_map{ - static_cast(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), - surface_me.totface}; - Array> poly_to_mface_map(surface_me.totpoly); - for (const int mface_i : mface_to_poly_map.index_range()) { - const int poly_i = mface_to_poly_map[mface_i]; - poly_to_mface_map[poly_i].append(mface_i); + /* Prepare transformation matrices. */ + const float4x4 curves_to_world_mat = curves_ob.obmat; + const float4x4 surface_to_world_mat = surface_ob.obmat; + const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); + const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; + + for (const int new_hair_i : curves_indices_to_transfer.index_range()) { + const int curve_i = curves_indices_to_transfer[new_hair_i]; + const IndexRange points = curves.points_for_curve(curve_i); + + const int looptri_i = looptri_indices[curve_i]; + const MLoopTri &looptri = looptris[looptri_i]; + const int poly_i = looptri.poly; + + const float3 &root_pos_cu = positions_cu[points.first()]; + const float3 root_pos_su = curves_to_surface_mat * root_pos_cu; + + const int mface_i = find_mface_for_root_position( + surface_me, poly_to_mface_map[poly_i], root_pos_su); + const MFace &mface = surface_me.mface[mface_i]; + + const float4 mface_weights = compute_mface_weights_for_position( + surface_me, mface, root_pos_su); + + ParticleData &particle = particles[new_hair_i]; + const int num_keys = points.size(); + MutableSpan hair_keys{ + static_cast(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys}; + + particle.hair = hair_keys.data(); + particle.totkey = hair_keys.size(); + copy_v4_v4(particle.fuv, mface_weights); + particle.num = mface_i; + /* Not sure if there is a better way to initialize this. */ + particle.num_dmcache = DMCACHE_NOTFOUND; + + float4x4 hair_to_surface_mat; + psys_mat_hair_to_object( + &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values); + /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */ + copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su); + const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted(); + + for (const int key_i : hair_keys.index_range()) { + const float3 &key_pos_cu = positions_cu[points[key_i]]; + const float3 key_pos_su = curves_to_surface_mat * key_pos_cu; + const float3 key_pos_ha = surface_to_hair_mat * key_pos_su; + + HairKey &key = hair_keys[key_i]; + copy_v3_v3(key.co, key_pos_ha); + key.time = 100.0f * key_i / (float)(hair_keys.size() - 1); } + } - /* Prepare transformation matrices. */ - const float4x4 curves_to_world_mat = curves_ob->obmat; - const float4x4 surface_to_world_mat = surface_ob.obmat; - const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; + particle_system->particles = particles.data(); + particle_system->totpart = particles.size(); + particle_system->flag |= PSYS_EDITED; + particle_system->recalc |= ID_RECALC_PSYS_RESET; - for (const int new_hair_i : curves_indices_to_transfer.index_range()) { - const int curve_i = curves_indices_to_transfer[new_hair_i]; - const IndexRange points = curves.points_for_curve(curve_i); - - const int looptri_i = looptri_indices[curve_i]; - const MLoopTri &looptri = looptris[looptri_i]; - const int poly_i = looptri.poly; - - const float3 &root_pos_cu = positions_cu[points.first()]; - const float3 root_pos_su = curves_to_surface_mat * root_pos_cu; - - const int mface_i = find_mface_for_root_position( - surface_me, poly_to_mface_map[poly_i], root_pos_su); - const MFace &mface = surface_me.mface[mface_i]; - - const float4 mface_weights = compute_mface_weights_for_position( - surface_me, mface, root_pos_su); - - ParticleData &particle = particles[new_hair_i]; - const int num_keys = points.size(); - MutableSpan hair_keys{ - static_cast(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), - num_keys}; - - particle.hair = hair_keys.data(); - particle.totkey = hair_keys.size(); - copy_v4_v4(particle.fuv, mface_weights); - particle.num = mface_i; - /* Not sure if there is a better way to initialize this. */ - particle.num_dmcache = DMCACHE_NOTFOUND; - - float4x4 hair_to_surface_mat; - psys_mat_hair_to_object( - &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values); - /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */ - copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su); - const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted(); - - for (const int key_i : hair_keys.index_range()) { - const float3 &key_pos_cu = positions_cu[points[key_i]]; - const float3 key_pos_su = curves_to_surface_mat * key_pos_cu; - const float3 key_pos_ha = surface_to_hair_mat * key_pos_su; - - HairKey &key = hair_keys[key_i]; - copy_v3_v3(key.co, key_pos_ha); - key.time = 100.0f * key_i / (float)(hair_keys.size() - 1); - } - } + DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE); +} + +static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op) +{ + Main &bmain = *CTX_data_main(C); + Scene &scene = *CTX_data_scene(C); - particle_system->particles = particles.data(); - particle_system->totpart = particles.size(); - particle_system->flag |= PSYS_EDITED; - particle_system->recalc |= ID_RECALC_PSYS_RESET; + bool could_not_convert_some_curves = false; - DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY); - DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE); + Object &active_object = *CTX_data_active_object(C); + try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves); + + CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { + if (curves_ob != &active_object) { + try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves); + } } CTX_DATA_END; + if (could_not_convert_some_curves) { + BKE_report(op->reports, + RPT_INFO, + "Some curves could not be converted because they were not attached to the surface"); + } + WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, nullptr); return OPERATOR_FINISHED; -- cgit v1.2.3