Cleanup: extract function to snap curves to surface

This makes it possible to use this function without having
to call an operator. This is currently used by D14864.
This commit is contained in:
Jacques Lucke 2022-07-05 15:37:34 +02:00
parent b98d116257
commit d4099465cd
1 changed files with 143 additions and 128 deletions

View File

@ -517,152 +517,167 @@ static bool snap_curves_to_surface_poll(bContext *C)
return true;
}
static void snap_curves_to_surface_exec_object(Object &curves_ob,
const Object &surface_ob,
const AttachMode attach_mode,
bool *r_invalid_uvs,
bool *r_missing_uvs)
{
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
MeshComponent surface_mesh_component;
surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly);
VArraySpan<float2> surface_uv_map;
if (curves_id.surface_uv_map != nullptr) {
surface_uv_map = surface_mesh_component
.attribute_try_get_for_read(
curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
.typed<float2>();
}
MutableSpan<float3> positions_cu = curves.positions_for_write();
MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
BKE_mesh_runtime_looptri_len(&surface_mesh)};
const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
switch (attach_mode) {
case AttachMode::Nearest: {
BVHTreeFromMesh surface_bvh;
BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
const float3 old_first_point_pos_su = transforms.curves_to_surface *
old_first_point_pos_cu;
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(surface_bvh.tree,
old_first_point_pos_su,
&nearest,
surface_bvh.nearest_callback,
&surface_bvh);
const int looptri_index = nearest.index;
if (looptri_index == -1) {
continue;
}
const float3 new_first_point_pos_su = nearest.co;
const float3 new_first_point_pos_cu = transforms.surface_to_curves *
new_first_point_pos_su;
const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
for (float3 &pos_cu : positions_cu.slice(points)) {
pos_cu += pos_diff_cu;
}
if (!surface_uv_map.is_empty()) {
const MLoopTri &looptri = surface_looptris[looptri_index];
const int corner0 = looptri.tri[0];
const int corner1 = looptri.tri[1];
const int corner2 = looptri.tri[2];
const float2 &uv0 = surface_uv_map[corner0];
const float2 &uv1 = surface_uv_map[corner1];
const float2 &uv2 = surface_uv_map[corner2];
const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co;
const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co;
const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co;
float3 bary_coords;
interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2);
surface_uv_coords[curve_i] = uv;
}
}
});
break;
}
case AttachMode::Deform: {
if (surface_uv_map.is_empty()) {
*r_missing_uvs = true;
break;
}
using geometry::ReverseUVSampler;
ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris};
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
const float2 uv = surface_uv_coords[curve_i];
ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
*r_invalid_uvs = true;
continue;
}
const MLoopTri &looptri = *lookup_result.looptri;
const float3 &bary_coords = lookup_result.bary_weights;
const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
float3 new_first_point_pos_su;
interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
const float3 new_first_point_pos_cu = transforms.surface_to_curves *
new_first_point_pos_su;
const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
for (float3 &pos_cu : positions_cu.slice(points)) {
pos_cu += pos_diff_cu;
}
}
});
break;
}
}
DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
}
static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
{
const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
std::atomic<bool> found_invalid_uv = false;
bool found_invalid_uvs = false;
bool found_missing_uvs = false;
CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
if (curves_ob->type != OB_CURVES) {
continue;
}
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) {
if (curves_id.surface->type != OB_MESH) {
continue;
}
Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
MeshComponent surface_mesh_component;
surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly);
VArraySpan<float2> surface_uv_map;
if (curves_id.surface_uv_map != nullptr) {
surface_uv_map = surface_mesh_component
.attribute_try_get_for_read(
curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
.typed<float2>();
}
MutableSpan<float3> positions_cu = curves.positions_for_write();
MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
BKE_mesh_runtime_looptri_len(&surface_mesh)};
const bke::CurvesSurfaceTransforms transforms{*curves_ob, &surface_ob};
switch (attach_mode) {
case AttachMode::Nearest: {
BVHTreeFromMesh surface_bvh;
BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
const float3 old_first_point_pos_su = transforms.curves_to_surface *
old_first_point_pos_cu;
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(surface_bvh.tree,
old_first_point_pos_su,
&nearest,
surface_bvh.nearest_callback,
&surface_bvh);
const int looptri_index = nearest.index;
if (looptri_index == -1) {
continue;
}
const float3 new_first_point_pos_su = nearest.co;
const float3 new_first_point_pos_cu = transforms.surface_to_curves *
new_first_point_pos_su;
const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
for (float3 &pos_cu : positions_cu.slice(points)) {
pos_cu += pos_diff_cu;
}
if (!surface_uv_map.is_empty()) {
const MLoopTri &looptri = surface_looptris[looptri_index];
const int corner0 = looptri.tri[0];
const int corner1 = looptri.tri[1];
const int corner2 = looptri.tri[2];
const float2 &uv0 = surface_uv_map[corner0];
const float2 &uv1 = surface_uv_map[corner1];
const float2 &uv2 = surface_uv_map[corner2];
const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co;
const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co;
const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co;
float3 bary_coords;
interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2);
surface_uv_coords[curve_i] = uv;
}
}
});
break;
}
case AttachMode::Deform: {
if (surface_uv_map.is_empty()) {
BKE_report(op->reports,
RPT_ERROR,
"Curves do not have attachment information that can be used for deformation");
break;
}
using geometry::ReverseUVSampler;
ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris};
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
const float2 uv = surface_uv_coords[curve_i];
ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
found_invalid_uv = true;
continue;
}
const MLoopTri &looptri = *lookup_result.looptri;
const float3 &bary_coords = lookup_result.bary_weights;
const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
float3 new_first_point_pos_su;
interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
const float3 new_first_point_pos_cu = transforms.surface_to_curves *
new_first_point_pos_su;
const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
for (float3 &pos_cu : positions_cu.slice(points)) {
pos_cu += pos_diff_cu;
}
}
});
break;
}
}
DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
snap_curves_to_surface_exec_object(
*curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
}
CTX_DATA_END;
if (found_invalid_uv) {
if (found_missing_uvs) {
BKE_report(op->reports,
RPT_ERROR,
"Curves do not have attachment information that can be used for deformation");
}
if (found_invalid_uvs) {
BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
}