1
1

Compare commits

...

10 Commits

Author SHA1 Message Date
3447b31a31 progress 2021-04-20 14:15:23 +02:00
6e4b10d1b2 progress 2021-04-20 13:20:57 +02:00
81b76f8daf progress 2021-04-20 12:53:45 +02:00
29d11cad54 cleanup 2021-04-20 12:37:05 +02:00
ed04957d76 initial transfer from pointcloud 2021-04-20 12:14:14 +02:00
89bf629589 Merge branch 'master' into temp-attribute-transfer-node 2021-04-20 11:25:24 +02:00
1703831f62 progress 2021-04-20 10:50:51 +02:00
6e8cb545b3 extract mesh surface sampling to separate file 2021-04-20 10:44:00 +02:00
435e9393e9 Merge branch 'master' into temp-attribute-transfer-node 2021-04-20 09:28:59 +02:00
a62c1d7700 initial boilerplate code 2021-04-19 17:58:23 +02:00
17 changed files with 760 additions and 92 deletions

View File

@@ -498,6 +498,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeMapRange"),
NodeItem("GeometryNodeAttributeTransfer"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),

View File

@@ -21,8 +21,12 @@
#include "DNA_customdata_types.h"
#include "FN_cpp_type.hh"
namespace blender::attribute_math {
using fn::CPPType;
/**
* Utility function that simplifies calling a templated function based on a custom data type.
*/
@@ -54,6 +58,31 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
}
}
template<typename Func> void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
{
if (cpp_type.is<float>()) {
func(float());
}
else if (cpp_type.is<float2>()) {
func(float2());
}
else if (cpp_type.is<float3>()) {
func(float3());
}
else if (cpp_type.is<int>()) {
func(int());
}
else if (cpp_type.is<bool>()) {
func(bool());
}
else if (cpp_type.is<Color4f>()) {
func(Color4f());
}
else {
BLI_assert_unreachable();
}
}
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*

View File

@@ -158,6 +158,9 @@ class GeometryComponent {
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name, const AttributeDomain domain) const;
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name, const CustomDataType data_type) const;
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
* virtual array will contain a default value. This never returns null. */
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(

View File

@@ -0,0 +1,55 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup bke
*/
#include "FN_generic_virtual_array.hh"
#include "BLI_float3.hh"
#include "BKE_attribute.h"
struct Mesh;
namespace blender::bke::mesh_surface_sample {
using fn::CPPType;
using fn::GMutableSpan;
using fn::GSpan;
using fn::GVArray;
void sample_point_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
const GVArray &data_in,
GMutableSpan data_out);
void sample_corner_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
const GVArray &data_in,
GMutableSpan data_out);
void sample_face_attribute(const Mesh &mesh,
Span<int> looptri_indices,
const GVArray &data_in,
GMutableSpan data_out);
} // namespace blender::bke::mesh_surface_sample

View File

@@ -1414,6 +1414,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
#define GEO_NODE_SWITCH 1043
#define GEO_NODE_ATTRIBUTE_TRANSFER 1044
/** \} */

View File

@@ -190,6 +190,7 @@ set(SRC
intern/mesh_remap.c
intern/mesh_remesh_voxel.c
intern/mesh_runtime.c
intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_validate.c
intern/mesh_validate.cc
@@ -379,6 +380,7 @@ set(SRC
BKE_mesh_remap.h
BKE_mesh_remesh_voxel.h
BKE_mesh_runtime.h
BKE_mesh_sample.hh
BKE_mesh_tangent.h
BKE_mesh_types.h
BKE_mesh_wrapper.h

View File

@@ -786,6 +786,23 @@ std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_
return std::move(attribute.varray);
}
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const blender::StringRef attribute_name, const CustomDataType data_type) const
{
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
const blender::fn::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::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
}
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,

View File

