Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
| Show All 25 Lines | |||||
| namespace blender::nodes::node_geo_input_normal_cc { | namespace blender::nodes::node_geo_input_normal_cc { | ||||
| static void node_declare(NodeDeclarationBuilder &b) | static void node_declare(NodeDeclarationBuilder &b) | ||||
| { | { | ||||
| b.add_output<decl::Vector>(N_("Normal")).field_source(); | 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) | 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)); | params.set_output("Normal", std::move(normal_field)); | ||||
| } | } | ||||
| } // namespace blender::nodes::node_geo_input_normal_cc | } // namespace blender::nodes::node_geo_input_normal_cc | ||||
| void register_node_type_geo_input_normal() | void register_node_type_geo_input_normal() | ||||
| { | { | ||||
| namespace file_ns = blender::nodes::node_geo_input_normal_cc; | namespace file_ns = blender::nodes::node_geo_input_normal_cc; | ||||
| static bNodeType ntype; | static bNodeType ntype; | ||||
| geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT); | geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT); | ||||
| ntype.geometry_node_execute = file_ns::node_geo_exec; | ntype.geometry_node_execute = file_ns::node_geo_exec; | ||||
| ntype.declare = file_ns::node_declare; | ntype.declare = file_ns::node_declare; | ||||
| nodeRegisterType(&ntype); | nodeRegisterType(&ntype); | ||||
| } | } | ||||