1
1

Compare commits

...

7 Commits

Author SHA1 Message Date
0a3f4c9d45 support normal smoothing 2022-06-23 12:34:54 +02:00
bc78c0ec25 progress 2022-06-22 15:21:55 +02:00
9212204389 progress 2022-06-22 13:48:12 +02:00
8100721dab support translation 2022-06-22 12:40:40 +02:00
2d0dc88209 Merge branch 'master' into deform-curves-with-surface 2022-06-22 10:56:21 +02:00
c57ed65cc8 add comment 2022-05-05 15:51:01 +02:00
46022f8556 initial commit 2022-05-05 15:38:23 +02:00
7 changed files with 262 additions and 0 deletions

View File

@@ -113,6 +113,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeMeshBoolean")
yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
yield NodeItem("GeometryNodeSampleMeshDeformation")
yield NodeItem("GeometryNodeSplitEdges")
yield NodeItem("GeometryNodeSubdivideMesh")
yield NodeItem("GeometryNodeSubdivisionSurface")

View File

@@ -1496,6 +1496,7 @@ struct TexResult;
#define GEO_NODE_INPUT_INSTANCE_ROTATION 1159
#define GEO_NODE_INPUT_INSTANCE_SCALE 1160
#define GEO_NODE_VOLUME_CUBE 1161
#define GEO_NODE_SAMPLE_MESH_DEFORMATION 1162
/** \} */

View File

@@ -4811,6 +4811,7 @@ static void registerGeometryNodes()
register_node_type_geo_realize_instances();
register_node_type_geo_remove_attribute();
register_node_type_geo_rotate_instances();
register_node_type_geo_sample_mesh_deformation();
register_node_type_geo_scale_elements();
register_node_type_geo_scale_instances();
register_node_type_geo_separate_components();

View File

@@ -109,6 +109,7 @@ void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
void register_node_type_geo_remove_attribute(void);
void register_node_type_geo_rotate_instances(void);
void register_node_type_geo_sample_mesh_deformation(void);
void register_node_type_geo_scale_elements(void);
void register_node_type_geo_scale_instances(void);
void register_node_type_geo_select_by_handle_type(void);

View File

@@ -399,6 +399,7 @@ DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", Tri
DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
DefNode(GeometryNode, GEO_NODE_SAMPLE_MESH_DEFORMATION, 0, "SAMPLE_MESH_DEFORMATION", SampleMeshDeformation, "Sample Mesh Deformation", "")
/* undefine macros */
#undef DefNode

View File

