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:
authorJacques Lucke <jacques@blender.org>2020-12-02 15:25:25 +0300
committerJacques Lucke <jacques@blender.org>2020-12-02 17:38:47 +0300
commit6be56c13e96048cbc494ba5473a8deaf2cf5a6f8 (patch)
tree83435d439b3d106eb1405b2b815bf1d5aa1fdd43 /source/blender/nodes/geometry
parentddbe3274eff68523547bc8eb70dd95c3d411b89b (diff)
Geometry Nodes: initial scattering and geometry nodes
This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
Diffstat (limited to 'source/blender/nodes/geometry')
-rw-r--r--source/blender/nodes/geometry/node_geometry_exec.cc23
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc45
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc29
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh39
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc167
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc152
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_common.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc96
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc276
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc94
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc139
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc87
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc160
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc113
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc145
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc79
16 files changed, 1689 insertions, 0 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc
new file mode 100644
index 00000000000..64a04d79076
--- /dev/null
+++ b/source/blender/nodes/geometry/node_geometry_exec.cc
@@ -0,0 +1,23 @@
+/*
+ * 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 "NOD_geometry_exec.hh"
+
+MAKE_CPP_TYPE(GeometrySet, GeometrySet);
+
+namespace blender::nodes {
+
+}
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
new file mode 100644
index 00000000000..d4a9805f311
--- /dev/null
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -0,0 +1,45 @@
+/*
+ * 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 <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "NOD_geometry.h"
+
+#include "BKE_node.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_node_types.h"
+
+#include "RNA_access.h"
+
+bNodeTreeType *ntreeType_Geometry;
+
+void register_node_tree_type_geo(void)
+{
+ bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
+ MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
+ tt->type = NTREE_GEOMETRY;
+ strcpy(tt->idname, "GeometryNodeTree");
+ strcpy(tt->ui_name, N_("Geometry Node Editor"));
+ tt->ui_icon = 0; /* defined in drawnode.c */
+ strcpy(tt->ui_description, N_("Geometry nodes"));
+ tt->rna_ext.srna = &RNA_GeometryNodeTree;
+
+ ntreeTypeAdd(tt);
+}
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
new file mode 100644
index 00000000000..e8d2494f91d
--- /dev/null
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -0,0 +1,29 @@
+/*
+ * 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 "node_util.h"
+
+bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+{
+ return STREQ(ntree->idname, "GeometryNodeTree");
+}
+
+void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
+{
+ node_type_base(ntype, type, name, nclass, flag);
+ ntype->poll = geo_node_poll_default;
+}
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
new file mode 100644
index 00000000000..bb26763642b
--- /dev/null
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string.h>
+
+#include "BLI_float3.hh"
+#include "BLI_utildefines.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_node_types.h"
+
+#include "BKE_node.h"
+
+#include "BLT_translation.h"
+
+#include "NOD_geometry.h"
+#include "NOD_geometry_exec.hh"
+
+#include "node_util.h"
+
+void geo_node_type_base(
+ struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
+bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
new file mode 100644
index 00000000000..5e2830d2f4e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "BKE_attribute.h"
+#include "BKE_attribute_access.hh"
+
+#include "BLI_array.hh"
+#include "BLI_math_base_safe.h"
+#include "BLI_rand.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "NOD_math_functions.hh"
+
+static bNodeSocketTemplate geo_node_attribute_math_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute A")},
+ {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("Attribute B")},
+ {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_math_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = NODE_MATH_ADD;
+ node->custom2 = GEO_NODE_USE_ATTRIBUTE_A | GEO_NODE_USE_ATTRIBUTE_B;
+}
+
+static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_attribute_a = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sock_float_a = sock_attribute_a->next;
+ bNodeSocket *sock_attribute_b = sock_float_a->next;
+ bNodeSocket *sock_float_b = sock_attribute_b->next;
+
+ GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node->custom2);
+
+ nodeSetSocketAvailability(sock_attribute_a, flag & GEO_NODE_USE_ATTRIBUTE_A);
+ nodeSetSocketAvailability(sock_attribute_b, flag & GEO_NODE_USE_ATTRIBUTE_B);
+ nodeSetSocketAvailability(sock_float_a, !(flag & GEO_NODE_USE_ATTRIBUTE_A));
+ nodeSetSocketAvailability(sock_float_b, !(flag & GEO_NODE_USE_ATTRIBUTE_B));
+}
+
+namespace blender::nodes {
+
+static void do_math_operation(const FloatReadAttribute &input_a,
+ const FloatReadAttribute &input_b,
+ FloatWriteAttribute result,
+ const int operation)
+{
+ const int size = input_a.size();
+
+ Span<float> span_a = input_a.get_span();
+ Span<float> span_b = input_b.get_span();
+ MutableSpan<float> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl_fl_to_fl(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float in1 = span_a[i];
+ const float in2 = span_b[i];
+ const float out = math_function(in1, in2);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ const int operation = node.custom1;
+
+ /* The result type of this node is always float. */
+ const CustomDataType result_type = CD_PROP_FLOAT;
+ /* The result domain is always point for now. */
+ const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
+
+ /* Get result attribute first, in case it has to overwrite one of the existing attributes. */
+ const std::string result_name = params.get_input<std::string>("Result");
+ WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node.custom2);
+
+ auto get_input_attribute = [&](GeometryNodeUseAttributeFlag use_flag,
+ StringRef attribute_socket_identifier,
+ StringRef value_socket_identifier) {
+ if (flag & use_flag) {
+ const std::string attribute_name = params.get_input<std::string>(
+ attribute_socket_identifier);
+ return component.attribute_try_get_for_read(attribute_name, result_domain, result_type);
+ }
+ const float value = params.get_input<float>(value_socket_identifier);
+ return component.attribute_get_constant_for_read(result_domain, result_type, &value);
+ };
+
+ ReadAttributePtr attribute_a = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_A, "Attribute A", "A");
+ ReadAttributePtr attribute_b = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_B, "Attribute B", "B");
+
+ if (!attribute_a || !attribute_b) {
+ /* Attribute wasn't found. */
+ return;
+ }
+
+ do_math_operation(
+ std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
+}
+
+static void geo_node_attribute_math_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_math()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MATH, "Attribute Math", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec;
+ node_type_update(&ntype, geo_node_attribute_math_update);
+ node_type_init(&ntype, geo_node_attribute_math_init);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
new file mode 100644
index 00000000000..a0ba8e3bf81
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -0,0 +1,152 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_math_matrix.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+
+#include "RNA_enum_types.h"
+
+#include "BKE_mesh.h"
+
+#include "bmesh.h"
+#include "tools/bmesh_boolean.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_boolean_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry A")},
+ {SOCK_GEOMETRY, N_("Geometry B")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_boolean_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+{
+ return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
+}
+
+static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
+{
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
+
+ BMesh *bm;
+ {
+ struct BMeshCreateParams bmesh_create_params = {0};
+ bmesh_create_params.use_toolflags = false;
+ bm = BM_mesh_create(&allocsize, &bmesh_create_params);
+ }
+
+ {
+ struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
+ bmesh_from_mesh_params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
+ BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
+ }
+
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+ int tottri;
+ BMLoop *(*looptris)[3] = (BMLoop *
+ (*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
+ BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
+
+ const int i_faces_end = mesh_b->totpoly;
+
+ /* We need face normals because of 'BM_face_split_edgenet'
+ * we could calculate on the fly too (before calling split). */
+
+ int i = 0;
+ BMIter iter;
+ BMFace *bm_face;
+ BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
+ normalize_v3(bm_face->no);
+
+ /* Temp tag to test which side split faces are from. */
+ BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
+
+ i++;
+ if (i == i_faces_end) {
+ break;
+ }
+ }
+
+ BM_mesh_boolean(
+ bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, boolean_mode);
+
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ MEM_freeN(looptris);
+
+ return result;
+}
+
+namespace blender::nodes {
+static void geo_node_boolean_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry A");
+ GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry B");
+ GeometrySet geometry_set_out;
+
+ GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
+ if (operation < 0 || operation > 2) {
+ BLI_assert(false);
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
+ const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
+ const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
+
+ if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
+ if (operation == GEO_NODE_BOOLEAN_UNION) {
+ if (mesh_in_a != nullptr) {
+ params.set_output("Geometry", geometry_set_in_a);
+ }
+ else {
+ params.set_output("Geometry", geometry_set_in_b);
+ }
+ }
+ else {
+ params.set_output("Geometry", geometry_set_in_a);
+ }
+ return;
+ }
+
+ Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
+ geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
+
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_boolean()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc
new file mode 100644
index 00000000000..8adc3962698
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc
@@ -0,0 +1,45 @@
+/*
+ * 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 "BKE_node.h"
+
+#include "NOD_geometry.h"
+
+#include "NOD_common.h"
+#include "node_common.h"
+#include "node_geometry_util.hh"
+
+void register_node_type_geo_group(void)
+{
+ static bNodeType ntype;
+
+ node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", 0, 0);
+ ntype.type = NODE_GROUP;
+ ntype.poll = geo_node_poll_default;
+ ntype.poll_instance = node_group_poll_instance;
+ ntype.insert_link = node_insert_link_default;
+ ntype.update_internal_links = node_update_internal_links_default;
+ ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup");
+ BLI_assert(ntype.rna_ext.srna != nullptr);
+ RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
+
+ node_type_socket_templates(&ntype, nullptr, nullptr);
+ node_type_size(&ntype, 140, 60, 400);
+ node_type_label(&ntype, node_group_label);
+ node_type_group_update(&ntype, node_group_update);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
new file mode 100644
index 00000000000..22e75b3fe03
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -0,0 +1,96 @@
+/*
+ * 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_math_base.h"
+#include "BLI_math_rotation.h"
+
+#include "DNA_modifier_types.h"
+
+#include "node_geometry_util.hh"
+
+extern "C" {
+Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
+}
+
+static bNodeSocketTemplate geo_node_edge_split_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Edge Angle"), true},
+ {SOCK_FLOAT,
+ N_("Angle"),
+ DEG2RADF(30.0f),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ DEG2RADF(180.0f),
+ PROP_ANGLE},
+ {SOCK_BOOLEAN, N_("Sharp Edges")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_edge_split_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+static void geo_node_edge_split_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const bool use_sharp_flag = params.extract_input<bool>("Sharp Edges");
+ const bool use_edge_angle = params.extract_input<bool>("Edge Angle");
+
+ if (!use_edge_angle && !use_sharp_flag) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const float split_angle = params.extract_input<float>("Angle");
+ const Mesh *mesh_in = geometry_set.get_mesh_for_read();
+
+ /* Use modifier struct to pass arguments to the modifier code. */
+ EdgeSplitModifierData emd;
+ memset(&emd, 0, sizeof(EdgeSplitModifierData));
+ emd.split_angle = split_angle;
+ if (use_edge_angle) {
+ emd.flags = MOD_EDGESPLIT_FROMANGLE;
+ }
+ if (use_sharp_flag) {
+ emd.flags |= MOD_EDGESPLIT_FROMFLAG;
+ }
+
+ Mesh *mesh_out = doEdgeSplit(mesh_in, &emd);
+ geometry_set.replace_mesh(mesh_out);
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_edge_split()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_edge_split_in, geo_node_edge_split_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
new file mode 100644
index 00000000000..3bf560f86f5
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -0,0 +1,276 @@
+/*
+ * 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 "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_pointcloud.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_join_geometry_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_join_geometry_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components)
+{
+ int totverts = 0;
+ int totloops = 0;
+ int totedges = 0;
+ int totpolys = 0;
+
+ for (const MeshComponent *mesh_component : src_components) {
+ const Mesh *mesh = mesh_component->get_for_read();
+ totverts += mesh->totvert;
+ totloops += mesh->totloop;
+ totedges += mesh->totedge;
+ totpolys += mesh->totpoly;
+ }
+
+ const Mesh *first_input_mesh = src_components[0]->get_for_read();
+ Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
+ BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+
+ int vert_offset = 0;
+ int loop_offset = 0;
+ int edge_offset = 0;
+ int poly_offset = 0;
+ for (const MeshComponent *mesh_component : src_components) {
+ const Mesh *mesh = mesh_component->get_for_read();
+ if (mesh == nullptr) {
+ continue;
+ }
+
+ for (const int i : IndexRange(mesh->totvert)) {
+ const MVert &old_vert = mesh->mvert[i];
+ MVert &new_vert = new_mesh->mvert[vert_offset + i];
+ new_vert = old_vert;
+ }
+
+ for (const int i : IndexRange(mesh->totedge)) {
+ const MEdge &old_edge = mesh->medge[i];
+ MEdge &new_edge = new_mesh->medge[edge_offset + i];
+ new_edge = old_edge;
+ new_edge.v1 += vert_offset;
+ new_edge.v2 += vert_offset;
+ }
+ for (const int i : IndexRange(mesh->totloop)) {
+ const MLoop &old_loop = mesh->mloop[i];
+ MLoop &new_loop = new_mesh->mloop[loop_offset + i];
+ new_loop = old_loop;
+ new_loop.v += vert_offset;
+ new_loop.e += edge_offset;
+ }
+ for (const int i : IndexRange(mesh->totpoly)) {
+ const MPoly &old_poly = mesh->mpoly[i];
+ MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
+ new_poly = old_poly;
+ new_poly.loopstart += loop_offset;
+ }
+
+ vert_offset += mesh->totvert;
+ loop_offset += mesh->totloop;
+ edge_offset += mesh->totedge;
+ poly_offset += mesh->totpoly;
+ }
+
+ return new_mesh;
+}
+
+template<typename Component>
+static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
+{
+ return components;
+}
+
+static Set<std::string> find_all_attribute_names(Span<const GeometryComponent *> components)
+{
+ Set<std::string> attribute_names;
+ for (const GeometryComponent *component : components) {
+ Set<std::string> names = component->attribute_names();
+ for (const std::string &name : names) {
+ attribute_names.add(name);
+ }
+ }
+ return attribute_names;
+}
+
+static void determine_final_data_type_and_domain(Span<const GeometryComponent *> components,
+ StringRef attribute_name,
+ CustomDataType *r_type,
+ AttributeDomain *r_domain)
+{
+ for (const GeometryComponent *component : components) {
+ ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
+ if (attribute) {
+ /* TODO: Use data type with most information. */
+ *r_type = bke::cpp_type_to_custom_data_type(attribute->cpp_type());
+ /* TODO: Use highest priority domain. */
+ *r_domain = attribute->domain();
+ return;
+ }
+ }
+ BLI_assert(false);
+}
+
+static void fill_new_attribute(Span<const GeometryComponent *> src_components,
+ StringRef attribute_name,
+ const CustomDataType data_type,
+ const AttributeDomain domain,
+ fn::GMutableSpan dst_span)
+{
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+
+ int offset = 0;
+ for (const GeometryComponent *component : src_components) {
+ const int domain_size = component->attribute_domain_size(domain);
+ ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ attribute_name, domain, data_type, nullptr);
+
+ fn::GSpan src_span = read_attribute->get_span();
+ const void *src_buffer = src_span.data();
+ void *dst_buffer = dst_span[offset];
+ cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
+
+ offset += domain_size;
+ }
+}
+
+static void join_attributes(Span<const GeometryComponent *> src_components,
+ GeometryComponent &result,
+ Span<StringRef> ignored_attributes = {})
+{
+ Set<std::string> attribute_names = find_all_attribute_names(src_components);
+ for (StringRef name : ignored_attributes) {
+ attribute_names.remove(name);
+ }
+
+ for (const std::string &attribute_name : attribute_names) {
+ CustomDataType data_type;
+ AttributeDomain domain;
+ determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
+
+ result.attribute_try_create(attribute_name, domain, data_type);
+ WriteAttributePtr write_attribute = result.attribute_try_get_for_write(attribute_name);
+ if (!write_attribute ||
+ &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
+ write_attribute->domain() != domain) {
+ continue;
+ }
+ fn::GMutableSpan dst_span = write_attribute->get_span();
+ fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
+ write_attribute->apply_span();
+ }
+}
+
+static void join_components(Span<const MeshComponent *> src_components, GeometrySet &result)
+{
+ Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(src_components);
+
+ MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
+ dst_component.replace(new_mesh);
+
+ /* The position attribute is handled above already. */
+ join_attributes(to_base_components(src_components), dst_component, {"position"});
+}
+
+static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)
+{
+ int totpoints = 0;
+ for (const PointCloudComponent *pointcloud_component : src_components) {
+ totpoints += pointcloud_component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ }
+
+ PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoints);
+ dst_component.replace(pointcloud);
+
+ join_attributes(to_base_components(src_components), dst_component);
+}
+
+static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
+{
+ InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
+ for (const InstancesComponent *component : src_components) {
+ const int size = component->instances_amount();
+ Span<const Object *> objects = component->objects();
+ Span<float3> positions = component->positions();
+ Span<float3> rotations = component->rotations();
+ Span<float3> scales = component->scales();
+ for (const int i : IndexRange(size)) {
+ dst_component.add_instance(objects[i], positions[i], rotations[i], scales[i]);
+ }
+ }
+}
+
+template<typename Component>
+static void join_component_type(Span<const GeometrySet *> src_geometry_sets, GeometrySet &result)
+{
+ Vector<const Component *> components;
+ for (const GeometrySet *geometry_set : src_geometry_sets) {
+ const Component *component = geometry_set->get_component_for_read<Component>();
+ if (component != nullptr && !component->is_empty()) {
+ components.append(component);
+ }
+ }
+
+ if (components.size() == 0) {
+ return;
+ }
+ if (components.size() == 1) {
+ result.add(*components[0]);
+ return;
+ }
+ join_components(components, result);
+}
+
+static void geo_node_join_geometry_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_a = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_b = params.extract_input<GeometrySet>("Geometry_001");
+ GeometrySet geometry_set_result;
+
+ std::array<const GeometrySet *, 2> src_geometry_sets = {&geometry_set_a, &geometry_set_b};
+
+ join_component_type<MeshComponent>(src_geometry_sets, geometry_set_result);
+ join_component_type<PointCloudComponent>(src_geometry_sets, geometry_set_result);
+ join_component_type<InstancesComponent>(src_geometry_sets, geometry_set_result);
+
+ params.set_output("Geometry", std::move(geometry_set_result));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_join_geometry()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
new file mode 100644
index 00000000000..8d80e1ce40a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -0,0 +1,94 @@
+/*
+ * 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 "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "BLI_math_matrix.h"
+
+static bNodeSocketTemplate geo_node_object_info_in[] = {
+ {SOCK_OBJECT, N_("Object")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_object_info_out[] = {
+ {SOCK_VECTOR, N_("Location")},
+ {SOCK_VECTOR, N_("Rotation")},
+ {SOCK_VECTOR, N_("Scale")},
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+static void geo_node_object_info_exec(GeoNodeExecParams params)
+{
+ bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
+ "Object");
+ Object *object = params.handle_map().lookup(object_handle);
+
+ float3 location = {0, 0, 0};
+ float3 rotation = {0, 0, 0};
+ float3 scale = {0, 0, 0};
+ GeometrySet geometry_set;
+
+ const Object *self_object = params.self_object();
+
+ if (object != nullptr) {
+ float quaternion[4];
+ mat4_decompose(location, quaternion, scale, object->obmat);
+ quat_to_eul(rotation, quaternion);
+
+ if (object != self_object) {
+ if (object->type == OB_MESH) {
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object, false);
+ if (mesh != nullptr) {
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+
+ /* Make a copy because the life time of the other mesh might be shorter. */
+ Mesh *copied_mesh = BKE_mesh_copy_for_eval(mesh, false);
+
+ /* Transform into the local space of the object that is being modified. */
+ float transform[4][4];
+ mul_m4_m4m4(transform, self_object->imat, object->obmat);
+ BKE_mesh_transform(copied_mesh, transform, true);
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(copied_mesh);
+ mesh_component.copy_vertex_group_names_from_object(*object);
+ }
+ }
+ }
+ }
+
+ params.set_output("Location", location);
+ params.set_output("Rotation", rotation);
+ params.set_output("Scale", scale);
+ params.set_output("Geometry", geometry_set);
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_object_info()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_object_info_in, geo_node_object_info_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_object_info_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
new file mode 100644
index 00000000000..7f94ca35e6e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -0,0 +1,139 @@
+/*
+ * 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_float3.hh"
+#include "BLI_hash.h"
+#include "BLI_math_vector.h"
+#include "BLI_rand.hh"
+#include "BLI_span.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_deform.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_pointcloud.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_point_distribute_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_STRING, N_("Density Attribute")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_point_distribute_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
+ const float density,
+ const FloatReadAttribute &density_factors)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(mesh);
+
+ Vector<float3> points;
+
+ for (const int looptri_index : IndexRange(looptris_len)) {
+ const MLoopTri &looptri = looptris[looptri_index];
+ 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 float v0_density_factor = std::max(0.0f, density_factors[v0_index]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
+ const float looptri_density_factor = (v0_density_factor + v1_density_factor +
+ v2_density_factor) /
+ 3.0f;
+ const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
+
+ const int looptri_seed = BLI_hash_int(looptri_index);
+ RandomNumberGenerator looptri_rng(looptri_seed);
+
+ const float points_amount_fl = area * density * looptri_density_factor;
+ const float add_point_probability = fractf(points_amount_fl);
+ const bool add_point = add_point_probability > looptri_rng.get_float();
+ const int point_amount = (int)points_amount_fl + (int)add_point;
+
+ for (int i = 0; i < point_amount; i++) {
+ const float3 bary_coords = looptri_rng.get_barycentric_coordinates();
+ float3 point_pos;
+ interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
+ points.append(point_pos);
+ }
+ }
+
+ return points;
+}
+
+static void geo_node_point_distribute_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
+ const float density = params.extract_input<float>("Density");
+ const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
+
+ if (density <= 0.0f) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
+ const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh *mesh_in = mesh_component.get_for_read();
+
+ const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
+ density_attribute, ATTR_DOMAIN_POINT, 1.0f);
+
+ Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
+ memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
+ for (const int i : points.index_range()) {
+ *(float3 *)(pointcloud->co + i) = points[i];
+ pointcloud->radius[i] = 0.05f;
+ }
+
+ geometry_set_out.replace_pointcloud(pointcloud);
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_point_distribute()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
new file mode 100644
index 00000000000..bb8f1ff4909
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.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 "BKE_mesh.h"
+#include "BKE_persistent_data_handle.hh"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_point_instance_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_OBJECT, N_("Object")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_point_instance_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void add_instances_from_geometry_component(InstancesComponent &instances,
+ const GeometryComponent &src_geometry,
+ Object *object)
+{
+ Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ "scale", ATTR_DOMAIN_POINT, {1, 1, 1});
+
+ for (const int i : IndexRange(positions.size())) {
+ instances.add_instance(object, positions[i], rotations[i], scales[i]);
+ }
+}
+
+static void geo_node_point_instance_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+ bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
+ "Object");
+ Object *object = params.handle_map().lookup(object_handle);
+
+ if (object != nullptr && object != params.self_object()) {
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ if (geometry_set.has<MeshComponent>()) {
+ add_instances_from_geometry_component(
+ instances, *geometry_set.get_component_for_read<MeshComponent>(), object);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ add_instances_from_geometry_component(
+ instances, *geometry_set.get_component_for_read<PointCloudComponent>(), object);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set_out));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_point_instance()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
new file mode 100644
index 00000000000..68ea5481028
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
@@ -0,0 +1,160 @@
+/*
+ * 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 "BLI_rand.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
+static bNodeSocketTemplate geo_node_random_attribute_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_random_attribute_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_random_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = CD_PROP_FLOAT;
+}
+
+static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+
+ const int data_type = node->custom1;
+
+ nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+}
+
+namespace blender::nodes {
+
+static void randomize_attribute(FloatWriteAttribute &attribute,
+ float min,
+ float max,
+ RandomNumberGenerator &rng)
+{
+ MutableSpan<float> attribute_span = attribute.get_span();
+ for (const int i : IndexRange(attribute.size())) {
+ const float value = rng.get_float() * (max - min) + min;
+ attribute_span[i] = value;
+ }
+ attribute.apply_span();
+}
+
+static void randomize_attribute(Float3WriteAttribute &attribute,
+ float3 min,
+ float3 max,
+ RandomNumberGenerator &rng)
+{
+ MutableSpan<float3> attribute_span = attribute.get_span();
+ for (const int i : IndexRange(attribute.size())) {
+ const float x = rng.get_float();
+ const float y = rng.get_float();
+ const float z = rng.get_float();
+ const float3 value = float3(x, y, z) * (max - min) + min;
+ attribute_span[i] = value;
+ }
+ attribute.apply_span();
+}
+
+static void randomize_attribute(GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ RandomNumberGenerator &rng)
+{
+ const bNode &node = params.node();
+ const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ if (attribute_name.empty()) {
+ return;
+ }
+
+ WriteAttributePtr attribute = component.attribute_try_ensure_for_write(
+ attribute_name, domain, data_type);
+ if (!attribute) {
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ FloatWriteAttribute float_attribute = std::move(attribute);
+ const float min_value = params.get_input<float>("Min_001");
+ const float max_value = params.get_input<float>("Max_001");
+ randomize_attribute(float_attribute, min_value, max_value, rng);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ Float3WriteAttribute float3_attribute = std::move(attribute);
+ const float3 min_value = params.get_input<float3>("Min");
+ const float3 max_value = params.get_input<float3>("Max");
+ randomize_attribute(float3_attribute, min_value, max_value, rng);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void geo_node_random_attribute_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const int seed = params.get_input<int>("Seed");
+
+ if (geometry_set.has<MeshComponent>()) {
+ RandomNumberGenerator rng;
+ rng.seed_random(seed);
+ randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, rng);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ RandomNumberGenerator rng;
+ rng.seed_random(seed + 3245231);
+ randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, rng);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_random_attribute()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out);
+ node_type_init(&ntype, geo_node_random_attribute_init);
+ node_type_update(&ntype, geo_node_random_attribute_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_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
new file mode 100644
index 00000000000..dccdf94243e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_mesh.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_subdivision_surface_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
+ {SOCK_BOOLEAN, N_("Use Creases")},
+ {SOCK_BOOLEAN, N_("Boundary Smooth")},
+ {SOCK_BOOLEAN, N_("Smooth UVs")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+#ifndef WITH_OPENSUBDIV
+ /* Return input geometry if Blender is built without OpenSubdiv. */
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+#else
+ const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
+
+ /* Only process subdivion if level is greater than 0. */
+ if (subdiv_level == 0) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const bool use_crease = params.extract_input<bool>("Use Creases");
+ const bool boundary_smooth = params.extract_input<bool>("Boundary Smooth");
+ const bool smooth_uvs = params.extract_input<bool>("Smooth UVs");
+ const Mesh *mesh_in = geometry_set.get_mesh_for_read();
+
+ /* Initialize mesh settings. */
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = (1 << subdiv_level) + 1;
+ mesh_settings.use_optimal_display = false;
+
+ /* Initialize subdivision settings. */
+ SubdivSettings subdiv_settings;
+ subdiv_settings.is_simple = false;
+ subdiv_settings.is_adaptive = false;
+ subdiv_settings.use_creases = use_crease;
+ subdiv_settings.level = subdiv_level;
+
+ subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
+ boundary_smooth);
+ subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
+ smooth_uvs);
+
+ /* Apply subdivision to mesh. */
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
+
+ /* In case of bad topology, skip to input mesh. */
+ if (subdiv == nullptr) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
+
+ geometry_set.replace_mesh(mesh_out);
+
+ // BKE_subdiv_stats_print(&subdiv->stats);
+ BKE_subdiv_free(subdiv);
+
+ params.set_output("Geometry", std::move(geometry_set));
+#endif
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_subdivision_surface()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", 0, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_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
new file mode 100644
index 00000000000..6360a3dd9f7
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -0,0 +1,145 @@
+/*
+ * 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_math_matrix.h"
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_transform_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
+ {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_transform_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static bool use_translate(const float3 rotation, const float3 scale)
+{
+ if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) {
+ return false;
+ }
+ if (compare_ff(scale.x, 1.0f, 1e-9f) != 1 || compare_ff(scale.y, 1.0f, 1e-9f) != 1 ||
+ compare_ff(scale.z, 1.0f, 1e-9f) != 1) {
+ return false;
+ }
+ return true;
+}
+
+static 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);
+ }
+ else {
+ float mat[4][4];
+ loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ BKE_mesh_transform(mesh, mat, true);
+ BKE_mesh_calc_normals(mesh);
+ }
+}
+
+static void transform_pointcloud(PointCloud *pointcloud,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ /* Use only translation if rotation and scale don't apply. */
+ if (use_translate(rotation, scale)) {
+ for (int i = 0; i < pointcloud->totpoint; i++) {
+ add_v3_v3(pointcloud->co[i], translation);
+ }
+ }
+ else {
+ float mat[4][4];
+ loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ for (int i = 0; i < pointcloud->totpoint; i++) {
+ mul_m4_v3(mat, pointcloud->co[i]);
+ }
+ }
+}
+
+static void transform_instances(InstancesComponent &instances,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ MutableSpan<float3> positions = instances.positions();
+
+ /* Use only translation if rotation and scale don't apply. */
+ if (use_translate(rotation, scale)) {
+ for (float3 &position : positions) {
+ add_v3_v3(position, translation);
+ }
+ }
+ else {
+ float mat[4][4];
+ loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ for (float3 &position : positions) {
+ mul_m4_v3(mat, position);
+ }
+ }
+}
+
+static void geo_node_transform_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const float3 translation = params.extract_input<float3>("Translation");
+ const float3 rotation = params.extract_input<float3>("Rotation");
+ const float3 scale = params.extract_input<float3>("Scale");
+
+ if (geometry_set.has_mesh()) {
+ Mesh *mesh = geometry_set.get_mesh_for_write();
+ transform_mesh(mesh, translation, rotation, scale);
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
+ transform_pointcloud(pointcloud, translation, rotation, scale);
+ }
+
+ if (geometry_set.has_instances()) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ transform_instances(instances, translation, rotation, scale);
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_transform()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_transform_in, geo_node_transform_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_transform_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
new file mode 100644
index 00000000000..cec717e4a61
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -0,0 +1,79 @@
+/*
+ * 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_node_types.h"
+
+#include "RNA_enum_types.h"
+
+#include "node_geometry_util.hh"
+
+extern "C" {
+Mesh *triangulate_mesh(Mesh *mesh,
+ const int quad_method,
+ const int ngon_method,
+ const int min_vertices,
+ const int flag);
+}
+
+static bNodeSocketTemplate geo_node_triangulate_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Minimum Vertices"), 4, 0, 0, 0, 4, 10000},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_triangulate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE;
+ node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY;
+}
+
+namespace blender::nodes {
+static void geo_node_triangulate_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
+
+ GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
+ params.node().custom1);
+ GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
+ params.node().custom2);
+
+ /* #triangulate_mesh might modify the input mesh currently. */
+ Mesh *mesh_in = geometry_set.get_mesh_for_write();
+ if (mesh_in != nullptr) {
+ Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
+ geometry_set.replace_mesh(mesh_out);
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_triangulate()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", 0, 0);
+ node_type_socket_templates(&ntype, geo_node_triangulate_in, geo_node_triangulate_out);
+ node_type_init(&ntype, geo_triangulate_init);
+ ntype.geometry_node_execute = blender::nodes::geo_node_triangulate_exec;
+ nodeRegisterType(&ntype);
+}