diff options
Diffstat (limited to 'source/blender/nodes/geometry')
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 ¶ms) +{ + 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 ¶ms, + 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); +} |