@@ -0,0 +1,158 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_attribute_math.hh"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
namespace blender::bke::mesh_surface_sample {
static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
return {looptris, looptris_len};
}
template<typename T>
BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
const int v0_index = mesh.mloop[looptri.tri[0]].v;
const int v1_index = mesh.mloop[looptri.tri[1]].v;
const int v2_index = mesh.mloop[looptri.tri[2]].v;
const T v0 = data_in[v0_index];
const T v1 = data_in[v1_index];
const T v2 = data_in[v2_index];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
data_out[i] = interpolated_value;
}
}
void sample_point_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const GVArray &data_in,
const GMutableSpan data_out)
{
BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_out.size() == bary_coords.size());
BLI_assert(data_in.size() == mesh.totvert);
BLI_assert(data_in.type() == data_out.type());
const CPPType &type = data_in.type();
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
sample_point_attribute<T>(
mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
});
}
template<typename T>
BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
const int loop_index_0 = looptri.tri[0];
const int loop_index_1 = looptri.tri[1];
const int loop_index_2 = looptri.tri[2];
const T v0 = data_in[loop_index_0];
const T v1 = data_in[loop_index_1];
const T v2 = data_in[loop_index_2];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
data_out[i] = interpolated_value;
}
}
void sample_corner_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const GVArray &data_in,
const GMutableSpan data_out)
{
BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_out.size() == bary_coords.size());
BLI_assert(data_in.size() == mesh.totloop);
BLI_assert(data_in.type() == data_out.type());
const CPPType &type = data_in.type();
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
sample_corner_attribute<T>(
mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
});
}
template<typename T>
void sample_face_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : data_out.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const int poly_index = looptri.poly;
data_out[i] = data_in[poly_index];
}
}
void sample_face_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const GVArray &data_in,
const GMutableSpan data_out)
{
BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_in.size() == mesh.totpoly);
BLI_assert(data_in.type() == data_out.type());
const CPPType &type = data_in.type();
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
});
}
} // namespace blender::bke::mesh_surface_sample

View File

@@ -4941,6 +4941,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();

View File

@@ -85,6 +85,14 @@ class GSpan {
BLI_assert(type_->is<T>());
return Span<T>(static_cast<const T *>(data_), size_);
}
GSpan slice(const int64_t start, int64_t size) const
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
}
};
/**
@@ -153,6 +161,14 @@ class GMutableSpan {
BLI_assert(type_->is<T>());
return MutableSpan<T>(static_cast<T *>(data_), size_);
}
GMutableSpan slice(const int64_t start, int64_t size) const
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
}
};
} // namespace blender::fn

View File

@@ -1313,6 +1313,13 @@ typedef struct NodeSwitch {
uint8_t input_type;
} NodeSwitch;
typedef struct NodeGeometryAttributeTransfer {
/* AttributeDomain. */
uint8_t domain;
/* GeometryNodeAttributeTransferMappingMode. */
uint8_t mapping;
} NodeGeometryAttributeTransfer;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1807,6 +1814,12 @@ typedef enum GeometryNodeMeshLineCountMode {
GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1,
} GeometryNodeMeshLineCountMode;
typedef enum GeometryNodeAttributeTransferMappingMode {
GEO_NODE_ATTRIBUTE_TRANSFER_MAPPING_NEAREST_INTERPOLATED = 0,
GEO_NODE_ATTRIBUTE_TRANSFER_MAPPING_NEAREST = 1,
GEO_NODE_ATTRIBUTE_TRANSFER_MAPPING_TOPOLOGY = 2,
} GeometryNodeAttributeTransferMappingMode;
#ifdef __cplusplus
}
#endif

View File

