/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" #include "UI_resources.h" #include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_primitive_cylinder_cc { NODE_STORAGE_FUNCS(NodeGeometryMeshCylinder) static void node_declare(NodeDeclarationBuilder &b) { b.add_input(N_("Vertices")) .default_value(32) .min(3) .max(512) .description(N_("The number of vertices on the top and bottom circles")); b.add_input(N_("Side Segments")) .default_value(1) .min(1) .max(512) .description(N_("The number of rectangular segments along each side")); b.add_input(N_("Fill Segments")) .default_value(1) .min(1) .max(512) .description(N_("The number of concentric rings used to fill the round faces")); b.add_input(N_("Radius")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE) .description(N_("The radius of the cylinder")); b.add_input(N_("Depth")) .default_value(2.0f) .min(0.0f) .subtype(PROP_DISTANCE) .description(N_("The height of the cylinder")); b.add_output(N_("Mesh")); b.add_output(N_("Top")).field_source(); b.add_output(N_("Side")).field_source(); b.add_output(N_("Bottom")).field_source(); } static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); } static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMeshCylinder *node_storage = MEM_cnew(__func__); node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; node->storage = node_storage; } static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *vertices_socket = static_cast(node->inputs.first); bNodeSocket *rings_socket = vertices_socket->next; bNodeSocket *fill_subdiv_socket = rings_socket->next; const NodeGeometryMeshCylinder &storage = node_storage(*node); const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; const bool has_fill = fill != GEO_NODE_MESH_CIRCLE_FILL_NONE; nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill); } static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryMeshCylinder &storage = node_storage(params.node()); const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; const float radius = params.extract_input("Radius"); const float depth = params.extract_input("Depth"); const int circle_segments = params.extract_input("Vertices"); if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); params.set_default_remaining_outputs(); return; } const int side_segments = params.extract_input("Side Segments"); if (side_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); params.set_default_remaining_outputs(); return; } const bool no_fill = fill == GEO_NODE_MESH_CIRCLE_FILL_NONE; const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); if (fill_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); params.set_default_remaining_outputs(); return; } ConeAttributeOutputs attribute_outputs; if (params.output_is_required("Top")) { attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); } if (params.output_is_required("Bottom")) { attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); } if (params.output_is_required("Side")) { attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); } /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, circle_segments, side_segments, fill_segments, fill, attribute_outputs); if (attribute_outputs.top_id) { params.set_output("Top", AnonymousAttributeFieldInput::Create( std::move(attribute_outputs.top_id), params.attribute_producer_name())); } if (attribute_outputs.bottom_id) { params.set_output( "Bottom", AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.bottom_id), params.attribute_producer_name())); } if (attribute_outputs.side_id) { params.set_output("Side", AnonymousAttributeFieldInput::Create( std::move(attribute_outputs.side_id), params.attribute_producer_name())); } params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes::node_geo_mesh_primitive_cylinder_cc void register_node_type_geo_mesh_primitive_cylinder() { namespace file_ns = blender::nodes::node_geo_mesh_primitive_cylinder_cc; static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY); ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; node_type_storage( &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); }