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:
Diffstat (limited to 'source/blender/nodes/geometry')
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc12
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc164
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc87
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc5
-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.cc103
-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_point_distribute.cc507
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivide.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_subdivision_surface_simple.cc)24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc12
21 files changed, 1899 insertions, 239 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 87178c52ab9..2c4c7da64cc 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -71,6 +71,17 @@ static void geometry_node_tree_update(bNodeTree *ntree)
ntree_update_reroute_nodes(ntree);
}
+static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCallback func)
+{
+ func(calldata, NODE_CLASS_INPUT, N_("Input"));
+ func(calldata, NODE_CLASS_GEOMETRY, N_("Geometry"));
+ func(calldata, NODE_CLASS_ATTRIBUTE, N_("Attribute"));
+ func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
+ func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
+ func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
+ func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
+}
+
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
@@ -83,6 +94,7 @@ void register_node_tree_type_geo(void)
tt->rna_ext.srna = &RNA_GeometryNodeTree;
tt->update = geometry_node_tree_update;
tt->get_from_context = geometry_node_tree_get_from_context;
+ tt->foreach_nodeclass = foreach_nodeclass;
ntreeTypeAdd(tt);
}
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_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
new file mode 100644
index 00000000000..11d220dd903
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
@@ -0,0 +1,164 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_attribute_convert_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_convert_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_convert_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
+ __func__);
+
+ data->data_type = CD_PROP_FLOAT;
+ data->domain = ATTR_DOMAIN_AUTO;
+ node->storage = data;
+}
+
+namespace blender::nodes {
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
+ if (result_attribute) {
+ return result_attribute->domain();
+ }
+ ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
+ if (source_attribute) {
+ return source_attribute->domain();
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void attribute_convert_calc(GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ const StringRef source_name,
+ const StringRef result_name,
+ const CustomDataType result_type,
+ const AttributeDomain domain)
+{
+ const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
+ get_result_domain(
+ component, source_name, result_name) :
+ domain;
+
+ ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ source_name, result_domain, result_type);
+ if (!source_attribute) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + source_name + "\"");
+ return;
+ }
+
+ OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
+ result_name, result_domain, result_type);
+ if (!result_attribute) {
+ return;
+ }
+
+ fn::GSpan source_span = source_attribute->get_span();
+ fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
+ if (source_span.is_empty() || result_span.is_empty()) {
+ return;
+ }
+ BLI_assert(source_span.size() == result_span.size());
+
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
+ BLI_assert(cpp_type != nullptr);
+
+ cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
+
+ result_attribute.apply_span_and_save();
+}
+
+static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ const std::string result_name = params.extract_input<std::string>("Result");
+ const std::string source_name = params.extract_input<std::string>("Attribute");
+ const NodeAttributeConvert &node_storage = *(const NodeAttributeConvert *)params.node().storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+ const AttributeDomain domain = static_cast<AttributeDomain>(node_storage.domain);
+
+ if (result_name.empty()) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+ if (geometry_set.has<MeshComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<MeshComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_convert()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_convert_in, geo_node_attribute_convert_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec;
+ ntype.draw_buttons = geo_node_attribute_convert_layout;
+ node_type_init(&ntype, geo_node_attribute_convert_init);
+ node_type_storage(
+ &ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index bb5b5073c32..27c35da7d37 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -224,7 +224,7 @@ static void randomize_attribute_on_component(GeometryComponent &component,
if (operation != GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) {
if (!component.attribute_exists(attribute_name)) {
params.error_message_add(NodeWarningType::Error,
- "No attribute with name '" + attribute_name + "'.");
+ TIP_("No attribute with name \"") + attribute_name + "\"");
return;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
new file mode 100644
index 00000000000..837f0c3629a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -0,0 +1,87 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_remove_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING,
+ N_("Attribute"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ PROP_NONE,
+ SOCK_MULTI_INPUT},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_remove_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void remove_attribute(GeometryComponent &component,
+ GeoNodeExecParams &params,
+ Span<std::string> attribute_names)
+{
+ for (std::string attribute_name : attribute_names) {
+ if (attribute_name.empty()) {
+ continue;
+ }
+
+ if (!component.attribute_try_delete(attribute_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Cannot delete attribute with name \"") + attribute_name +
+ "\"");
+ }
+ }
+}
+
+static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_remove()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 38215b54640..9f331190420 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -100,7 +100,7 @@ static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boole
}
BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, boolean_mode);
+ bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, false, boolean_mode);
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
BM_mesh_free(bm);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 4e15f232934..52512769a47 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -222,8 +222,9 @@ static void join_components(Span<const MeshComponent *> src_components, Geometry
dst_component.replace(new_mesh);
/* Don't copy attributes that are stored directly in the mesh data structs. */
- join_attributes(
- to_base_components(src_components), dst_component, {"position", "material_index"});
+ join_attributes(to_base_components(src_components),
+ dst_component,
+ {"position", "material_index", "normal", "shade_smooth", "crease"});
}
static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)
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..158491f40e7
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -0,0 +1,103 @@
+/*
+ * 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"), 2.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_NGON;
+
+ 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);
+
+ 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_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 1bc280c4bb7..4795970377a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -92,18 +92,17 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
return {looptris, looptris_len};
}
-static int sample_mesh_surface(const Mesh &mesh,
- const float4x4 &transform,
- const float base_density,
- const FloatReadAttribute *density_factors,
- const int seed,
- Vector<float3> &r_positions,
- Vector<float3> &r_bary_coords,
- Vector<int> &r_looptri_indices)
+static void sample_mesh_surface(const Mesh &mesh,
+ const float4x4 &transform,
+ const float base_density,
+ const FloatReadAttribute *density_factors,
+ const int seed,
+ Vector<float3> &r_positions,
+ Vector<float3> &r_bary_coords,
+ Vector<int> &r_looptri_indices)
{
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
- int points_len = 0;
for (const int looptri_index : looptris.index_range()) {
const MLoopTri &looptri = looptris[looptri_index];
const int v0_loop = looptri.tri[0];
@@ -140,20 +139,17 @@ static int sample_mesh_surface(const Mesh &mesh,
r_positions.append(point_pos);
r_bary_coords.append(bary_coord);
r_looptri_indices.append(looptri_index);
- points_len++;
}
}
- return points_len;
}
-BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_array,
+BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_all,
const int initial_points_len)
{
KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len);
int i_point = 0;
- for (const int i_instance : positions_array.index_range()) {
- Span<float3> positions = positions_array[i_instance];
+ for (const Vector<float3> &positions : positions_all) {
for (const float3 position : positions) {
BLI_kdtree_3d_insert(kdtree, i_point, position);
i_point++;
@@ -164,7 +160,8 @@ BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_array
}
BLI_NOINLINE static void update_elimination_mask_for_close_points(
- Span<Vector<float3>> positions_array,
+ Span<Vector<float3>> positions_all,
+ Span<int> instance_start_offsets,
const float minimum_distance,
MutableSpan<bool> elimination_mask,
const int initial_points_len)
@@ -173,26 +170,27 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
return;
}
- KDTree_3d *kdtree = build_kdtree(positions_array, initial_points_len);
+ KDTree_3d *kdtree = build_kdtree(positions_all, initial_points_len);
/* The elimination mask is a flattened array for every point,
* so keep track of the index to it separately. */
- int i_point = 0;
- for (Span<float3> positions : positions_array) {
- for (const float3 position : positions) {
- if (elimination_mask[i_point]) {
- i_point++;
+ for (const int i_instance : positions_all.index_range()) {
+ Span<float3> positions = positions_all[i_instance];
+ const int offset = instance_start_offsets[i_instance];
+
+ for (const int i : positions.index_range()) {
+ if (elimination_mask[offset + i]) {
continue;
}
struct CallbackData {
int index;
MutableSpan<bool> elimination_mask;
- } callback_data = {i_point, elimination_mask};
+ } callback_data = {offset + i, elimination_mask};
BLI_kdtree_3d_range_search_cb(
kdtree,
- position,
+ positions[i],
minimum_distance,
[](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) {
CallbackData &callback_data = *static_cast<CallbackData *>(user_data);
@@ -202,8 +200,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
return true;
},
&callback_data);
-
- i_point++;
}
}
BLI_kdtree_3d_free(kdtree);
@@ -233,8 +229,8 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
- const float probablity = attribute_math::mix3<float>(
- bary_coord, v0_density_factor, v1_density_factor, v2_density_factor);
+ const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
+ v2_density_factor * bary_coord.z;
const float hash = BLI_hash_int_01(bary_coord.hash());
if (hash > probablity) {
@@ -314,6 +310,23 @@ BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
}
template<typename T>
+BLI_NOINLINE static void interpolate_attribute_polygon(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<T> data_in,
+ MutableSpan<T> data_out)
+{
+ BLI_assert(data_in.size() == mesh.totpoly);
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : data_out.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int poly_index = looptri.poly;
+ data_out[i] = data_in[poly_index];
+ }
+}
+
+template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
@@ -331,6 +344,10 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
mesh, bary_coords, looptri_indices, source_span, output_span);
break;
}
+ case ATTR_DOMAIN_POLYGON: {
+ interpolate_attribute_polygon<T>(mesh, looptri_indices, source_span, output_span);
+ break;
+ }
default: {
/* Not supported currently. */
return;
@@ -339,9 +356,9 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
}
BLI_NOINLINE static void interpolate_existing_attributes(
- Span<GeometryInstanceGroup> sets,
- Span<int> group_start_indices,
- Map<std::string, AttributeKind> &attributes,
+ Span<GeometryInstanceGroup> set_groups,
+ Span<int> instance_start_offsets,
+ const Map<std::string, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
@@ -352,70 +369,64 @@ BLI_NOINLINE static void interpolate_existing_attributes(
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
- BLI_assert(attribute_out);
if (!attribute_out) {
continue;
}
- attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
- using T = decltype(dummy);
+ fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
- MutableSpan<T> out_span = attribute_out->get_span_for_write_only<T>();
-
- int i_set_with_mesh = 0;
- int i_instance = 0;
- for (const GeometryInstanceGroup &set_group : sets) {
- const GeometrySet &set = set_group.geometry_set;
- if (!set.has_mesh()) {
- continue;
- }
- const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *source_component.get_for_read();
-
- /* Use a dummy read without specifying a domain or data type in order to
- * get the existing attribute's domain. Interpolation is done manually based
- * on the bary coords in #interpolate_attribute. */
- ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
- attribute_name);
- if (!dummy_attribute) {
- i_instance += set_group.transforms.size();
- i_set_with_mesh++;
- continue;
- }
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *source_component.get_for_read();
+
+ /* Use a dummy read without specifying a domain or data type in order to
+ * get the existing attribute's domain. Interpolation is done manually based
+ * on the bary coords in #interpolate_attribute. */
+ ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
+ attribute_name);
+ if (!dummy_attribute) {
+ i_instance += set_group.transforms.size();
+ continue;
+ }
- const AttributeDomain source_domain = dummy_attribute->domain();
- ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
- attribute_name, source_domain, output_data_type, nullptr);
- BLI_assert(source_attribute);
- Span<T> source_span = source_attribute->get_span<T>();
+ const AttributeDomain source_domain = dummy_attribute->domain();
+ ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
+ attribute_name, source_domain, output_data_type, nullptr);
+ if (!source_attribute) {
+ i_instance += set_group.transforms.size();
+ continue;
+ }
+ fn::GSpan source_span = source_attribute->get_span();
- if (!source_attribute) {
- i_instance += set_group.transforms.size();
- i_set_with_mesh++;
- continue;
- }
+ attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
+ using T = decltype(dummy);
- int i_point = group_start_indices[i_set_with_mesh];
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- Span<float3> bary_coords = bary_coords_array[i_instance].as_span();
- Span<int> looptri_indices = looptri_indices_array[i_instance].as_span();
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
+ interpolate_attribute<T>(mesh,
+ bary_coords,
+ looptri_indices,
+ source_domain,
+ source_span.typed<T>(),
+ instance_span);
- MutableSpan<T> instance_span = out_span.slice(i_point, bary_coords.size());
- interpolate_attribute<T>(
- mesh, bary_coords, looptri_indices, source_domain, source_span, instance_span);
-
- i_point += bary_coords.size();
i_instance++;
}
- i_set_with_mesh++;
- }
- });
+ });
+ }
attribute_out.apply_span_and_save();
}
}
BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> sets,
+ Span<int> instance_start_offsets,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
@@ -427,28 +438,29 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
"rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- MutableSpan<int> ids_full = id_attribute->get_span_for_write_only<int>();
- MutableSpan<float3> normals_full = normal_attribute->get_span_for_write_only<float3>();
- MutableSpan<float3> rotations_full = rotation_attribute->get_span_for_write_only<float3>();
+ MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
+ MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
+ MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
- int i_point = 0;
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
const GeometrySet &set = set_group.geometry_set;
- if (!set.has_mesh()) {
- continue;
- }
-
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- Span<float3> bary_coords = bary_coords_array[i_instance].as_span();
- Span<int> looptri_indices = looptri_indices_array[i_instance].as_span();
- MutableSpan<int> ids = ids_full.slice(i_point, bary_coords.size());
- MutableSpan<float3> normals = normals_full.slice(i_point, bary_coords.size());
- MutableSpan<float3> rotations = rotations_full.slice(i_point, bary_coords.size());
+ for (const float4x4 &transform : set_group.transforms) {
+ const int offset = instance_start_offsets[i_instance];
+
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+ MutableSpan<int> ids = result_ids.slice(offset, bary_coords.size());
+ MutableSpan<float3> normals = result_normals.slice(offset, bary_coords.size());
+ MutableSpan<float3> rotations = result_rotations.slice(offset, bary_coords.size());
+
+ /* Use one matrix multiplication per point instead of three (for each triangle corner). */
+ float rotation_matrix[3][3];
+ mat4_to_rot(rotation_matrix, transform.values);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
@@ -458,17 +470,17 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
const int v0_index = mesh.mloop[looptri.tri[0]].v;
const int v1_index = mesh.mloop[looptri.tri[1]].v;
const int v2_index = mesh.mloop[looptri.tri[2]].v;
- const float3 v0_pos = mesh.mvert[v0_index].co;
- const float3 v1_pos = mesh.mvert[v1_index].co;
- const float3 v2_pos = mesh.mvert[v2_index].co;
+ const float3 v0_pos = float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = float3(mesh.mvert[v2_index].co);
- ids[i] = (int)(bary_coord.hash()) + (uint64_t)looptri_index;
+ ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index);
normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos);
+ mul_m3_v3(rotation_matrix, normals[i]);
rotations[i] = normal_to_euler_rotation(normals[i]);
}
i_instance++;
- i_point += bary_coords.size();
}
}
@@ -478,172 +490,239 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
}
BLI_NOINLINE static void add_remaining_point_attributes(
- Span<GeometryInstanceGroup> sets,
- Span<int> group_start_indices,
- Map<std::string, AttributeKind> &attributes,
+ Span<GeometryInstanceGroup> set_groups,
+ Span<int> instance_start_offsets,
+ const Map<std::string, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
- interpolate_existing_attributes(
- sets, group_start_indices, attributes, component, bary_coords_array, looptri_indices_array);
- compute_special_attributes(sets, component, bary_coords_array, looptri_indices_array);
+ interpolate_existing_attributes(set_groups,
+ instance_start_offsets,
+ attributes,
+ component,
+ bary_coords_array,
+ looptri_indices_array);
+ compute_special_attributes(
+ set_groups, instance_start_offsets, component, bary_coords_array, looptri_indices_array);
+}
+
+static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
+ const StringRef density_attribute_name,
+ const float density,
+ const int seed,
+ MutableSpan<Vector<float3>> positions_all,
+ MutableSpan<Vector<float3>> bary_coords_all,
+ MutableSpan<Vector<int>> looptri_indices_all)
+{
+ /* If there is an attribute name, the default value for the densities should be zero so that
+ * points are only scattered where the attribute exists. Otherwise, just "ignore" the density
+ * factors. */
+ const bool use_one_default = density_attribute_name.is_empty();
+
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
+ const Mesh &mesh = *component.get_for_read();
+ for (const float4x4 &transform : set_group.transforms) {
+ Vector<float3> &positions = positions_all[i_instance];
+ Vector<float3> &bary_coords = bary_coords_all[i_instance];
+ Vector<int> &looptri_indices = looptri_indices_all[i_instance];
+ sample_mesh_surface(mesh,
+ transform,
+ density,
+ &density_factors,
+ seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+ i_instance++;
+ }
+ }
+}
+
+static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups,
+ const StringRef density_attribute_name,
+ const float density,
+ const int seed,
+ const float minimum_distance,
+ MutableSpan<Vector<float3>> positions_all,
+ MutableSpan<Vector<float3>> bary_coords_all,
+ MutableSpan<Vector<int>> looptri_indices_all)
+{
+ Array<int> instance_start_offsets(positions_all.size());
+ int initial_points_len = 0;
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ for (const float4x4 &transform : set_group.transforms) {
+ Vector<float3> &positions = positions_all[i_instance];
+ Vector<float3> &bary_coords = bary_coords_all[i_instance];
+ Vector<int> &looptri_indices = looptri_indices_all[i_instance];
+ sample_mesh_surface(
+ mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices);
+
+ instance_start_offsets[i_instance] = initial_points_len;
+ initial_points_len += positions.size();
+ i_instance++;
+ }
+ }
+
+ /* If there is an attribute name, the default value for the densities should be zero so that
+ * points are only scattered where the attribute exists. Otherwise, just "ignore" the density
+ * factors. */
+ const bool use_one_default = density_attribute_name.is_empty();
+
+ /* Unlike the other result arrays, the elimination mask in stored as a flat array for every
+ * point, in order to simplify culling points from the KDTree (which needs to know about all
+ * points at once). */
+ Array<bool> elimination_mask(initial_points_len, false);
+ update_elimination_mask_for_close_points(positions_all,
+ instance_start_offsets,
+ minimum_distance,
+ elimination_mask,
+ initial_points_len);
+
+ i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ Vector<float3> &positions = positions_all[i_instance];
+ Vector<float3> &bary_coords = bary_coords_all[i_instance];
+ Vector<int> &looptri_indices = looptri_indices_all[i_instance];
+
+ const int offset = instance_start_offsets[i_instance];
+ update_elimination_mask_based_on_density_factors(
+ mesh,
+ density_factors,
+ bary_coords,
+ looptri_indices,
+ elimination_mask.as_mutable_span().slice(offset, positions.size()));
+
+ eliminate_points_based_on_mask(elimination_mask.as_span().slice(offset, positions.size()),
+ positions,
+ bary_coords,
+ looptri_indices);
+
+ i_instance++;
+ }
+ }
}
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- GeometrySet geometry_set_out;
const GeometryNodePointDistributeMode distribute_method =
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
- if (!geometry_set.has_mesh() && !geometry_set.has_instances()) {
- params.error_message_add(NodeWarningType::Error, "Geometry must contain a mesh.");
- params.set_output("Geometry", std::move(geometry_set_out));
- return;
- }
-
+ const int seed = params.get_input<int>("Seed");
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
if (density <= 0.0f) {
- params.set_output("Geometry", std::move(geometry_set_out));
+ params.set_output("Geometry", GeometrySet());
return;
}
- const int seed = params.get_input<int>("Seed");
-
- Vector<GeometryInstanceGroup> sets = bke::geometry_set_gather_instances(geometry_set);
- int instances_len = 0;
- for (GeometryInstanceGroup set_group : sets) {
- instances_len += set_group.transforms.size();
+ Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
+ if (set_groups.is_empty()) {
+ params.set_output("Geometry", GeometrySet());
+ return;
}
- /* Store data per-instance in order to simplify attribute access after the scattering,
- * and to make the point elimination simpler for the poisson disk mode. Node that some
- * vectors will be empty if any instances don't contain mesh data. */
- Array<Vector<float3>> positions_array(instances_len);
- Array<Vector<float3>> bary_coords_array(instances_len);
- Array<Vector<int>> looptri_indices_array(instances_len);
-
- int initial_points_len = 0;
- int i_instance = 0;
- for (const GeometryInstanceGroup &set_group : sets) {
- const GeometrySet &set = set_group.geometry_set;
+ /* Remove any set inputs that don't contain a mesh, to avoid checking later on. */
+ for (int i = set_groups.size() - 1; i >= 0; i--) {
+ const GeometrySet &set = set_groups[i].geometry_set;
if (!set.has_mesh()) {
- continue;
+ set_groups.remove_and_reorder(i);
}
+ }
- const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *component.get_for_read();
- for (const float4x4 &transform : set_group.transforms) {
- Vector<float3> &positions = positions_array[i_instance];
- Vector<float3> &bary_coords = bary_coords_array[i_instance];
- Vector<int> &looptri_indices = looptri_indices_array[i_instance];
-
- switch (distribute_method) {
- case GEO_NODE_POINT_DISTRIBUTE_RANDOM: {
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
- density_attribute_name, ATTR_DOMAIN_CORNER, 1.0f);
- initial_points_len += sample_mesh_surface(mesh,
- transform,
- density,
- &density_factors,
- seed,
- positions,
- bary_coords,
- looptri_indices);
- break;
- }
- case GEO_NODE_POINT_DISTRIBUTE_POISSON:
- initial_points_len += sample_mesh_surface(
- mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices);
- break;
- }
- i_instance++;
- }
+ if (set_groups.is_empty()) {
+ params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh"));
+ params.set_output("Geometry", GeometrySet());
+ return;
}
- if (distribute_method == GEO_NODE_POINT_DISTRIBUTE_POISSON) {
- /* Unlike the other result arrays, the elimination mask in stored as a flat array for every
- * point, in order to simplify culling points from the KDTree (which needs to know about all
- * points at once). */
- Array<bool> elimination_mask(initial_points_len, false);
- const float minimum_distance = params.get_input<float>("Distance Min");
- update_elimination_mask_for_close_points(
- positions_array, minimum_distance, elimination_mask, initial_points_len);
-
- int i_point = 0;
- i_instance = 0;
- for (const GeometryInstanceGroup &set_group : sets) {
- const GeometrySet &set = set_group.geometry_set;
- if (!set.has_mesh()) {
- continue;
- }
+ int instances_len = 0;
+ for (GeometryInstanceGroup &set_group : set_groups) {
+ instances_len += set_group.transforms.size();
+ }
- const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *component.get_for_read();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
- density_attribute_name, ATTR_DOMAIN_CORNER, 1.0f);
-
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- Vector<float3> &positions = positions_array[i_instance];
- Vector<float3> &bary_coords = bary_coords_array[i_instance];
- Vector<int> &looptri_indices = looptri_indices_array[i_instance];
-
- update_elimination_mask_based_on_density_factors(
- mesh,
- density_factors,
- bary_coords,
- looptri_indices,
- elimination_mask.as_mutable_span().slice(i_point, positions.size()));
-
- /* The positions vector's size is changed, temporarily store the
- * original size to properly advance the elimination mask index. */
- const int initial_positions_size = positions.size();
- eliminate_points_based_on_mask(elimination_mask.as_span().slice(i_point, positions.size()),
- positions,
- bary_coords,
- looptri_indices);
-
- i_point += initial_positions_size;
- i_instance++;
- }
+ /* Store data per-instance in order to simplify attribute access after the scattering,
+ * and to make the point elimination simpler for the poisson disk mode. Note that some
+ * vectors will be empty if any instances don't contain mesh data. */
+ Array<Vector<float3>> positions_all(instances_len);
+ Array<Vector<float3>> bary_coords_all(instances_len);
+ Array<Vector<int>> looptri_indices_all(instances_len);
+
+ switch (distribute_method) {
+ case GEO_NODE_POINT_DISTRIBUTE_RANDOM: {
+ distribute_points_random(set_groups,
+ density_attribute_name,
+ density,
+ seed,
+ positions_all,
+ bary_coords_all,
+ looptri_indices_all);
+ break;
+ }
+ case GEO_NODE_POINT_DISTRIBUTE_POISSON: {
+ const float minimum_distance = params.extract_input<float>("Distance Min");
+ distribute_points_poisson_disk(set_groups,
+ density_attribute_name,
+ density,
+ seed,
+ minimum_distance,
+ positions_all,
+ bary_coords_all,
+ looptri_indices_all);
+ break;
}
}
int final_points_len = 0;
- Array<int> group_start_indices(sets.size());
- for (const int i : positions_array.index_range()) {
- Vector<float3> &positions = positions_array[i];
- group_start_indices[i] = final_points_len;
+ Array<int> instance_start_offsets(set_groups.size());
+ for (const int i : positions_all.index_range()) {
+ Vector<float3> &positions = positions_all[i];
+ instance_start_offsets[i] = final_points_len;
final_points_len += positions.size();
}
PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len);
- int i_point = 0;
- for (Vector<float3> &positions : positions_array) {
- memcpy(pointcloud->co + i_point, positions.data(), sizeof(float3) * positions.size());
- i_point += positions.size();
+ for (const int instance_index : positions_all.index_range()) {
+ const int offset = instance_start_offsets[instance_index];
+ Span<float3> positions = positions_all[instance_index];
+ memcpy(pointcloud->co + offset, positions.data(), sizeof(float3) * positions.size());
}
- MutableSpan(pointcloud->radius, pointcloud->totpoint).fill(0.05f);
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ GeometrySet geometry_set_out = GeometrySet::create_with_pointcloud(pointcloud);
PointCloudComponent &point_component =
geometry_set_out.get_component_for_write<PointCloudComponent>();
- point_component.replace(pointcloud);
Map<std::string, AttributeKind> attributes;
bke::gather_attribute_info(
- attributes, {GeometryComponentType::Mesh}, sets, {"position", "normal", "id"});
- add_remaining_point_attributes(sets,
- group_start_indices,
+ attributes, {GEO_COMPONENT_TYPE_MESH}, set_groups, {"position", "normal", "id"});
+ add_remaining_point_attributes(set_groups,
+ instance_start_offsets,
attributes,
point_component,
- bary_coords_array,
- looptri_indices_array);
+ bary_coords_all,
+ looptri_indices_all);
params.set_output("Geometry", std::move(geometry_set_out));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index 669b5ee4614..dbbb73bd36d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -103,7 +103,7 @@ static void get_instanced_data__collection(
if (BLI_listbase_is_empty(&collection->children) &&
BLI_listbase_is_empty(&collection->gobject)) {
- params.error_message_add(NodeWarningType::Info, "Collection is empty.");
+ params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty"));
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index cb3cd012c77..015f4cd38e7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -46,6 +46,9 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
{
OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ if (!position_attribute) {
+ return;
+ }
ReadAttributePtr attribute = params.get_input_attribute(
"Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
if (!attribute) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface_simple.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
index 2a0cb727cd6..06c5586a3ff 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface_simple.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
@@ -25,20 +25,20 @@
#include "node_geometry_util.hh"
-static bNodeSocketTemplate geo_node_subdivision_surface_simple_in[] = {
+static bNodeSocketTemplate geo_node_subdivide_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_subdivision_surface_simple_out[] = {
+static bNodeSocketTemplate geo_node_subdivide_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
-static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params)
+static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -49,7 +49,7 @@ static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params)
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
- "Disabled, Blender was built without OpenSubdiv");
+ TIP_("Disabled, Blender was built without OpenSubdiv"));
params.set_output("Geometry", std::move(geometry_set));
return;
#endif
@@ -91,7 +91,8 @@ static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params)
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
BKE_mesh_calc_normals(mesh_out);
- geometry_set.replace_mesh(mesh_out);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out);
BKE_subdiv_free(subdiv);
@@ -99,17 +100,12 @@ static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params)
}
} // namespace blender::nodes
-void register_node_type_geo_subdivision_surface_simple()
+void register_node_type_geo_subdivide()
{
static bNodeType ntype;
- geo_node_type_base(&ntype,
- GEO_NODE_SUBDIVISION_SURFACE_SIMPLE,
- "Simple Subdivision Surface",
- NODE_CLASS_GEOMETRY,
- 0);
- node_type_socket_templates(
- &ntype, geo_node_subdivision_surface_simple_in, geo_node_subdivision_surface_simple_out);
- ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_simple_exec;
+ geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE, "Subdivide", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_subdivide_in, geo_node_subdivide_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index 47304a0de68..b14a512ce28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -69,7 +69,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
#else
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
- /* Only process subdivion if level is greater than 0. */
+ /* Only process subdivision if level is greater than 0. */
if (subdiv_level == 0) {
params.set_output("Geometry", std::move(geometry_set));
return;
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];