@@ -9680,6 +9680,43 @@ static void def_geo_switch(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_attribute_transfer(StructRNA *srna)
{
static EnumPropertyItem mapping_items[] = {
{GEO_NODE_ATTRIBUTE_TRANSFER_MAPPING_NEAREST_INTERPOLATED,
"NEAREST_INTERPOLATED",
0,
"Nearest Interpolated",
"Transfer the value from the nearest point on the surface"},
{GEO_NODE_ATTRIBUTE_TRANSFER_MAPPING_NEAREST,
"NEAREST",
0,
"Nearest",
"Transfer the value from the nearest element on the surface, without interpolation"},
{GEO_NODE_ATTRIBUTE_TRANSFER_MAPPING_TOPOLOGY,
"TOPOLOGY",
0,
"Topology",
"Transfer the values from a geometry with the same topology"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage");
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO);
RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, mapping_items);
RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@@ -154,6 +154,7 @@ set(SRC
geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_bounding_box.cc

View File

@@ -42,6 +42,7 @@ void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);

View File

@@ -311,6 +311,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range,
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
/* undefine macros */
#undef DefNode

View File

@@ -0,0 +1,403 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "BLI_kdopbvh.h"
#include "BKE_bvhutils.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_transfer_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Target")},
{SOCK_STRING, N_("Source")},
{SOCK_STRING, N_("Destination")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_transfer_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_attribute_transfer_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE);
uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
}
namespace blender::nodes {
static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
sizeof(NodeGeometryAttributeTransfer), __func__);
node->storage = data;
}
static CustomDataType get_result_data_type(const GeometrySet &geometry,
const StringRef attribute_name)
{
Vector<CustomDataType> data_types;
const PointCloudComponent *pointcloud_component =
geometry.get_component_for_read<PointCloudComponent>();
if (pointcloud_component != nullptr) {
ReadAttributeLookup attribute = pointcloud_component->attribute_try_get_for_read(
attribute_name);
if (attribute) {
data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
}
}
const MeshComponent *mesh_component = geometry.get_component_for_read<MeshComponent>();
if (mesh_component != nullptr) {
ReadAttributeLookup attribute = mesh_component->attribute_try_get_for_read(attribute_name);
if (attribute) {
data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
}
}
return bke::attribute_data_type_highest_complexity(data_types);
}
static void get_closest_pointcloud_point_indices(const PointCloud &pointcloud,
const VArray<float3> &positions,
const MutableSpan<int> r_indices,
const MutableSpan<float> r_distances_sq)
{
BLI_assert(positions.size() == r_indices.size());
BLI_assert(pointcloud.totpoint > 0);
BVHTreeFromPointCloud tree_data;
BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
for (const int i : positions.index_range()) {
BVHTreeNearest nearest;
nearest.dist_sq = FLT_MAX;
const float3 position = positions[i];
BLI_bvhtree_find_nearest(
tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
r_indices[i] = nearest.index;
r_distances_sq[i] = nearest.dist_sq;
}
free_bvhtree_from_pointcloud(&tree_data);
}
static void get_closest_mesh_point_indices(const Mesh &mesh,
const VArray<float3> &positions,
const MutableSpan<int> r_indices,
const MutableSpan<float> r_distances_sq)
{
BLI_assert(positions.size() == r_indices.size());
BLI_assert(mesh.totvert > 0);
BVHTreeFromMesh tree_data;
BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2);
for (const int i : positions.index_range()) {
BVHTreeNearest nearest;
nearest.dist_sq = FLT_MAX;
const float3 position = positions[i];
BLI_bvhtree_find_nearest(
tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
r_indices[i] = nearest.index;
r_distances_sq[i] = nearest.dist_sq;
}
free_bvhtree_from_mesh(&tree_data);
}
static void get_closest_mesh_surface_samples(const Mesh &mesh,
const VArray<float3> &positions,
const MutableSpan<int> r_looptri_indices,
const MutableSpan<float3> r_positions,
const MutableSpan<float> r_distances_sq)
{
BLI_assert(positions.size() == r_looptri_indices.size());
BLI_assert(positions.size() == r_positions.size());
BVHTreeFromMesh tree_data;
BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2);
for (const int i : positions.index_range()) {
BVHTreeNearest nearest;
nearest.dist_sq = FLT_MAX;
const float3 position = positions[i];
BLI_bvhtree_find_nearest(
tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
r_looptri_indices[i] = nearest.index;
r_positions[i] = nearest.co;
r_distances_sq[i] = nearest.dist_sq;
}
free_bvhtree_from_mesh(&tree_data);
}
static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
return {looptris, looptris_len};
}
static void get_barycentric_coords(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> positions,
const MutableSpan<float3> r_bary_coords)
{
BLI_assert(r_bary_coords.size() == positions.size());
BLI_assert(r_bary_coords.size() == looptri_indices.size());
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : r_bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const int v0_index = mesh.mloop[looptri.tri[0]].v;
const int v1_index = mesh.mloop[looptri.tri[1]].v;
const int v2_index = mesh.mloop[looptri.tri[2]].v;
interp_weights_tri_v3(r_bary_coords[i],
mesh.mvert[v0_index].co,
mesh.mvert[v1_index].co,
mesh.mvert[v2_index].co,
positions[i]);
}
}
static void transfer_attribute(const GeometrySet &src_geometry,
GeometryComponent &dst_component,
const AttributeDomain result_domain,
const CustomDataType data_type,
const StringRef src_name,
const StringRef dst_name)
{
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
"position", result_domain, {0, 0, 0});
const int64_t tot_dst_positions = dst_positions.size();
bool use_pointcloud = false;
Array<int> pointcloud_point_indices;
Array<float> pointcloud_point_distances_sq;
bool use_mesh = false;
Array<int> mesh_looptri_indices;
Array<float3> mesh_point_positions;
Array<float> mesh_point_distances_sq;
if (src_geometry.has<PointCloudComponent>()) {
const PointCloudComponent &component =
*src_geometry.get_component_for_read<PointCloudComponent>();
const PointCloud *pointcloud = component.get_for_read();
if (pointcloud != nullptr && pointcloud->totpoint > 0) {
pointcloud_point_indices.reinitialize(tot_dst_positions);
pointcloud_point_distances_sq.reinitialize(tot_dst_positions);
get_closest_pointcloud_point_indices(
*pointcloud, dst_positions, pointcloud_point_indices, pointcloud_point_distances_sq);
use_pointcloud = true;
}
}
if (src_geometry.has<MeshComponent>()) {
const MeshComponent &component = *src_geometry.get_component_for_read<MeshComponent>();
const Mesh *mesh = component.get_for_read();
if (mesh != nullptr && mesh->totpoly > 0) {
mesh_looptri_indices.reinitialize(tot_dst_positions);
mesh_point_positions.reinitialize(tot_dst_positions);
mesh_point_distances_sq.reinitialize(tot_dst_positions);
get_closest_mesh_surface_samples(*mesh,
dst_positions,
mesh_looptri_indices,
mesh_point_positions,
mesh_point_distances_sq);
use_mesh = true;
}
}
Vector<int> pointcloud_sample_indices;
Vector<int> mesh_sample_indices;
if (use_mesh && use_pointcloud) {
for (const int i : IndexRange(tot_dst_positions)) {
if (pointcloud_point_distances_sq[i] < mesh_point_distances_sq[i]) {
pointcloud_sample_indices.append(i);
}
else {
mesh_sample_indices.append(i);
}
}
}
else if (use_mesh) {
/* TODO: Optimize. */
mesh_sample_indices = IndexRange(tot_dst_positions).as_span();
}
else if (use_pointcloud) {
pointcloud_sample_indices = IndexRange(tot_dst_positions).as_span();
}
else {
return;
}
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
dst_name, result_domain, data_type);
if (!dst_attribute) {
return;
}
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
if (!pointcloud_sample_indices.is_empty()) {
const PointCloudComponent &component =
*src_geometry.get_component_for_read<PointCloudComponent>();
ReadAttributeLookup src_attribute = component.attribute_try_get_for_read(src_name, data_type);
if (src_attribute) {
BLI_assert(src_attribute.domain == ATTR_DOMAIN_POINT);
for (const int i : pointcloud_sample_indices) {
const int point_index = pointcloud_point_indices[i];
src_attribute.varray->get(point_index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
else {
const void *default_value = type.default_value();
for (const int i : pointcloud_point_indices) {
dst_attribute->set_by_copy(i, default_value);
}
}
}
if (!mesh_sample_indices.is_empty()) {
const MeshComponent &component = *src_geometry.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
ReadAttributeLookup src_attribute = component.attribute_try_get_for_read(src_name, data_type);
if (src_attribute) {
GMutableSpan dst_span = dst_attribute.as_span();
Array<float3> bary_coords(tot_dst_positions);
get_barycentric_coords(mesh, mesh_looptri_indices, mesh_point_positions, bary_coords);
/* TODO: Take mask into account. */
switch (src_attribute.domain) {
case ATTR_DOMAIN_POINT: {
bke::mesh_surface_sample::sample_point_attribute(
mesh, mesh_looptri_indices, bary_coords, *src_attribute.varray, dst_span);
break;
}
case ATTR_DOMAIN_FACE: {
bke::mesh_surface_sample::sample_face_attribute(
mesh, mesh_looptri_indices, *src_attribute.varray, dst_span);
break;
}
case ATTR_DOMAIN_CORNER: {
bke::mesh_surface_sample::sample_corner_attribute(
mesh, mesh_looptri_indices, bary_coords, *src_attribute.varray, dst_span);
break;
}
case ATTR_DOMAIN_EDGE: {
break;
}
}
}
else {
const void *default_value = type.default_value();
for (const int i : mesh_sample_indices) {
dst_attribute->set_by_copy(i, default_value);
}
}
}
dst_attribute.save();
}
static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
{
GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Target");
const std::string src_attribute_name = params.extract_input<std::string>("Source");
const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
if (src_attribute_name.empty() || dst_attribute_name.empty()) {
params.set_output("Geometry", dst_geometry_set);
return;
}
const NodeGeometryAttributeTransfer &storage =
*(const NodeGeometryAttributeTransfer *)params.node().storage;
const AttributeDomain dst_domain = (AttributeDomain)storage.domain;
const GeometryNodeAttributeTransferMappingMode mapping =
(GeometryNodeAttributeTransferMappingMode)storage.mapping;
dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
const CustomDataType result_data_type = get_result_data_type(src_geometry_set,
src_attribute_name);
if (dst_geometry_set.has<MeshComponent>()) {
transfer_attribute(src_geometry_set,
dst_geometry_set.get_component_for_write<MeshComponent>(),
dst_domain,
result_data_type,
src_attribute_name,
dst_attribute_name);
}
if (dst_geometry_set.has<PointCloudComponent>()) {
transfer_attribute(src_geometry_set,
dst_geometry_set.get_component_for_write<PointCloudComponent>(),
dst_domain,
result_data_type,
src_attribute_name,
dst_attribute_name);
}
params.set_output("Geometry", dst_geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_transfer()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(
&ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out);
node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
node_type_storage(&ntype,
"NodeGeometryAttributeTransfer",
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
ntype.draw_buttons = geo_node_attribute_transfer_layout;
nodeRegisterType(&ntype);
}

View File

@@ -28,6 +28,7 @@
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
#include "BKE_pointcloud.h"
#include "UI_interface.h"
@@ -249,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
template<typename T>
BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
const Span<float3> bary_coords,
const Span<int> looptri_indices,
const Span<T> data_in,
MutableSpan<T> data_out)
{
BLI_assert(data_in.size() == mesh.totvert);
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
const int v0_index = mesh.mloop[looptri.tri[0]].v;
const int v1_index = mesh.mloop[looptri.tri[1]].v;
const int v2_index = mesh.mloop[looptri.tri[2]].v;
const T &v0 = data_in[v0_index];
const T &v1 = data_in[v1_index];
const T &v2 = data_in[v2_index];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
data_out[i] = interpolated_value;
}
}
template<typename T>
BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
const Span<float3> bary_coords,
const Span<int> looptri_indices,
const Span<T> data_in,
MutableSpan<T> data_out)
{
BLI_assert(data_in.size() == mesh.totloop);
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
const int loop_index_0 = looptri.tri[0];
const int loop_index_1 = looptri.tri[1];
const int loop_index_2 = looptri.tri[2];
const T &v0 = data_in[loop_index_0];
const T &v1 = data_in[loop_index_1];
const T &v2 = data_in[loop_index_2];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
data_out[i] = interpolated_value;
}
}
template<typename T>
BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<T> data_in,
MutableSpan<T> data_out)
{
BLI_assert(data_in.size() == mesh.totpoly);
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : data_out.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const int poly_index = looptri.poly;
data_out[i] = data_in[poly_index];
}
}
template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
const AttributeDomain source_domain,
Span<T> source_span,
MutableSpan<T> output_span)
const GVArray &source_data,
GMutableSpan output_data)
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span);
bke::mesh_surface_sample::sample_point_attribute(
mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
interpolate_attribute_corner<T>(
mesh, bary_coords, looptri_indices, source_span, output_span);
bke::mesh_surface_sample::sample_corner_attribute(
mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_FACE: {
interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span);
bke::mesh_surface_sample::sample_face_attribute(
mesh, looptri_indices, source_data, output_data);
break;
}
default: {
@@ -395,22 +324,22 @@ BLI_NOINLINE static void interpolate_existing_attributes(
continue;
}
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
const int offset = instance_start_offsets[i_instance];
Span<float3> bary_coords = bary_coords_array[i_instance];
Span<int> looptri_indices = looptri_indices_array[i_instance];
GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
interpolate_attribute(
mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
i_instance++;
}
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
GVArray_Span<T> source_span{*source_attribute};
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
const int offset = instance_start_offsets[i_instance];
Span<float3> bary_coords = bary_coords_array[i_instance];
Span<int> looptri_indices = looptri_indices_array[i_instance];
MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
interpolate_attribute<T>(
mesh, bary_coords, looptri_indices, source_domain, source_span, instance_span);
i_instance++;
}
});
}