@@ -119,6 +119,7 @@ set(SRC
nodes/node_geo_realize_instances.cc
nodes/node_geo_remove_attribute.cc
nodes/node_geo_rotate_instances.cc
nodes/node_geo_sample_mesh_deformation.cc
nodes/node_geo_scale_elements.cc
nodes/node_geo_scale_instances.cc
nodes/node_geo_separate_components.cc

View File

@@ -0,0 +1,256 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_type_conversions.hh"
#include "BLI_float3x3.hh"
#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "NOD_socket_search_link.hh"
#include "GEO_reverse_uv_sampler.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_sample_mesh_deformation_cc {
using attribute_math::mix3;
using geometry::ReverseUVSampler;
NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
b.add_input<decl::Vector>(N_("Rest Position")).hide_value().supports_field();
b.add_input<decl::Vector>(N_("UV Map")).hide_value().supports_field();
b.add_input<decl::Vector>(N_("Sample UV")).supports_field();
b.add_input<decl::Bool>(N_("Smooth Normals")).default_value(true);
b.add_output<decl::Vector>(N_("Translation")).dependent_field({3});
b.add_output<decl::Vector>(N_("Rotation")).dependent_field({3});
}
static void node_layout(uiLayout *UNUSED(layout), bContext *UNUSED(C), PointerRNA *UNUSED(ptr))
{
}
static void node_init(bNodeTree *UNUSED(tree), bNode *UNUSED(node))
{
}
static void node_update(bNodeTree *UNUSED(ntree), bNode *UNUSED(node))
{
}
class SampleMeshDeformationFunction : public fn::MultiFunction {
private:
GeometrySet geometry_;
Mesh *mesh_;
Span<MVert> verts_;
Array<float3> rest_positions_;
Array<float2> uv_map_;
Span<float3> corner_normals_;
Span<float3> face_normals_;
std::optional<ReverseUVSampler> reverse_uv_sampler;
bool smooth_normals_;
public:
SampleMeshDeformationFunction(GeometrySet geometry,
VArray<float3> rest_positions,
VArray<float2> uv_map,
const bool smooth_normals)
: geometry_(std::move(geometry)),
rest_positions_(VArray_Span(rest_positions)),
uv_map_(VArray_Span(uv_map)),
smooth_normals_(smooth_normals)
{
mesh_ = geometry_.get_mesh_for_write();
verts_ = Span<MVert>(mesh_->mvert, mesh_->totvert);
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
BKE_mesh_runtime_looptri_len(mesh_)};
if (!CustomData_has_layer(&mesh_->ldata, CD_NORMAL)) {
BKE_mesh_calc_normals_split(mesh_);
}
corner_normals_ = {
reinterpret_cast<const float3 *>(CustomData_get_layer(&mesh_->ldata, CD_NORMAL)),
mesh_->totloop};
face_normals_ = {reinterpret_cast<const float3 *>(BKE_mesh_poly_normals_ensure(mesh_)),
mesh_->totpoly};
reverse_uv_sampler.emplace(uv_map_, looptris);
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Sample Mesh Deformation"};
signature.single_input<float2>("Sample UV");
signature.single_output<float3>("Translation");
signature.single_output<float3>("Rotation");
return signature.build();
}
void call(const IndexMask mask,
fn::MFParams params,
fn::MFContext UNUSED(context)) const override
{
const VArray_Span<float2> sample_uvs = params.readonly_single_input<float2>(0, "Sample UV");
MutableSpan<float3> r_translations = params.uninitialized_single_output_if_required<float3>(
1, "Translation");
MutableSpan<float3> r_rotations = params.uninitialized_single_output_if_required<float3>(
2, "Rotation");
const bool compute_translation = !r_translations.is_empty();
const bool compute_rotation = !r_rotations.is_empty();
for (const int i : mask) {
const float2 &sample_uv = sample_uvs[i];
const ReverseUVSampler::Result sample_result = reverse_uv_sampler->sample(sample_uv);
if (sample_result.type != ReverseUVSampler::ResultType::Ok) {
if (compute_translation) {
r_translations[i] = float3(0.0f);
}
if (compute_rotation) {
r_rotations[i] = float3(0.0f);
}
continue;
}
const MLoopTri &looptri = *sample_result.looptri;
const float3 &bary_weights = sample_result.bary_weights;
const int corner_0 = looptri.tri[0];
const int corner_1 = looptri.tri[1];
const int corner_2 = looptri.tri[2];
const int vert_0 = mesh_->mloop[corner_0].v;
const int vert_1 = mesh_->mloop[corner_1].v;
const int vert_2 = mesh_->mloop[corner_2].v;
const float3 &old_pos_0 = rest_positions_[corner_0];
const float3 &old_pos_1 = rest_positions_[corner_1];
const float3 &old_pos_2 = rest_positions_[corner_2];
const float3 &new_pos_0 = verts_[vert_0].co;
const float3 &new_pos_1 = verts_[vert_1].co;
const float3 &new_pos_2 = verts_[vert_2].co;
if (compute_translation) {
const float3 old_pos = mix3(bary_weights, old_pos_0, old_pos_1, old_pos_2);
const float3 new_pos = mix3(bary_weights, new_pos_0, new_pos_1, new_pos_2);
const float3 translation = new_pos - old_pos;
r_translations[i] = translation;
}
if (compute_rotation) {
const float3 old_dir_1 = old_pos_1 - old_pos_0;
const float3 old_dir_2 = old_pos_2 - old_pos_0;
const float3 new_dir_1 = new_pos_1 - new_pos_0;
const float3 new_dir_2 = new_pos_2 - new_pos_0;
/* This can lead to issues if the original face was not planar. */
const float3 old_normal = math::normalize(math::cross(old_dir_1, old_dir_2));
const float3 old_tangent_x = math::normalize(math::cross(old_normal, old_dir_1));
const float3 old_tangent_y = math::cross(old_normal, old_tangent_x);
const float3 new_normal = [&]() {
if (smooth_normals_) {
const float3 &new_normal_0 = corner_normals_[corner_0];
const float3 &new_normal_1 = corner_normals_[corner_1];
const float3 &new_normal_2 = corner_normals_[corner_2];
return math::normalize(mix3(bary_weights, new_normal_0, new_normal_1, new_normal_2));
}
return face_normals_[looptri.poly];
}();
const float3 new_tangent_x = math::normalize(math::cross(new_normal, new_dir_1));
const float3 new_tangent_y = math::cross(new_normal, new_tangent_x);
float3x3 old_transform;
copy_v3_v3(old_transform.values[0], old_tangent_x);
copy_v3_v3(old_transform.values[1], old_tangent_y);
copy_v3_v3(old_transform.values[2], old_normal);
float3x3 new_transform;
copy_v3_v3(new_transform.values[0], new_tangent_x);
copy_v3_v3(new_transform.values[1], new_tangent_y);
copy_v3_v3(new_transform.values[2], new_normal);
const float3x3 old_transform_inverse = old_transform.transposed();
const float3x3 transform = new_transform * old_transform_inverse;
float3 euler;
mat3_to_eul(euler, transform.values);
r_rotations[i] = euler;
}
}
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
Field<float3> rest_positions_field = params.extract_input<Field<float3>>("Rest Position");
Field<float3> uv_map_field = params.extract_input<Field<float3>>("UV Map");
Field<float3> sample_uv_field = params.extract_input<Field<float3>>("Sample UV");
const bool smooth_normals = params.extract_input<bool>("Smooth Normals");
if (!geometry.has_mesh()) {
params.set_default_remaining_outputs();
return;
}
geometry.ensure_owns_direct_data();
const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
const MeshComponent &mesh_component = *geometry.get_component_for_read<MeshComponent>();
GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_CORNER};
FieldEvaluator field_evaluator{field_context,
mesh_component.attribute_domain_num(ATTR_DOMAIN_CORNER)};
field_evaluator.add(rest_positions_field);
field_evaluator.add(conversions.try_convert(std::move(uv_map_field), CPPType::get<float2>()));
field_evaluator.evaluate();
VArray<float3> rest_positions = field_evaluator.get_evaluated<float3>(0);
VArray<float2> uv_map = field_evaluator.get_evaluated<float2>(1);
auto fn = std::make_unique<SampleMeshDeformationFunction>(
std::move(geometry), std::move(rest_positions), std::move(uv_map), smooth_normals);
Field<float2> sample_uv_field_float2 = conversions.try_convert(std::move(sample_uv_field),
CPPType::get<float2>());
auto operation = std::make_shared<FieldOperation>(
std::move(fn), Vector<GField>{std::move(sample_uv_field_float2)});
params.set_output("Translation", Field<float3>(operation, 0));
params.set_output("Rotation", Field<float3>(operation, 1));
params.set_default_remaining_outputs();
}
} // namespace blender::nodes::node_geo_sample_mesh_deformation_cc
void register_node_type_geo_sample_mesh_deformation()
{
namespace file_ns = blender::nodes::node_geo_sample_mesh_deformation_cc;
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_SAMPLE_MESH_DEFORMATION, "Sample Mesh Deformation", NODE_CLASS_GEOMETRY);
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.draw_buttons = file_ns::node_layout;
ntype.declare = file_ns::node_declare;
node_type_init(&ntype, file_ns::node_init);
node_type_update(&ntype, file_ns::node_update);
nodeRegisterType(&ntype);
}