Geometry Nodes: Add "Location" output to Attribute Proximity node

This patch adds an output field to the Attribute Proximity node and
renames the existing string socket from "Result" to "Distance".
  - The "Distance" output contains distance to the closest position
    on the Target geometry.
  - The new "Location" output contains the coordinates of the closest
    position on the Target geometry.

A basic use case for this data is a simple shrinkwrap operation.

Differential Revision: https://developer.blender.org/D10415
This commit is contained in:
Victor-Louis De Gusseme
2021-02-26 15:23:09 -06:00
committed by Hans Goudey
parent e00a87163c
commit f7933d0744
3 changed files with 90 additions and 43 deletions

View File

@@ -31,7 +31,8 @@
static bNodeSocketTemplate geo_node_attribute_proximity_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Target")},
{SOCK_STRING, N_("Result")},
{SOCK_STRING, N_("Distance")},
{SOCK_STRING, N_("Location")},
{-1, ""},
};
@@ -60,50 +61,66 @@ static void geo_attribute_proximity_init(bNodeTree *UNUSED(ntree), bNode *node)
namespace blender::nodes {
static void proximity_calc(MutableSpan<float> distance_span,
MutableSpan<float3> location_span,
Span<float3> positions,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
const bool bvh_pointcloud_success)
const bool bvh_pointcloud_success,
const bool store_distances,
const bool store_locations)
{
/* The pointcloud loop uses the values already in the span,
* which is only set if the mesh BVH is used (because it's first). */
if (!bvh_mesh_success) {
distance_span.fill(FLT_MAX);
}
IndexRange range = positions.index_range();
parallel_for(range, 512, [&](IndexRange range) {
BVHTreeNearest nearest;
BVHTreeNearest nearest_from_mesh;
BVHTreeNearest nearest_from_pointcloud;
if (bvh_mesh_success) {
copy_v3_fl(nearest.co, FLT_MAX);
nearest.index = -1;
copy_v3_fl(nearest_from_mesh.co, FLT_MAX);
copy_v3_fl(nearest_from_pointcloud.co, FLT_MAX);
for (int i : range) {
nearest.dist_sq = len_squared_v3v3(nearest.co, positions[i]);
nearest_from_mesh.index = -1;
nearest_from_pointcloud.index = -1;
for (int i : range) {
/* Use the distance to the last found point as upper bound to speedup the bvh lookup. */
nearest_from_mesh.dist_sq = len_squared_v3v3(nearest_from_mesh.co, positions[i]);
if (bvh_mesh_success) {
BLI_bvhtree_find_nearest(tree_data_mesh.tree,
positions[i],
&nearest,
&nearest_from_mesh,
tree_data_mesh.nearest_callback,
&tree_data_mesh);
distance_span[i] = sqrtf(nearest.dist_sq);
}
}
if (bvh_pointcloud_success) {
copy_v3_fl(nearest.co, FLT_MAX);
nearest.index = -1;
/* Use the distance to the closest point in the mesh to speedup the pointcloud bvh lookup.
* This is ok because we only need to find the closest point in the pointcloud if it's closer
* than the mesh. */
nearest_from_pointcloud.dist_sq = nearest_from_mesh.dist_sq;
for (int i : range) {
/* Use the distance to the last found point as upper bound to speedup the bvh lookup. */
nearest.dist_sq = len_squared_v3v3(nearest.co, positions[i]);
if (bvh_pointcloud_success) {
BLI_bvhtree_find_nearest(tree_data_pointcloud.tree,
positions[i],
&nearest,
&nearest_from_pointcloud,
tree_data_pointcloud.nearest_callback,
&tree_data_pointcloud);
distance_span[i] = std::min(distance_span[i], sqrtf(nearest.dist_sq));
}
if (nearest_from_pointcloud.dist_sq < nearest_from_mesh.dist_sq) {
if (store_distances) {
distance_span[i] = sqrtf(nearest_from_pointcloud.dist_sq);
}
if (store_locations) {
location_span[i] = nearest_from_pointcloud.co;
}
}
else {
if (store_distances) {
distance_span[i] = sqrtf(nearest_from_mesh.dist_sq);
}
if (store_locations) {
location_span[i] = nearest_from_mesh.co;
}
}
}
});
@@ -151,14 +168,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
/* This node works on the "point" domain, since that is where positions are stored. */
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
const std::string result_attribute_name = params.get_input<std::string>("Result");
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
result_attribute_name, result_domain, CD_PROP_FLOAT);
distance_attribute_name, result_domain, CD_PROP_FLOAT);
const std::string location_attribute_name = params.get_input<std::string>("Location");
OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
location_attribute_name, result_domain, CD_PROP_FLOAT3);
ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
if (!distance_attribute || !position_attribute) {
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
@@ -183,12 +204,24 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
proximity_calc(distance_attribute->get_span_for_write_only<float>(),
position_attribute->get_span<float3>(),
Span<float3> position_span = position_attribute->get_span<float3>();
MutableSpan<float> distance_span = distance_attribute ?
distance_attribute->get_span_for_write_only<float>() :
MutableSpan<float>();
MutableSpan<float3> location_span = location_attribute ?
location_attribute->get_span_for_write_only<float3>() :
MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
position_span,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
bvh_pointcloud_success);
bvh_pointcloud_success,
distance_attribute, /* Boolean. */
location_attribute); /* Boolean. */
if (bvh_mesh_success) {
free_bvhtree_from_mesh(&tree_data_mesh);
@@ -197,7 +230,12 @@ static void attribute_calc_proximity(GeometryComponent &component,
free_bvhtree_from_pointcloud(&tree_data_pointcloud);
}
distance_attribute.apply_span_and_save();
if (distance_attribute) {
distance_attribute.apply_span_and_save();
}
if (location_attribute) {
location_attribute.apply_span_and_save();
}
}
static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)