Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-03-17 00:35:12 +0300
committerHans Goudey <h.goudey@me.com>2021-03-17 00:35:36 +0300
commit9a56a3865c06b472ac54c0351e270dcf738add07 (patch)
tree375bfcdabd2bb184be581ebb95f249938952ad19
parent3e87d8a4315d794efff659e40f0bb9e34e2aec8a (diff)
Geometry Nodes: Add initial version of mesh primitives
This commit includes nodes to build the following primitives: - Cone - Cylinder - Circle - Cube - UV Sphere - Ico Sphere - Line - Plane/Grid In general the inputs are the same as the corresponding operators in the 3D view. **Line Primitive** The line primitive has two modes-- adding vertices between two end points, or adding vertices each at an offset from the start point. For the former mode, there is a choice between a vertex count and a distance between each point. **Plane Primitive** This commit includes the "Plane" and "Grid" primitives as one node. Generally primitives are named after the simpler form of the shape they create (i.e. "Cone" can make some more complex shapes). Also, generally you want to tweak the number of subdivisions anyway, so defaulting to plane is not an inconvenience. And generally having fewer redundant base primitives is better. **Future Improvements** A following patch proposes to improve the speed of the cylinder, cone, and sphere primitives: D10730. Additional possible future improvements would be adding subdivisions to the cube node and rings to the cone and cylinder nodes. Differential Revision: https://developer.blender.org/D10715
-rw-r--r--release/scripts/startup/nodeitems_builtins.py10
-rw-r--r--source/blender/blenkernel/BKE_node.h8
-rw-r--r--source/blender/blenkernel/intern/node.cc8
-rw-r--r--source/blender/makesdna/DNA_node_types.h38
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c90
-rw-r--r--source/blender/nodes/CMakeLists.txt8
-rw-r--r--source/blender/nodes/NOD_geometry.h8
-rw-r--r--source/blender/nodes/NOD_static_types.h8
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc244
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc279
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc84
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc105
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc91
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc188
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc183
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc131
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc12
18 files changed, 1502 insertions, 6 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index fefb266e591..e8f2d7db683 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -520,6 +520,16 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivideSmooth"),
NodeItem("GeometryNodeSubdivide"),
+
+ # These should be in a sub-menu, but that requires a refactor to build the add menu manually.
+ NodeItem("GeometryNodeMeshCube"),
+ NodeItem("GeometryNodeMeshCircle"),
+ NodeItem("GeometryNodeMeshUVSphere"),
+ NodeItem("GeometryNodeMeshIcoSphere"),
+ NodeItem("GeometryNodeMeshCylinder"),
+ NodeItem("GeometryNodeMeshCone"),
+ NodeItem("GeometryNodeMeshLine"),
+ NodeItem("GeometryNodeMeshPlane"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
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];