diff options
Diffstat (limited to 'source/blender')
17 files changed, 1492 insertions, 6 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 42022cce5a5..f5c617c1d04 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1384,6 +1384,14 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_SUBDIVIDE 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 #define GEO_NODE_ATTRIBUTE_CONVERT 1031 +#define GEO_NODE_MESH_PRIMITIVE_CUBE 1032 +#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033 +#define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1034 +#define GEO_NODE_MESH_PRIMITIVE_CYLINDER 1035 +#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1036 +#define GEO_NODE_MESH_PRIMITIVE_CONE 1037 +#define GEO_NODE_MESH_PRIMITIVE_LINE 1038 +#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 66c0e724fdf..bbc655d7fc8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4811,6 +4811,14 @@ static void registerGeometryNodes() register_node_type_geo_edge_split(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); + register_node_type_geo_mesh_primitive_circle(); + register_node_type_geo_mesh_primitive_cone(); + register_node_type_geo_mesh_primitive_cube(); + register_node_type_geo_mesh_primitive_cylinder(); + register_node_type_geo_mesh_primitive_ico_sphere(); + register_node_type_geo_mesh_primitive_line(); + register_node_type_geo_mesh_primitive_plane(); + register_node_type_geo_mesh_primitive_uv_sphere(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 967258f1b81..06ee22b1452 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1258,6 +1258,28 @@ typedef struct NodeAttributeConvert { int16_t domain; } NodeAttributeConvert; +typedef struct NodeGeometryMeshCircle { + /* GeometryNodeMeshCircleFillType. */ + uint8_t fill_type; +} NodeGeometryMeshCircle; + +typedef struct NodeGeometryMeshCylinder { + /* GeometryNodeMeshCircleFillType. */ + uint8_t fill_type; +} NodeGeometryMeshCylinder; + +typedef struct NodeGeometryMeshCone { + /* GeometryNodeMeshCircleFillType. */ + uint8_t fill_type; +} NodeGeometryMeshCone; + +typedef struct NodeGeometryMeshLine { + /* GeometryNodeMeshLineMode. */ + uint8_t mode; + /* GeometryNodeMeshLineCountMode. */ + uint8_t count_mode; +} NodeGeometryMeshLine; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1734,6 +1756,22 @@ typedef enum GeometryNodePointsToVolumeResolutionMode { GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE = 1, } GeometryNodePointsToVolumeResolutionMode; +typedef enum GeometryNodeMeshCircleFillType { + GEO_NODE_MESH_CIRCLE_FILL_NONE = 0, + GEO_NODE_MESH_CIRCLE_FILL_NGON = 1, + GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN = 2, +} GeometryNodeMeshCircleFillType; + +typedef enum GeometryNodeMeshLineMode { + GEO_NODE_MESH_LINE_MODE_END_POINTS = 0, + GEO_NODE_MESH_LINE_MODE_OFFSET = 1, +} GeometryNodeMeshLineMode; + +typedef enum GeometryNodeMeshLineCountMode { + GEO_NODE_MESH_LINE_COUNT_TOTAL = 0, + GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1, +} GeometryNodeMeshLineCountMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 35c319c2f79..04ff181d9b5 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -402,8 +402,15 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { {0, "CryptoObject", 0, "Object", "Use Object layer"}, {1, "CryptoMaterial", 0, "Material", "Use Material layer"}, {2, "CryptoAsset", 0, "Asset", "Use Asset layer"}, - {0, NULL, 0, NULL, NULL}}; + {0, NULL, 0, NULL, NULL}, +}; +static EnumPropertyItem rna_node_geometry_mesh_circle_fill_type_items[] = { + {GEO_NODE_MESH_CIRCLE_FILL_NONE, "NONE", 0, "None", ""}, + {GEO_NODE_MESH_CIRCLE_FILL_NGON, "NGON", 0, "N-Gon", ""}, + {GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN, "TRIANGLE_FAN", 0, "Triangles", ""}, + {0, NULL, 0, NULL, NULL}, +}; #endif #define ITEM_ATTRIBUTE \ @@ -9437,6 +9444,87 @@ static void def_geo_attribute_separate_xyz(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_mesh_circle(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshCircle", "storage"); + + prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); + RNA_def_property_ui_text(prop, "Fill Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_mesh_cylinder(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshCylinder", "storage"); + + prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); + RNA_def_property_ui_text(prop, "Fill Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_mesh_cone(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshCone", "storage"); + + prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); + RNA_def_property_ui_text(prop, "Fill Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_mesh_line(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_MESH_LINE_MODE_OFFSET, + "OFFSET", + 0, + "Offset", + "Specify the offset from one vertex to the next"}, + {GEO_NODE_MESH_LINE_MODE_END_POINTS, + "END_POINTS", + 0, + "End Points", + "Specify the line's start and end points"}, + {0, NULL, 0, NULL, NULL}, + }; + + static EnumPropertyItem count_mode_items[] = { + {GEO_NODE_MESH_LINE_COUNT_TOTAL, + "TOTAL", + 0, + "Count", + "Specify the total number of vertices"}, + {GEO_NODE_MESH_LINE_COUNT_RESOLUTION, + "RESOLUTION", + 0, + "Resolution", + "Specify the distance between vertices"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshLine", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "count_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, count_mode_items); + RNA_def_property_ui_text(prop, "Count Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index bbc14751cdd..baa1f1bc579 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -162,6 +162,14 @@ set(SRC geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc + geometry/nodes/node_geo_mesh_primitive_circle.cc + geometry/nodes/node_geo_mesh_primitive_cone.cc + geometry/nodes/node_geo_mesh_primitive_cube.cc + geometry/nodes/node_geo_mesh_primitive_cylinder.cc + geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc + geometry/nodes/node_geo_mesh_primitive_line.cc + geometry/nodes/node_geo_mesh_primitive_plane.cc + geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d28dc8209d4..86d3861c5ba 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -44,6 +44,14 @@ void register_node_type_geo_collection_info(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); +void register_node_type_geo_mesh_primitive_circle(void); +void register_node_type_geo_mesh_primitive_cone(void); +void register_node_type_geo_mesh_primitive_cube(void); +void register_node_type_geo_mesh_primitive_plane(void); +void register_node_type_geo_mesh_primitive_cylinder(void); +void register_node_type_geo_mesh_primitive_ico_sphere(void); +void register_node_type_geo_mesh_primitive_line(void); +void register_node_type_geo_mesh_primitive_uv_sphere(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e9983d7e92c..89144b678e7 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -302,6 +302,14 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separat DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 271e3771006..8eac8bc0a20 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -47,4 +47,17 @@ void update_attribute_input_socket_availabilities(bNode &node, Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); +void transform_mesh(Mesh *mesh, + const float3 translation, + const float3 rotation, + const float3 scale); + +Mesh *create_cylinder_or_cone_mesh(const float3 location, + const float3 rotation, + const float radius_top, + const float radius_bottom, + const float depth, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc new file mode 100644 index 00000000000..6d286a9d583 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -0,0 +1,244 @@ +/* + * 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 "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_circle_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_circle_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_circle_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCircle *node_storage = (NodeGeometryMeshCircle *)MEM_callocN( + sizeof(NodeGeometryMeshCircle), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static int circle_vert_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num + 1; + } + BLI_assert(false); + return 0; +} + +static int circle_edge_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num * 2; + } + BLI_assert(false); + return 0; +} + +static int circle_corner_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + return 0; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num * 3; + } + BLI_assert(false); + return 0; +} + +static int circle_face_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + return 0; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return 1; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num; + } + BLI_assert(false); + return 0; +} + +static Mesh *create_circle_mesh(const float radius, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type) +{ + Mesh *mesh = BKE_mesh_new_nomain(circle_vert_total(fill_type, verts_num), + circle_edge_total(fill_type, verts_num), + 0, + circle_corner_total(fill_type, verts_num), + circle_face_total(fill_type, verts_num)); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + float angle = 0.0f; + const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); + for (MVert &vert : verts) { + copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); + angle += angle_delta; + } + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + copy_v3_v3(verts.last().co, float3(0)); + } + + /* Point all vertex normals in the up direction. */ + const short up_normal[3] = {0, 0, SHRT_MAX}; + for (MVert &vert : verts) { + copy_v3_v3_short(vert.no, up_normal); + } + + /* Create outer edges. */ + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[i]; + edge.v1 = i; + edge.v2 = (i + 1) % verts_num; + } + + /* Set loose edge flags. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[i]; + edge.flag |= ME_LOOSEEDGE; + } + } + + /* Create triangle fan edges. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[verts_num + i]; + edge.v1 = verts_num; + edge.v2 = i; + } + } + + /* Create corners and faces. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + MPoly &poly = polys[0]; + poly.loopstart = 0; + poly.totloop = loops.size(); + + for (const int i : IndexRange(verts_num)) { + MLoop &loop = loops[i]; + loop.e = i; + loop.v = i; + } + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[i]; + poly.loopstart = 3 * i; + poly.totloop = 3; + + MLoop &loop_a = loops[3 * i]; + loop_a.e = i; + loop_a.v = i; + MLoop &loop_b = loops[3 * i + 1]; + loop_b.e = verts_num + ((i + 1) % verts_num); + loop_b.v = (i + 1) % verts_num; + MLoop &loop_c = loops[3 * i + 2]; + loop_c.e = verts_num + i; + loop_c.v = verts_num; + } + } + + return mesh; +} + +static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCircle &storage = *(const NodeGeometryMeshCircle *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); + + BLI_assert(BKE_mesh_is_valid(mesh)); + + transform_mesh(mesh, location, rotation, float3(1)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_circle() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Circle", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_circle_in, geo_node_mesh_primitive_circle_out); + node_type_init(&ntype, geo_node_mesh_primitive_circle_init); + node_type_storage( + &ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_circle_exec; + ntype.draw_buttons = geo_node_mesh_primitive_circle_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc new file mode 100644 index 00000000000..5e5dbd91d31 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -0,0 +1,279 @@ +/* + * 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_lib_id.h" +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius Top"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Radius Bottom"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Depth"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cone_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN( + sizeof(NodeGeometryMeshCone), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static int vert_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + int vert_total = 0; + if (!top_is_point) { + vert_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + vert_total++; + } + } + else { + vert_total++; + } + if (!bottom_is_point) { + vert_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + vert_total++; + } + } + else { + vert_total++; + } + + return vert_total; +} + +static int edge_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + if (top_is_point && bottom_is_point) { + return 1; + } + + int edge_total = 0; + if (!top_is_point) { + edge_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + edge_total += verts_num; + } + } + + edge_total += verts_num; + + if (!bottom_is_point) { + edge_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + edge_total += verts_num; + } + } + + return edge_total; +} + +static int corner_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int corner_total = 0; + if (!top_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += verts_num; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + corner_total += verts_num * 3; + } + } + + if (!top_is_point && !bottom_is_point) { + corner_total += verts_num * 4; + } + else { + corner_total += verts_num * 3; + } + + if (!bottom_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += verts_num; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + corner_total += verts_num * 3; + } + } + + return corner_total; +} + +static int face_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int face_total = 0; + if (!top_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + face_total += verts_num; + } + } + + face_total += verts_num; + + if (!bottom_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + face_total += verts_num; + } + } + + return face_total; +} + +Mesh *create_cylinder_or_cone_mesh(const float3 location, + const float3 rotation, + const float radius_top, + const float radius_bottom, + const float depth, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(1)); + + const bool top_is_point = radius_top == 0.0f; + const bool bottom_is_point = radius_bottom == 0.0f; + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = { + vert_total(fill_type, verts_num, top_is_point, bottom_is_point), + edge_total(fill_type, verts_num, top_is_point, bottom_is_point), + corner_total(fill_type, verts_num, top_is_point, bottom_is_point), + face_total(fill_type, verts_num, top_is_point, bottom_is_point)}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE); + const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN); + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b " + "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b", + verts_num, + radius_bottom, + radius_top, + cap_end, + cap_tri, + depth, + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius_top = params.extract_input<float>("Radius Top"); + const float radius_bottom = params.extract_input<float>("Radius Bottom"); + const float depth = params.extract_input<float>("Depth"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cylinder_or_cone_mesh( + location, rotation, radius_top, radius_bottom, depth, verts_num, fill_type); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cone() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cone_in, geo_node_mesh_primitive_cone_out); + node_type_init(&ntype, geo_node_mesh_primitive_cone_init); + node_type_storage( + &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; + ntype.draw_buttons = geo_node_mesh_primitive_cone_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc new file mode 100644 index 00000000000..5c32ff6b687 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -0,0 +1,84 @@ +/* + * 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 "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cube_in[] = { + {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *create_cube_mesh(const float3 location, const float3 rotation, const float size) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(size)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {8, 12, 24, 6}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_cube matrix=%m4 size=%f calc_uvs=%b", + transform.values, + size, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) +{ + const float size = params.extract_input<float>("Size"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cube_mesh(location, rotation, size); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cube() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cube_in, geo_node_mesh_primitive_cube_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cube_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc new file mode 100644 index 00000000000..9137be064de --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -0,0 +1,105 @@ +/* + * 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 "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Depth"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCylinder *node_storage = (NodeGeometryMeshCylinder *)MEM_callocN( + sizeof(NodeGeometryMeshCylinder), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCylinder &storage = *(const NodeGeometryMeshCylinder *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float depth = params.extract_input<float>("Depth"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + /* 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( + location, rotation, radius, radius, depth, verts_num, fill_type); + + transform_mesh(mesh, location, rotation, float3(1)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cylinder() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cylinder_in, geo_node_mesh_primitive_cylinder_out); + node_type_init(&ntype, geo_node_mesh_primitive_cylinder_init); + node_type_storage( + &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cylinder_exec; + ntype.draw_buttons = geo_node_mesh_primitive_cylinder_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc new file mode 100644 index 00000000000..9aaccb7d805 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -0,0 +1,91 @@ +/* + * 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 "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = { + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 0, 7}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *create_ico_sphere_mesh(const float3 location, + const float3 rotation, + const int subdivisions, + const float radius) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(1.0f)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {0, 0, 0, 0}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + subdivisions, + std::abs(radius), + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) +{ + const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 10); + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_ico_sphere_mesh(location, rotation, subdivisions, radius); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_ico_sphere() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_ico_sphere_in, geo_node_mesh_primitive_ico_sphere_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_ico_sphere_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc new file mode 100644 index 00000000000..a7d40571c39 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -0,0 +1,188 @@ +/* + * 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 "BLI_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_line_in[] = { + {SOCK_INT, N_("Count"), 10, 0.0f, 0.0f, 0.0f, 1, 10000}, + {SOCK_FLOAT, N_("Resolution"), 1.0f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, + N_("Start Location"), + 0.0f, + 0.0f, + 0.0f, + 1.0f, + -FLT_MAX, + FLT_MAX, + PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_line_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_line_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + if (RNA_enum_get(ptr, "mode") == GEO_NODE_MESH_LINE_MODE_END_POINTS) { + uiItemR(layout, ptr, "count_mode", 0, "", ICON_NONE); + } +} + +static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshLine *node_storage = (NodeGeometryMeshLine *)MEM_callocN( + sizeof(NodeGeometryMeshLine), __func__); + + node_storage->mode = GEO_NODE_MESH_LINE_MODE_OFFSET; + node_storage->count_mode = GEO_NODE_MESH_LINE_COUNT_TOTAL; + + node->storage = node_storage; +} + +static void geo_node_mesh_primitive_line_update(bNodeTree *UNUSED(tree), bNode *node) +{ + bNodeSocket *count_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *resolution_socket = count_socket->next; + bNodeSocket *start_socket = resolution_socket->next; + bNodeSocket *end_and_offset_socket = start_socket->next; + + const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)node->storage; + const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode; + const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode) + storage.count_mode; + + node_sock_label(end_and_offset_socket, + (mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) ? N_("End Location") : + N_("Offset")); + + nodeSetSocketAvailability(resolution_socket, + mode == GEO_NODE_MESH_LINE_MODE_END_POINTS && + count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION); + nodeSetSocketAvailability(count_socket, + mode == GEO_NODE_MESH_LINE_MODE_OFFSET || + count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL); +} + +namespace blender::nodes { + +static void fill_edge_data(MutableSpan<MEdge> edges) +{ + for (const int i : edges.index_range()) { + edges[i].v1 = i; + edges[i].v2 = i + 1; + edges[i].flag |= ME_LOOSEEDGE; + } +} + +static Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) +{ + if (count < 1) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + + short normal[3]; + normal_float_to_short_v3(normal, delta.normalized()); + + float3 co = start; + for (const int i : verts.index_range()) { + copy_v3_v3(verts[i].co, co); + copy_v3_v3_short(verts[i].no, normal); + co += delta; + } + + fill_edge_data(edges); + + return mesh; +} + +static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) +{ + const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)params.node().storage; + const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode; + const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode) + storage.count_mode; + + Mesh *mesh = nullptr; + const float3 start = params.extract_input<float3>("Start Location"); + if (mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) { + /* The label switches to "End Location", but the same socket is used. */ + const float3 end = params.extract_input<float3>("Offset"); + const float3 total_delta = end - start; + + if (count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.extract_input<float>("Resolution"), 0.0001f); + const int count = total_delta.length() / resolution + 1; + const float3 delta = total_delta.normalized() * resolution; + mesh = create_line_mesh(start, delta, count); + } + else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) { + const int count = params.extract_input<int>("Count"); + if (count > 1) { + const float3 delta = total_delta / (float)(count - 1); + mesh = create_line_mesh(start, delta, count); + } + } + } + else if (mode == GEO_NODE_MESH_LINE_MODE_OFFSET) { + const float3 delta = params.extract_input<float3>("Offset"); + const int count = params.extract_input<int>("Count"); + mesh = create_line_mesh(start, delta, count); + } + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_line() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Line", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_line_in, geo_node_mesh_primitive_line_out); + node_type_init(&ntype, geo_node_mesh_primitive_line_init); + node_type_update(&ntype, geo_node_mesh_primitive_line_update); + node_type_storage( + &ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_line_exec; + ntype.draw_buttons = geo_node_mesh_primitive_line_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc new file mode 100644 index 00000000000..adb1d4a9eb4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc @@ -0,0 +1,183 @@ +/* + * 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 "BLI_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_plane_in[] = { + {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_INT, N_("Vertices X"), 10, 0.0f, 0.0f, 0.0f, 2, 1000}, + {SOCK_INT, N_("Vertices Y"), 10, 0.0f, 0.0f, 0.0f, 2, 1000}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_plane_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void calculate_uvs(Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( + "uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); + MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + + for (const int i : loops.index_range()) { + const float3 &co = verts[loops[i].v].co; + uvs[i].x = (co.x + size) / (size * 2.0f); + uvs[i].y = (co.y + size) / (size * 2.0f); + } + + uv_attribute.apply_span_and_save(); +} + +static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float size) +{ + const int edges_x = verts_x - 1; + const int edges_y = verts_y - 1; + Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y, + edges_x * verts_y + edges_y * verts_x, + 0, + edges_x * edges_y * 4, + edges_x * edges_y); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + { + const float dx = size * 2.0f / edges_x; + const float dy = size * 2.0f / edges_y; + float x = -size; + for (const int x_index : IndexRange(verts_x)) { + float y = -size; + for (const int y_index : IndexRange(verts_y)) { + const int vert_index = x_index * verts_y + y_index; + verts[vert_index].co[0] = x; + verts[vert_index].co[1] = y; + verts[vert_index].co[2] = 0.0f; + y += dy; + } + x += dx; + } + } + + /* Point all vertex normals in the up direction. */ + const short up_normal[3] = {0, 0, SHRT_MAX}; + for (MVert &vert : verts) { + copy_v3_v3_short(vert.no, up_normal); + } + + /* Build the horizontal edges in the X direction. */ + const int y_edges_start = 0; + int edge_index = 0; + for (const int x : IndexRange(verts_x)) { + for (const int y : IndexRange(edges_y)) { + const int vert_index = x * verts_y + y; + MEdge &edge = edges[edge_index++]; + edge.v1 = vert_index; + edge.v2 = vert_index + 1; + } + } + + /* Build the vertical edges in the Y direction. */ + const int x_edges_start = edge_index; + for (const int y : IndexRange(verts_y)) { + for (const int x : IndexRange(edges_x)) { + const int vert_index = x * verts_y + y; + MEdge &edge = edges[edge_index++]; + edge.v1 = vert_index; + edge.v2 = vert_index + verts_y; + } + } + + int loop_index = 0; + int poly_index = 0; + for (const int x : IndexRange(edges_x)) { + for (const int y : IndexRange(edges_y)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 4; + const int vert_index = x * verts_y + y; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = vert_index; + loop_a.e = x_edges_start + edges_x * y + x; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = vert_index + verts_y; + loop_b.e = y_edges_start + edges_y * (x + 1) + y; + MLoop &loop_c = loops[loop_index++]; + loop_c.v = vert_index + verts_y + 1; + loop_c.e = x_edges_start + edges_x * (y + 1) + x; + MLoop &loop_d = loops[loop_index++]; + loop_d.v = vert_index + 1; + loop_d.e = y_edges_start + edges_y * x + y; + } + } + + calculate_uvs(mesh, verts, loops, size); + + return mesh; +} + +static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params) +{ + const float size = params.extract_input<float>("Size"); + const int verts_x = params.extract_input<int>("Vertices X"); + const int verts_y = params.extract_input<int>("Vertices Y"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + if (verts_x < 2 || verts_y < 2) { + params.set_output("Geometry", GeometrySet()); + return; + } + + Mesh *mesh = create_plane_mesh(verts_x, verts_y, size); + BLI_assert(BKE_mesh_is_valid(mesh)); + + transform_mesh(mesh, location, rotation, float3(1)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_plane() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_PLANE, "Plane", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_plane_in, geo_node_mesh_primitive_plane_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_plane_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc new file mode 100644 index 00000000000..e164ad247a3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -0,0 +1,131 @@ +/* + * 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_lib_id.h" +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = { + {SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024}, + {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static int sphere_vert_total(const int segments, const int rings) +{ + return segments * (rings - 1) + 2; +} + +static int sphere_edge_total(const int segments, const int rings) +{ + return segments * (rings * 2 - 1); +} + +static int sphere_corner_total(const int segments, const int rings) +{ + const int quad_corners = 4 * segments * (rings - 2); + const int tri_corners = 3 * segments * 2; + return quad_corners + tri_corners; +} + +static int sphere_face_total(const int segments, const int rings) +{ + const int quads = segments * (rings - 2); + const int triangles = segments * 2; + return quads + triangles; +} + +static Mesh *create_uv_sphere_mesh_bmesh(const float3 location, + const float3 rotation, + const float radius, + const int segments, + const int rings) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(radius)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings), + sphere_edge_total(segments, rings), + sphere_corner_total(segments, rings), + sphere_face_total(segments, rings)}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b", + segments, + rings, + radius, + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) +{ + const int segments_num = params.extract_input<int>("Segments"); + const int rings_num = params.extract_input<int>("Rings"); + if (segments_num < 3 || rings_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_uv_sphere_mesh_bmesh(location, rotation, radius, segments_num, rings_num); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_uv_sphere() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_uv_sphere_in, geo_node_mesh_primitive_uv_sphere_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_uv_sphere_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 539a7551be9..ef6f0be40f2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -57,14 +57,16 @@ static bool use_translate(const float3 rotation, const float3 scale) return true; } -static void transform_mesh(Mesh *mesh, - const float3 translation, - const float3 rotation, - const float3 scale) +void transform_mesh(Mesh *mesh, + const float3 translation, + const float3 rotation, + const float3 scale) { /* Use only translation if rotation and scale are zero. */ if (use_translate(rotation, scale)) { - BKE_mesh_translate(mesh, translation, true); + if (!translation.is_zero()) { + BKE_mesh_translate(mesh, translation, true); + } } else { float mat[4][4]; |