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
This commit is contained in:
parent
fe82b8d1e8
commit
f4af21038d
|
@ -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:
|
||||
/**
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -388,6 +390,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
|
||||
*
|
||||
|
|
|
@ -122,6 +122,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
|
||||
* \{ */
|
||||
|
|
|
@ -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"
|
||||
|
@ -567,6 +569,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
|
||||
* \{ */
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue