Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-01-11 01:41:05 +0300
committerHans Goudey <h.goudey@me.com>2022-01-11 01:41:05 +0300
commitf4af21038d82bba525dec4bfd291c43504607acf (patch)
treee8ffe16b4059d1a269cf497b1475a826afb39f80
parentfe82b8d1e8425c2e9ba30f85de20f85de274dd99 (diff)
Geometry Nodes: Move normal field input to be usable elsewhere
This commit moves the normal field input to `BKE_geometry_set.hh` from the node file so that normals can be used as an implicit input to other nodes. Differential Revision: https://developer.blender.org/D13779
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh24
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc94
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc122
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_normal.cc240
5 files changed, 285 insertions, 239 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index a429aecd828..acb89637f20 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -1078,6 +1078,30 @@ class IDAttributeFieldInput : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override;
};
+VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain);
+
+VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
+ const Mesh &mesh,
+ const IndexMask mask,
+ const AttributeDomain domain);
+
+class NormalFieldInput : public GeometryFieldInput {
+ public:
+ NormalFieldInput() : GeometryFieldInput(CPPType::get<float3>())
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const override;
+
+ std::string socket_inspection_name() const override;
+
+ uint64_t hash() const override;
+ bool is_equal_to(const fn::FieldNode &other) const override;
+};
+
class AnonymousAttributeFieldInput : public GeometryFieldInput {
private:
/**
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 1e24b29038d..16edbc36f9c 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "DNA_ID_enums.h"
#include "DNA_curve_types.h"
@@ -389,6 +391,98 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen
namespace blender::bke {
/* -------------------------------------------------------------------- */
+/** \name Curve Normals Access
+ * \{ */
+
+static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
+{
+ Span<int> offsets = spline.control_point_offsets();
+ Span<float3> evaluated_normals = spline.evaluated_normals();
+ for (const int i : IndexRange(spline.size())) {
+ normals[i] = evaluated_normals[offsets[i]];
+ }
+}
+
+static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
+{
+ normals.copy_from(spline.evaluated_normals());
+}
+
+/**
+ * Because NURBS control points are not necessarily on the path, the normal at the control points
+ * is not well defined, so create a temporary poly spline to find the normals. This requires extra
+ * copying currently, but may be more efficient in the future if attributes have some form of CoW.
+ */
+static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
+{
+ PolySpline poly_spline;
+ poly_spline.resize(spline.size());
+ poly_spline.positions().copy_from(spline.positions());
+ poly_spline.tilts().copy_from(spline.tilts());
+ normals.copy_from(poly_spline.evaluated_normals());
+}
+
+static Array<float3> curve_normal_point_domain(const CurveEval &curve)
+{
+ Span<SplinePtr> splines = curve.splines();
+ Array<int> offsets = curve.control_point_offsets();
+ const int total_size = offsets.last();
+ Array<float3> normals(total_size);
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
+ switch (splines[i]->type()) {
+ case Spline::Type::Bezier:
+ calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
+ break;
+ case Spline::Type::Poly:
+ calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
+ break;
+ case Spline::Type::NURBS:
+ calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
+ break;
+ }
+ }
+ });
+ return normals;
+}
+
+VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain)
+{
+ const CurveEval *curve = component.get_for_read();
+ if (curve == nullptr) {
+ return nullptr;
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ const Span<SplinePtr> splines = curve->splines();
+
+ /* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
+ * This is only possible when there is only one poly spline. */
+ if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
+ const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
+ return VArray<float3>::ForSpan(spline.evaluated_normals());
+ }
+
+ Array<float3> normals = curve_normal_point_domain(*curve);
+ return VArray<float3>::ForContainer(std::move(normals));
+ }
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ Array<float3> point_normals = curve_normal_point_domain(*curve);
+ VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
+ return component.attribute_try_adapt_domain<float3>(
+ std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
+ }
+
+ return nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Builtin Spline Attributes
*
* Attributes with a value for every spline, stored contiguously or in every spline separately.
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index cc15e6d7b84..2b4238d26bb 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -123,6 +123,128 @@ void MeshComponent::ensure_owns_direct_data()
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Mesh Normals Field Input
+ * \{ */
+
+namespace blender::bke {
+
+static VArray<float3> mesh_face_normals(const Mesh &mesh,
+ const Span<MVert> verts,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ const IndexMask mask)
+{
+ /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
+ if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) &&
+ CustomData_has_layer(&mesh.pdata, CD_NORMAL)) {
+ const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
+
+ return VArray<float3>::ForSpan({(const float3 *)data, polys.size()});
+ }
+
+ auto normal_fn = [verts, polys, loops](const int i) -> float3 {
+ float3 normal;
+ const MPoly &poly = polys[i];
+ BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal);
+ return normal;
+ };
+
+ return VArray<float3>::ForFunc(mask.min_array_size(), normal_fn);
+}
+
+static VArray<float3> mesh_vertex_normals(const Mesh &mesh,
+ const Span<MVert> verts,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ const IndexMask mask)
+{
+ /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
+ if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) &&
+ CustomData_has_layer(&mesh.vdata, CD_NORMAL)) {
+ const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
+
+ return VArray<float3>::ForSpan({(const float3 *)data, mesh.totvert});
+ }
+
+ /* If the normals are dirty, they must be recalculated for the output of this node's field
+ * source. Ideally vertex normals could be calculated lazily on a const mesh, but that's not
+ * possible at the moment, so we take ownership of the results. Sadly we must also create a copy
+ * of MVert to use the mesh normals API. This can be improved by adding mutex-protected lazy
+ * calculation of normals on meshes.
+ *
+ * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */
+ Array<MVert> temp_verts(verts);
+ Array<float3> normals(verts.size()); /* Use full size for accumulation from faces. */
+ BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(),
+ mask.min_array_size(),
+ loops.data(),
+ loops.size(),
+ polys.data(),
+ polys.size(),
+ nullptr,
+ (float(*)[3])normals.data());
+
+ return VArray<float3>::ForContainer(std::move(normals));
+}
+
+VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
+ const Mesh &mesh,
+ const IndexMask mask,
+ const AttributeDomain domain)
+{
+ Span<MVert> verts{mesh.mvert, mesh.totvert};
+ Span<MEdge> edges{mesh.medge, mesh.totedge};
+ Span<MPoly> polys{mesh.mpoly, mesh.totpoly};
+ Span<MLoop> loops{mesh.mloop, mesh.totloop};
+
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ return mesh_face_normals(mesh, verts, polys, loops, mask);
+ }
+ case ATTR_DOMAIN_POINT: {
+ return mesh_vertex_normals(mesh, verts, polys, loops, mask);
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* In this case, start with vertex normals and convert to the edge domain, since the
+ * conversion from edges to vertices is very simple. Use the full mask since the edges
+ * might use the vertex normal from any index. */
+ GVArray vert_normals = mesh_vertex_normals(
+ mesh, verts, polys, loops, IndexRange(verts.size()));
+ Span<float3> vert_normals_span = vert_normals.get_internal_span().typed<float3>();
+ Array<float3> edge_normals(mask.min_array_size());
+
+ /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid
+ * calculating unnecessary values and to allow normalizing the result much more simply. */
+ for (const int i : mask) {
+ const MEdge &edge = edges[i];
+ edge_normals[i] = float3::interpolate(
+ vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f)
+ .normalized();
+ }
+
+ return VArray<float3>::ForContainer(std::move(edge_normals));
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* The normals on corners are just the mesh's face normals, so start with the face normal
+ * array and copy the face normal for each of its corners. */
+ VArray<float3> face_normals = mesh_face_normals(
+ mesh, verts, polys, loops, IndexRange(polys.size()));
+
+ /* In this case using the mesh component's generic domain interpolation is fine, the data
+ * will still be normalized, since the face normal is just copied to every corner. */
+ return mesh_component.attribute_try_adapt_domain<float3>(
+ std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
+ }
+ default:
+ return {};
+ }
+}
+
+} // namespace blender::bke
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Attribute Access
* \{ */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 06e0e78745b..c1e386c626b 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -17,6 +17,8 @@
#include "BLI_map.hh"
#include "BLI_task.hh"
+#include "BLT_translation.h"
+
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.hh"
@@ -568,6 +570,48 @@ void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Mesh and Curve Normals Field Input
+ * \{ */
+
+namespace blender::bke {
+
+GVArray NormalFieldInput::get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const
+{
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ if (const Mesh *mesh = mesh_component.get_for_read()) {
+ return mesh_normals_varray(mesh_component, *mesh, mask, domain);
+ }
+ }
+ else if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return curve_normals_varray(curve_component, domain);
+ }
+ return {};
+}
+
+std::string NormalFieldInput::socket_inspection_name() const
+{
+ return TIP_("Normal");
+}
+
+uint64_t NormalFieldInput::hash() const
+{
+ return 213980475983;
+}
+
+bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const
+{
+ return dynamic_cast<const NormalFieldInput *>(&other) != nullptr;
+}
+
+} // namespace blender::bke
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name C API
* \{ */
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
index db5f30fb6c9..120ae0e9bd1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
@@ -31,247 +31,9 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Normal")).field_source();
}
-static VArray<float3> mesh_face_normals(const Mesh &mesh,
- const Span<MVert> verts,
- const Span<MPoly> polys,
- const Span<MLoop> loops,
- const IndexMask mask)
-{
- /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
- if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) &&
- CustomData_has_layer(&mesh.pdata, CD_NORMAL)) {
- const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
-
- return VArray<float3>::ForSpan({(const float3 *)data, polys.size()});
- }
-
- auto normal_fn = [verts, polys, loops](const int i) -> float3 {
- float3 normal;
- const MPoly &poly = polys[i];
- BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal);
- return normal;
- };
-
- return VArray<float3>::ForFunc(mask.min_array_size(), normal_fn);
-}
-
-static VArray<float3> mesh_vertex_normals(const Mesh &mesh,
- const Span<MVert> verts,
- const Span<MPoly> polys,
- const Span<MLoop> loops,
- const IndexMask mask)
-{
- /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
- if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) &&
- CustomData_has_layer(&mesh.vdata, CD_NORMAL)) {
- const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
-
- return VArray<float3>::ForSpan({(const float3 *)data, mesh.totvert});
- }
-
- /* If the normals are dirty, they must be recalculated for the output of this node's field
- * source. Ideally vertex normals could be calculated lazily on a const mesh, but that's not
- * possible at the moment, so we take ownership of the results. Sadly we must also create a copy
- * of MVert to use the mesh normals API. This can be improved by adding mutex-protected lazy
- * calculation of normals on meshes.
- *
- * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */
- Array<MVert> temp_verts(verts);
- Array<float3> normals(verts.size()); /* Use full size for accumulation from faces. */
- BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(),
- mask.min_array_size(),
- loops.data(),
- loops.size(),
- polys.data(),
- polys.size(),
- nullptr,
- (float(*)[3])normals.data());
-
- return VArray<float3>::ForContainer(std::move(normals));
-}
-
-static VArray<float3> construct_mesh_normals_gvarray(const MeshComponent &mesh_component,
- const Mesh &mesh,
- const IndexMask mask,
- const AttributeDomain domain)
-{
- Span<MVert> verts{mesh.mvert, mesh.totvert};
- Span<MEdge> edges{mesh.medge, mesh.totedge};
- Span<MPoly> polys{mesh.mpoly, mesh.totpoly};
- Span<MLoop> loops{mesh.mloop, mesh.totloop};
-
- switch (domain) {
- case ATTR_DOMAIN_FACE: {
- return mesh_face_normals(mesh, verts, polys, loops, mask);
- }
- case ATTR_DOMAIN_POINT: {
- return mesh_vertex_normals(mesh, verts, polys, loops, mask);
- }
- case ATTR_DOMAIN_EDGE: {
- /* In this case, start with vertex normals and convert to the edge domain, since the
- * conversion from edges to vertices is very simple. Use the full mask since the edges
- * might use the vertex normal from any index. */
- GVArray vert_normals = mesh_vertex_normals(
- mesh, verts, polys, loops, IndexRange(verts.size()));
- Span<float3> vert_normals_span = vert_normals.get_internal_span().typed<float3>();
- Array<float3> edge_normals(mask.min_array_size());
-
- /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid
- * calculating unnecessary values and to allow normalizing the result much more simply. */
- for (const int i : mask) {
- const MEdge &edge = edges[i];
- edge_normals[i] = float3::interpolate(
- vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f)
- .normalized();
- }
-
- return VArray<float3>::ForContainer(std::move(edge_normals));
- }
- case ATTR_DOMAIN_CORNER: {
- /* The normals on corners are just the mesh's face normals, so start with the face normal
- * array and copy the face normal for each of its corners. */
- VArray<float3> face_normals = mesh_face_normals(
- mesh, verts, polys, loops, IndexRange(polys.size()));
-
- /* In this case using the mesh component's generic domain interpolation is fine, the data
- * will still be normalized, since the face normal is just copied to every corner. */
- return mesh_component.attribute_try_adapt_domain<float3>(
- std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
- }
- default:
- return {};
- }
-}
-
-static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
-{
- Span<int> offsets = spline.control_point_offsets();
- Span<float3> evaluated_normals = spline.evaluated_normals();
- for (const int i : IndexRange(spline.size())) {
- normals[i] = evaluated_normals[offsets[i]];
- }
-}
-
-static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
-{
- normals.copy_from(spline.evaluated_normals());
-}
-
-/**
- * Because NURBS control points are not necessarily on the path, the normal at the control points
- * is not well defined, so create a temporary poly spline to find the normals. This requires extra
- * copying currently, but may be more efficient in the future if attributes have some form of CoW.
- */
-static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
-{
- PolySpline poly_spline;
- poly_spline.resize(spline.size());
- poly_spline.positions().copy_from(spline.positions());
- poly_spline.tilts().copy_from(spline.tilts());
- normals.copy_from(poly_spline.evaluated_normals());
-}
-
-static Array<float3> curve_normal_point_domain(const CurveEval &curve)
-{
- Span<SplinePtr> splines = curve.splines();
- Array<int> offsets = curve.control_point_offsets();
- const int total_size = offsets.last();
- Array<float3> normals(total_size);
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
- switch (splines[i]->type()) {
- case Spline::Type::Bezier:
- calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
- break;
- case Spline::Type::Poly:
- calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
- break;
- case Spline::Type::NURBS:
- calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
- break;
- }
- }
- });
- return normals;
-}
-
-static VArray<float3> construct_curve_normal_gvarray(const CurveComponent &component,
- const AttributeDomain domain)
-{
- const CurveEval *curve = component.get_for_read();
- if (curve == nullptr) {
- return nullptr;
- }
-
- if (domain == ATTR_DOMAIN_POINT) {
- const Span<SplinePtr> splines = curve->splines();
-
- /* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
- * This is only possible when there is only one poly spline. */
- if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
- const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
- return VArray<float3>::ForSpan(spline.evaluated_normals());
- }
-
- Array<float3> normals = curve_normal_point_domain(*curve);
- return VArray<float3>::ForContainer(std::move(normals));
- }
-
- if (domain == ATTR_DOMAIN_CURVE) {
- Array<float3> point_normals = curve_normal_point_domain(*curve);
- VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
- return component.attribute_try_adapt_domain<float3>(
- std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
- }
-
- return nullptr;
-}
-
-class NormalFieldInput final : public GeometryFieldInput {
- public:
- NormalFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Normal node")
- {
- category_ = Category::Generated;
- }
-
- GVArray get_varray_for_context(const GeometryComponent &component,
- const AttributeDomain domain,
- IndexMask mask) const final
- {
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain);
- }
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_normal_gvarray(curve_component, domain);
- }
- return {};
- }
-
- uint64_t hash() const override
- {
- /* Some random constant hash. */
- return 669605641;
- }
-
- bool is_equal_to(const fn::FieldNode &other) const override
- {
- return dynamic_cast<const NormalFieldInput *>(&other) != nullptr;
- }
-};
-
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> normal_field{std::make_shared<NormalFieldInput>()};
+ Field<float3> normal_field{std::make_shared<bke::NormalFieldInput>()};
params.set_output("Normal", std::move(normal_field));
}