diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f5282123ce8..a67941a3c37 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -656,6 +656,8 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshIcoSphere"), NodeItem("GeometryNodeMeshLine"), NodeItem("GeometryNodeMeshUVSphere"), + NodeItem("GeometryNodePizza"), + ]), GeometryNodeCategory("GEO_OUTPUT", "Output", items=[ NodeItem("GeometryNodeViewer"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 1ff10d06b00..c653ddc4e68 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1493,6 +1493,7 @@ struct TexResult; #define GEO_NODE_STORE_NAMED_ATTRIBUTE 1156 #define GEO_NODE_INPUT_NAMED_ATTRIBUTE 1157 #define GEO_NODE_REMOVE_ATTRIBUTE 1158 +#define GEO_NODE_PIZZA 1159 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index cf3b4f7bdf2..d253f5c2b37 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4789,6 +4789,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_to_curve(); register_node_type_geo_mesh_to_points(); register_node_type_geo_object_info(); + register_node_type_geo_pizza(); register_node_type_geo_points_to_vertices(); register_node_type_geo_points_to_volume(); register_node_type_geo_proximity(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 3be4f82ecb0..76caf02781b 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1270,6 +1270,10 @@ typedef struct NodeGeometryObjectInfo { uint8_t transform_space; } NodeGeometryObjectInfo; +typedef struct NodeGeometryPizza { + int olive_count; +} NodeGeometryPizza; + typedef struct NodeGeometryPointsToVolume { /* GeometryNodePointsToVolumeResolutionMode */ uint8_t resolution_mode; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index a9dec810079..54d0e81d668 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9851,6 +9851,21 @@ static void def_geo_object_info(StructRNA *srna) prop, "Transform Space", "The transformation of the vector and geometry outputs"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); } +static void def_geo_pizza(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryPizza", "storage"); + + // For each property, i.e., each user-exposed parameter of the node + // that is not a pluggable input: + prop = RNA_def_property(srna, "olive_count", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "olive_count"); + // Call various setters to fill in the property's settings: + RNA_def_property_range(prop, 0, 1000); + RNA_def_property_ui_text(prop, "Olive Count", "Number of olives topping the pizza"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} static void def_geo_points_to_volume(StructRNA *srna) { diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 064112b7efd..455c5fea6c0 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -100,6 +100,7 @@ void register_node_type_geo_mesh_subdivide(void); void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_mesh_to_points(void); void register_node_type_geo_object_info(void); +void register_node_type_geo_pizza(void); void register_node_type_geo_points_to_vertices(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_proximity(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e0a4d241b3b..ff5ae7c3ef7 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -375,6 +375,7 @@ DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurve DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "") DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "") +DefNode(GeometryNode, GEO_NODE_PIZZA, def_geo_pizza, "PIZZA", Pizza, "Pizza", "") DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") @@ -400,4 +401,4 @@ DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_T /* undefine macros */ #undef DefNode - /* clang-format on */ + /* clang-format on */ \ No newline at end of file diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 84280c0889a..048b54b55f9 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -110,6 +110,7 @@ set(SRC nodes/node_geo_mesh_to_curve.cc nodes/node_geo_mesh_to_points.cc nodes/node_geo_object_info.cc + nodes/node_geo_pizza.cc nodes/node_geo_points_to_vertices.cc nodes/node_geo_points_to_volume.cc nodes/node_geo_proximity.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_pizza.cc b/source/blender/nodes/geometry/nodes/node_geo_pizza.cc new file mode 100644 index 00000000000..4ee4deabfc4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_pizza.cc @@ -0,0 +1,195 @@ +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_task.hh" + +#include "BKE_material.h" +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_pizza_cc{ + +static Mesh *create_pizza_mesh(const int olive_count, + const float radius, + IndexRange &base_polys, + IndexRange &olives_polys) +{ + // (i) Compute element counts + int vert_count = 32 + olive_count * 4; + int edge_count = 32 + olive_count * 4; + int corner_count = 32 + olive_count * 4; + int face_count = 1 + olive_count; + + // (ii) Allocate memory + Mesh *mesh = BKE_mesh_new_nomain(vert_count, edge_count, 0, corner_count, face_count); + + // (iii) Fill in element buffers + MutableSpan verts{mesh->mvert, mesh->totvert}; + MutableSpan loops{mesh->mloop, mesh->totloop}; + MutableSpan edges{mesh->medge, mesh->totedge}; + MutableSpan polys{mesh->mpoly, mesh->totpoly}; + base_polys = IndexRange{0, 1}; + olives_polys = IndexRange{1, olive_count}; + + // (iii.a) Base + const float angle_delta = 2 * M_PI / 32; + for (const int i : IndexRange(32)) { + // Vertex coordinates + const float angle = i * angle_delta; + copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); + + // Edge + MEdge &edge = edges[i]; + edge.v1 = i; + edge.v2 = (i + 1) % 32; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + + // Corner + MLoop &loop = loops[i]; + loop.e = i; + loop.v = i; + } + // Face + MPoly &poly = polys[0]; + poly.loopstart = 0; + poly.totloop = 32; + + // (iii.b) Olives + const float angle_delta_olive = 2.0f * (M_PI / static_cast(olive_count - 1)); + for (const int i : IndexRange(olive_count)) { + const int offset = 32 + 4 * i; + + // Vertex coordinates + float cx = 0, cy = 0; + if (i > 0) { // (the olive #0 is at the center) + const float angle = (i - 1) * angle_delta_olive; + cx = std::cos(angle) * radius / 2; + cy = std::sin(angle) * radius / 2; + } + copy_v3_v3(verts[offset + 0].co, float3(cx + 0.05f, cy + 0.05f, 0.01f)); + copy_v3_v3(verts[offset + 1].co, float3(cx - 0.05f, cy + 0.05f, 0.01f)); + copy_v3_v3(verts[offset + 2].co, float3(cx - 0.05f, cy - 0.05f, 0.01f)); + copy_v3_v3(verts[offset + 3].co, float3(cx + 0.05f, cy - 0.05f, 0.01f)); + + for (const int k : IndexRange(4)) { + // Edge + MEdge &edge = edges[offset + k]; + edge.v1 = offset + k; + edge.v2 = offset + (k + 1) % 4; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + + // Corner + MLoop &loop = loops[offset + k]; + loop.e = offset + k; + loop.v = offset + k; + } + + // Face + MPoly &poly = polys[1 + i]; + poly.loopstart = offset; + poly.totloop = 4; + } + + BLI_assert(BKE_mesh_is_valid(mesh)); + return mesh; +} + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Size of the pizza")); + b.add_output("Mesh"); + b.add_output(N_("Base")).field_source(); + b.add_output(N_("Olives")).field_source(); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryPizza *data = MEM_cnew(__func__); + data->olive_count = 5; + node->storage = data; +} + +NODE_STORAGE_FUNCS(NodeGeometryPizza) // To define node_storage() + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryPizza &storage = node_storage(*node); + + bNodeSocket *out_socket_geometry = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_base = out_socket_geometry->next; + bNodeSocket *out_socket_olives = out_socket_base->next; + + // Stupid feature for the sake of the example: When there are too many + // olives, we no longer output the fields! + nodeSetSocketAvailability(ntree, out_socket_base, storage.olive_count < 25); + nodeSetSocketAvailability(ntree, out_socket_olives, storage.olive_count < 25); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "olive_count", 0, "", ICON_NONE); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + // We first retrieve the property (olive count) and the input socket (radius) + const NodeGeometryPizza &storage = node_storage(params.node()); + const int olive_count = storage.olive_count; + const float radius = params.extract_input("Radius"); + + // Then we create the mesh (let's put it in a separate function) + IndexRange base_polys, olives_polys; + Mesh *mesh = create_pizza_mesh(olive_count, radius, base_polys, olives_polys); + + // We build a geometry set to wrap the mesh and set it as the output value + GeometrySet output_geo = GeometrySet::create_with_mesh(mesh); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + + MeshComponent &component = output_geo.get_component_for_write(); + if (params.output_is_required("Base")) { + // Create the field from a range and a mesh component: + StrongAnonymousAttributeID id("Base"); + OutputAttribute_Typed attribute = + component.attribute_try_get_for_output_only(id.get(), ATTR_DOMAIN_FACE); + attribute.as_span().slice(base_polys).fill(true); + attribute.save(); + + // Output this field in the Base output + params.set_output("Base", + AnonymousAttributeFieldInput::Create( + std::move(id), params.attribute_producer_name())); + } + + if (params.output_is_required("Olives")) { + // [...] Idem for olives + } +} + + +} +void register_node_type_geo_pizza() +{ + namespace file_ns = blender::nodes::node_geo_pizza_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_PIZZA, "Pizza", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_storage(&ntype, "NodeGeometryPizza", node_free_standard_storage, node_copy_standard_storage); + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} \ No newline at end of file