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:
authorJohnny Matthews <johnny.matthews@gmail.com>2021-12-06 20:58:08 +0300
committerJohnny Matthews <johnny.matthews@gmail.com>2021-12-06 20:58:08 +0300
commit2814740f5be86fc389ba82ffbb3a40c43f47a9f5 (patch)
tree42db0205adcb154724394527c6b2c5c23150c084 /source/blender/nodes
parentee4ed99866fbb7ab048b637b2d71a872b7eef2b5 (diff)
Geometry Nodes: 4 Field Inputs for Mesh Topology Data
Creates 4 new nodes which provide topology information for the mesh. Values are interpolated from the primary domain in each case using basic attribute interpolation. Vertex Neighbors - Vertex Count - Face Count Face Neighbors - Vertex Count - Neighboring Face Count Edge Vertices - Vertex Index 1 - Vertex Index 2 - Position 1 - Position 2 Face Area - Face Area Differential Revision: https://developer.blender.org/D13343
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/NOD_geometry.h4
-rw-r--r--source/blender/nodes/NOD_static_types.h4
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc198
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc101
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc167
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc165
7 files changed, 643 insertions, 0 deletions
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index a3d36788ee2..a64c53c32e1 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -104,6 +104,10 @@ void register_node_type_geo_input_id(void);
void register_node_type_geo_input_index(void);
void register_node_type_geo_input_material_index(void);
void register_node_type_geo_input_material(void);
+void register_node_type_geo_input_mesh_edge_vertices(void);
+void register_node_type_geo_input_mesh_face_area(void);
+void register_node_type_geo_input_mesh_face_neighbors(void);
+void register_node_type_geo_input_mesh_vertex_neighbors(void);
void register_node_type_geo_input_normal(void);
void register_node_type_geo_input_position(void);
void register_node_type_geo_input_radius(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 7df670c5397..5ef0fc52877 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -357,6 +357,10 @@ DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "")
DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "")
DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "")
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 4a34e1bd6d0..2d7e0293b62 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -122,6 +122,10 @@ set(SRC
nodes/node_geo_input_index.cc
nodes/node_geo_input_material_index.cc
nodes/node_geo_input_material.cc
+ nodes/node_geo_input_mesh_edge_vertices.cc
+ nodes/node_geo_input_mesh_face_area.cc
+ nodes/node_geo_input_mesh_face_neighbors.cc
+ nodes/node_geo_input_mesh_vertex_neighbors.cc
nodes/node_geo_input_normal.cc
nodes/node_geo_input_position.cc
nodes/node_geo_input_radius.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
new file mode 100644
index 00000000000..a9385b50600
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
@@ -0,0 +1,198 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_edge_vertices_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Vertex Index 1"))
+ .field_source()
+ .description(N_("The index of the first vertex in the edge"));
+ b.add_output<decl::Int>(N_("Vertex Index 2"))
+ .field_source()
+ .description(N_("The index of the second vertex in the edge"));
+ b.add_output<decl::Vector>(N_("Position 1"))
+ .field_source()
+ .description(N_("The position of the first vertex in the edge"));
+ b.add_output<decl::Vector>(N_("Position 2"))
+ .field_source()
+ .description(N_("The position of the second vertex in the edge"));
+}
+
+enum VertexNumber { VERTEX_ONE, VERTEX_TWO };
+
+static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component,
+ const VertexNumber vertex,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+ if (domain == ATTR_DOMAIN_EDGE) {
+
+ if (vertex == VERTEX_ONE) {
+ return VArray<int>::ForFunc(mesh->totpoly,
+ [mesh](const int i) -> int { return mesh->medge[i].v1; });
+ }
+ return VArray<int>::ForFunc(mesh->totpoly,
+ [mesh](const int i) -> int { return mesh->medge[i].v2; });
+ }
+ return {};
+}
+
+class EdgeVerticesFieldInput final : public fn::FieldInput {
+ private:
+ VertexNumber vertex_;
+
+ public:
+ EdgeVerticesFieldInput(VertexNumber vertex)
+ : fn::FieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_edge_vertices_gvarray(mesh_component, vertex_, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(vertex_, 9872922352);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const EdgeVerticesFieldInput *other_field = dynamic_cast<const EdgeVerticesFieldInput *>(
+ &other)) {
+ return vertex_ == other_field->vertex_;
+ }
+ return false;
+ }
+};
+
+static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component,
+ const VertexNumber vertex,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (vertex == VERTEX_ONE) {
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForFunc(
+ mesh->totedge,
+ [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }),
+ ATTR_DOMAIN_EDGE,
+ domain);
+ }
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForFunc(
+ mesh->totedge,
+ [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }),
+ ATTR_DOMAIN_EDGE,
+ domain);
+}
+
+class EdgePositionFieldInput final : public fn::FieldInput {
+ private:
+ VertexNumber vertex_;
+
+ public:
+ EdgePositionFieldInput(VertexNumber vertex)
+ : fn::FieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_edge_positions_gvarray(mesh_component, vertex_, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(vertex_, 2359867235);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const EdgePositionFieldInput *other_field = dynamic_cast<const EdgePositionFieldInput *>(
+ &other)) {
+ return vertex_ == other_field->vertex_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> vertex_field_1{std::make_shared<EdgeVerticesFieldInput>(VERTEX_ONE)};
+ Field<int> vertex_field_2{std::make_shared<EdgeVerticesFieldInput>(VERTEX_TWO)};
+ Field<float3> position_field_1{std::make_shared<EdgePositionFieldInput>(VERTEX_ONE)};
+ Field<float3> position_field_2{std::make_shared<EdgePositionFieldInput>(VERTEX_TWO)};
+
+ params.set_output("Vertex Index 1", std::move(vertex_field_1));
+ params.set_output("Vertex Index 2", std::move(vertex_field_2));
+ params.set_output("Position 1", std::move(position_field_1));
+ params.set_output("Position 2", std::move(position_field_2));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_edge_vertices_cc
+
+void register_node_type_geo_input_mesh_edge_vertices()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_edge_vertices_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_EDGE_VERTICES, "Edge Vertices", NODE_CLASS_INPUT, 0);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
new file mode 100644
index 00000000000..d3605cd87e5
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
@@ -0,0 +1,101 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_face_area_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Area"))
+ .field_source()
+ .description(N_("The surface area of each of the mesh's faces"));
+}
+
+static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ auto area_fn = [mesh](const int i) -> float {
+ const MPoly *mp = &mesh->mpoly[i];
+ return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert);
+ };
+
+ return component.attribute_try_adapt_domain<float>(
+ VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
+}
+
+class FaceAreaFieldInput final : public fn::FieldInput {
+ public:
+ FaceAreaFieldInput() : fn::FieldInput(CPPType::get<float>(), "Face Area Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_face_area_gvarray(mesh_component, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 1346334523;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const FaceAreaFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ params.set_output("Area", Field<float>(std::make_shared<FaceAreaFieldInput>()));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_face_area_cc
+
+void register_node_type_geo_input_mesh_face_area()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_face_area_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_FACE_AREA, "Face Area", NODE_CLASS_INPUT, 0);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
new file mode 100644
index 00000000000..8f3cf623fe9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_face_neighbors_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Vertex Count"))
+ .field_source()
+ .description(N_("Number of edges or points in the face"));
+ b.add_output<decl::Int>(N_("Face Count"))
+ .field_source()
+ .description(N_("Number of faces which share an edge with the face"));
+}
+
+static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ Array<int> edge_count(mesh->totedge, 0);
+ for (const int i : IndexRange(mesh->totloop)) {
+ edge_count[mesh->mloop[i].e]++;
+ }
+
+ Array<int> poly_count(mesh->totpoly, 0);
+ for (const int poly_num : IndexRange(mesh->totpoly)) {
+ MPoly &poly = mesh->mpoly[poly_num];
+ for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) {
+ poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1;
+ }
+ }
+
+ return component.attribute_try_adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
+}
+
+class FaceNeighborCountFieldInput final : public fn::FieldInput {
+ public:
+ FaceNeighborCountFieldInput() : fn::FieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_neighbor_count_gvarray(mesh_component, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 823543774;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const FaceNeighborCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ return component.attribute_try_adapt_domain<int>(
+ VArray<int>::ForFunc(mesh->totpoly,
+ [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }),
+ ATTR_DOMAIN_FACE,
+ domain);
+}
+
+class FaceVertexCountFieldInput final : public fn::FieldInput {
+ public:
+ FaceVertexCountFieldInput() : fn::FieldInput(CPPType::get<int>(), "Vertex Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_vertex_count_gvarray(mesh_component, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 236235463634;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const FaceVertexCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> vertex_count_field{std::make_shared<FaceVertexCountFieldInput>()};
+ Field<int> neighbor_count_field{std::make_shared<FaceNeighborCountFieldInput>()};
+ params.set_output("Vertex Count", std::move(vertex_count_field));
+ params.set_output("Face Count", std::move(neighbor_count_field));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_face_neighbors_cc
+
+void register_node_type_geo_input_mesh_face_neighbors()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_face_neighbors_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, "Face Neighbors", NODE_CLASS_INPUT, 0);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
new file mode 100644
index 00000000000..edc265acb00
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
@@ -0,0 +1,165 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_vertex_neighbors_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Vertex Count"))
+ .field_source()
+ .description(N_("Vertex count and edge count are equal"));
+ b.add_output<decl::Int>(N_("Face Count"))
+ .field_source()
+ .description(N_("Number of faces that contain the vertex"));
+}
+
+static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<int> vertices(mesh->totvert, 0);
+ for (const int i : IndexRange(mesh->totedge)) {
+ vertices[mesh->medge[i].v1]++;
+ vertices[mesh->medge[i].v2]++;
+ }
+ return VArray<int>::ForContainer(std::move(vertices));
+ }
+ return {};
+}
+
+class VertexCountFieldInput final : public fn::FieldInput {
+ public:
+ VertexCountFieldInput() : fn::FieldInput(CPPType::get<int>(), "Vertex Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_vertex_count_gvarray(mesh_component, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 23574528465;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const VertexCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<int> vertices(mesh->totvert, 0);
+ for (const int i : IndexRange(mesh->totloop)) {
+ int vertex = mesh->mloop[i].v;
+ vertices[vertex]++;
+ }
+ return VArray<int>::ForContainer(std::move(vertices));
+ }
+ return {};
+}
+
+class VertexFaceCountFieldInput final : public fn::FieldInput {
+ public:
+ VertexFaceCountFieldInput() : fn::FieldInput(CPPType::get<int>(), "Vertex Face Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_face_count_gvarray(mesh_component, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 3462374322;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const VertexFaceCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> vertex_field{std::make_shared<VertexCountFieldInput>()};
+ Field<int> face_field{std::make_shared<VertexFaceCountFieldInput>()};
+
+ params.set_output("Vertex Count", std::move(vertex_field));
+ params.set_output("Face Count", std::move(face_field));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_vertex_neighbors_cc
+
+void register_node_type_geo_input_mesh_vertex_neighbors()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_vertex_neighbors_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, "Vertex Neighbors", NODE_CLASS_INPUT, 0);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}