diff options
author | Johnny Matthews <johnny.matthews@gmail.com> | 2021-12-06 20:58:08 +0300 |
---|---|---|
committer | Johnny Matthews <johnny.matthews@gmail.com> | 2021-12-06 20:58:08 +0300 |
commit | 2814740f5be86fc389ba82ffbb3a40c43f47a9f5 (patch) | |
tree | 42db0205adcb154724394527c6b2c5c23150c084 /source | |
parent | ee4ed99866fbb7ab048b637b2d71a872b7eef2b5 (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')
9 files changed, 651 insertions, 1 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index fcfbe3ccc6f..f925834beac 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1530,7 +1530,10 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SET_ID 1135 #define GEO_NODE_ATTRIBUTE_DOMAIN_SIZE 1136 #define GEO_NODE_DUAL_MESH 1137 - +#define GEO_NODE_INPUT_MESH_EDGE_VERTICES 1138 +#define GEO_NODE_INPUT_MESH_FACE_AREA 1139 +#define GEO_NODE_INPUT_MESH_FACE_NEIGHBORS 1140 +#define GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS 1141 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 359dbaab12b..6558ad894b8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5900,6 +5900,10 @@ static void registerGeometryNodes() register_node_type_geo_input_index(); register_node_type_geo_input_material_index(); register_node_type_geo_input_material(); + register_node_type_geo_input_mesh_edge_vertices(); + register_node_type_geo_input_mesh_face_area(); + register_node_type_geo_input_mesh_face_neighbors(); + register_node_type_geo_input_mesh_vertex_neighbors(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); register_node_type_geo_input_radius(); 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); +} |