Curves: fix some issues with operator to convert to particle system

Ref T97171.

Differential Revision: https://developer.blender.org/D14637
This commit is contained in:
Jacques Lucke 2022-04-14 12:25:54 +02:00
parent 8f0e06a0ca
commit c9574412c7
Notes: blender-bot 2023-12-08 16:39:08 +01:00
Referenced by issue #97171, Convert Curves to Particle System issues
1 changed files with 158 additions and 134 deletions

View File

@ -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);
if (curves_ob.type != OB_CURVES) {
return;
}
Curves &curves_id = *static_cast<Curves *>(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<Mesh *>(surface_ob.data);
const Span<float3> positions_cu = curves.positions();
const VArray<int> looptri_indices = curves.surface_triangle_indices();
const Span<MLoopTri> 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<int> 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) {
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<ParticleSystemModifierData *>(
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;
psys_free_particles(particle_system);
settings.type = PART_HAIR;
settings.totpart = 0;
psys_changed_type(&surface_ob, particle_system);
MutableSpan<ParticleData> particles{
static_cast<ParticleData *>(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);
/* Prepare utility data structure to map hair roots to mfaces. */
const Span<int> mface_to_poly_map{
static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
surface_me.totface};
Array<Vector<int>> 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<HairKey> hair_keys{
static_cast<HairKey *>(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);
}
}
particle_system->particles = particles.data();
particle_system->totpart = particles.size();
particle_system->flag |= PSYS_EDITED;
particle_system->recalc |= ID_RECALC_PSYS_RESET;
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);
bool could_not_convert_some_curves = false;
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->type != OB_CURVES) {
continue;
if (curves_ob != &active_object) {
try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
}
Curves &curves_id = *static_cast<Curves *>(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;
}
Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
const Span<float3> positions_cu = curves.positions();
const VArray<int> looptri_indices = curves.surface_triangle_indices();
const Span<MLoopTri> 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<int> 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);
}
}
const int hairs_num = curves_indices_to_transfer.size();
if (hairs_num == 0) {
continue;
}
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<ParticleSystemModifierData *>(
object_add_particle_system(bmain, scene, &surface_ob, curves_ob->id.name + 2));
particle_system = psmd.psys;
}
ParticleSettings &settings = *particle_system->part;
psys_free_particles(particle_system);
settings.type = PART_HAIR;
settings.totpart = 0;
psys_changed_type(&surface_ob, particle_system);
MutableSpan<ParticleData> particles{
static_cast<ParticleData *>(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);
/* Prepare utility data structure to map hair roots to mfaces. */
const Span<int> mface_to_poly_map{
static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
surface_me.totface};
Array<Vector<int>> 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<HairKey> hair_keys{
static_cast<HairKey *>(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);
}
}
particle_system->particles = particles.data();
particle_system->totpart = particles.size();
particle_system->flag |= PSYS_EDITED;
particle_system->recalc |= ID_RECALC_PSYS_RESET;
DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY);
DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE);
}
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;