diff options
Diffstat (limited to 'source/blender/nodes/geometry')
21 files changed, 1899 insertions, 239 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 87178c52ab9..2c4c7da64cc 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -71,6 +71,17 @@ static void geometry_node_tree_update(bNodeTree *ntree) ntree_update_reroute_nodes(ntree); } +static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCallback func) +{ + func(calldata, NODE_CLASS_INPUT, N_("Input")); + func(calldata, NODE_CLASS_GEOMETRY, N_("Geometry")); + func(calldata, NODE_CLASS_ATTRIBUTE, N_("Attribute")); + func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); + func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector")); + func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor")); + func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); +} + void register_node_tree_type_geo(void) { bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>( @@ -83,6 +94,7 @@ void register_node_tree_type_geo(void) tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; tt->get_from_context = geometry_node_tree_get_from_context; + tt->foreach_nodeclass = foreach_nodeclass; ntreeTypeAdd(tt); } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 271e3771006..8eac8bc0a20 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -47,4 +47,17 @@ void update_attribute_input_socket_availabilities(bNode &node, Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); +void transform_mesh(Mesh *mesh, + const float3 translation, + const float3 rotation, + const float3 scale); + +Mesh *create_cylinder_or_cone_mesh(const float3 location, + const float3 rotation, + const float radius_top, + const float radius_bottom, + const float depth, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc new file mode 100644 index 00000000000..11d220dd903 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc @@ -0,0 +1,164 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static bNodeSocketTemplate geo_node_attribute_convert_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_convert_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_convert_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert), + __func__); + + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_AUTO; + node->storage = data; +} + +namespace blender::nodes { + +static AttributeDomain get_result_domain(const GeometryComponent &component, + StringRef source_name, + StringRef result_name) +{ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); + if (source_attribute) { + return source_attribute->domain(); + } + return ATTR_DOMAIN_POINT; +} + +static void attribute_convert_calc(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const StringRef source_name, + const StringRef result_name, + const CustomDataType result_type, + const AttributeDomain domain) +{ + const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? + get_result_domain( + component, source_name, result_name) : + domain; + + ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + source_name, result_domain, result_type); + if (!source_attribute) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + source_name + "\""); + return; + } + + OutputAttributePtr result_attribute = component.attribute_try_get_for_output( + result_name, result_domain, result_type); + if (!result_attribute) { + return; + } + + fn::GSpan source_span = source_attribute->get_span(); + fn::GMutableSpan result_span = result_attribute->get_span_for_write_only(); + if (source_span.is_empty() || result_span.is_empty()) { + return; + } + BLI_assert(source_span.size() == result_span.size()); + + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type); + BLI_assert(cpp_type != nullptr); + + cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size()); + + result_attribute.apply_span_and_save(); +} + +static void geo_node_attribute_convert_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + const std::string result_name = params.extract_input<std::string>("Result"); + const std::string source_name = params.extract_input<std::string>("Attribute"); + const NodeAttributeConvert &node_storage = *(const NodeAttributeConvert *)params.node().storage; + const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type); + const AttributeDomain domain = static_cast<AttributeDomain>(node_storage.domain); + + if (result_name.empty()) { + params.set_output("Geometry", geometry_set); + return; + } + + if (geometry_set.has<MeshComponent>()) { + attribute_convert_calc(geometry_set.get_component_for_write<MeshComponent>(), + params, + source_name, + result_name, + data_type, + domain); + } + if (geometry_set.has<PointCloudComponent>()) { + attribute_convert_calc(geometry_set.get_component_for_write<PointCloudComponent>(), + params, + source_name, + result_name, + data_type, + domain); + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_convert() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_convert_in, geo_node_attribute_convert_out); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec; + ntype.draw_buttons = geo_node_attribute_convert_layout; + node_type_init(&ntype, geo_node_attribute_convert_init); + node_type_storage( + &ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index bb5b5073c32..27c35da7d37 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -224,7 +224,7 @@ static void randomize_attribute_on_component(GeometryComponent &component, if (operation != GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) { if (!component.attribute_exists(attribute_name)) { params.error_message_add(NodeWarningType::Error, - "No attribute with name '" + attribute_name + "'."); + TIP_("No attribute with name \"") + attribute_name + "\""); return; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc new file mode 100644 index 00000000000..837f0c3629a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -0,0 +1,87 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_remove_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, + N_("Attribute"), + 0.0f, + 0.0f, + 0.0f, + 1.0f, + -1.0f, + 1.0f, + PROP_NONE, + SOCK_MULTI_INPUT}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_remove_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void remove_attribute(GeometryComponent &component, + GeoNodeExecParams ¶ms, + Span<std::string> attribute_names) +{ + for (std::string attribute_name : attribute_names) { + if (attribute_name.empty()) { + continue; + } + + if (!component.attribute_try_delete(attribute_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("Cannot delete attribute with name \"") + attribute_name + + "\""); + } + } +} + +static void geo_node_attribute_remove_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + remove_attribute( + geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names); + } + if (geometry_set.has<PointCloudComponent>()) { + remove_attribute( + geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); + } + + params.set_output("Geometry", geometry_set); +} +} // namespace blender::nodes + +void register_node_type_geo_attribute_remove() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 38215b54640..9f331190420 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -100,7 +100,7 @@ static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boole } BM_mesh_boolean( - bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, boolean_mode); + bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, false, boolean_mode); Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a); BM_mesh_free(bm); diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 4e15f232934..52512769a47 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -222,8 +222,9 @@ static void join_components(Span<const MeshComponent *> src_components, Geometry dst_component.replace(new_mesh); /* Don't copy attributes that are stored directly in the mesh data structs. */ - join_attributes( - to_base_components(src_components), dst_component, {"position", "material_index"}); + join_attributes(to_base_components(src_components), + dst_component, + {"position", "material_index", "normal", "shade_smooth", "crease"}); } static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc new file mode 100644 index 00000000000..6d286a9d583 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -0,0 +1,244 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_circle_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_circle_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_circle_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCircle *node_storage = (NodeGeometryMeshCircle *)MEM_callocN( + sizeof(NodeGeometryMeshCircle), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static int circle_vert_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num + 1; + } + BLI_assert(false); + return 0; +} + +static int circle_edge_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num * 2; + } + BLI_assert(false); + return 0; +} + +static int circle_corner_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + return 0; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return verts_num; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num * 3; + } + BLI_assert(false); + return 0; +} + +static int circle_face_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) +{ + switch (fill_type) { + case GEO_NODE_MESH_CIRCLE_FILL_NONE: + return 0; + case GEO_NODE_MESH_CIRCLE_FILL_NGON: + return 1; + case GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN: + return verts_num; + } + BLI_assert(false); + return 0; +} + +static Mesh *create_circle_mesh(const float radius, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type) +{ + Mesh *mesh = BKE_mesh_new_nomain(circle_vert_total(fill_type, verts_num), + circle_edge_total(fill_type, verts_num), + 0, + circle_corner_total(fill_type, verts_num), + circle_face_total(fill_type, verts_num)); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + float angle = 0.0f; + const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); + for (MVert &vert : verts) { + copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); + angle += angle_delta; + } + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + copy_v3_v3(verts.last().co, float3(0)); + } + + /* Point all vertex normals in the up direction. */ + const short up_normal[3] = {0, 0, SHRT_MAX}; + for (MVert &vert : verts) { + copy_v3_v3_short(vert.no, up_normal); + } + + /* Create outer edges. */ + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[i]; + edge.v1 = i; + edge.v2 = (i + 1) % verts_num; + } + + /* Set loose edge flags. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[i]; + edge.flag |= ME_LOOSEEDGE; + } + } + + /* Create triangle fan edges. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MEdge &edge = edges[verts_num + i]; + edge.v1 = verts_num; + edge.v2 = i; + } + } + + /* Create corners and faces. */ + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + MPoly &poly = polys[0]; + poly.loopstart = 0; + poly.totloop = loops.size(); + + for (const int i : IndexRange(verts_num)) { + MLoop &loop = loops[i]; + loop.e = i; + loop.v = i; + } + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + for (const int i : IndexRange(verts_num)) { + MPoly &poly = polys[i]; + poly.loopstart = 3 * i; + poly.totloop = 3; + + MLoop &loop_a = loops[3 * i]; + loop_a.e = i; + loop_a.v = i; + MLoop &loop_b = loops[3 * i + 1]; + loop_b.e = verts_num + ((i + 1) % verts_num); + loop_b.v = (i + 1) % verts_num; + MLoop &loop_c = loops[3 * i + 2]; + loop_c.e = verts_num + i; + loop_c.v = verts_num; + } + } + + return mesh; +} + +static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCircle &storage = *(const NodeGeometryMeshCircle *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); + + BLI_assert(BKE_mesh_is_valid(mesh)); + + transform_mesh(mesh, location, rotation, float3(1)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_circle() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Circle", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_circle_in, geo_node_mesh_primitive_circle_out); + node_type_init(&ntype, geo_node_mesh_primitive_circle_init); + node_type_storage( + &ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_circle_exec; + ntype.draw_buttons = geo_node_mesh_primitive_circle_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc new file mode 100644 index 00000000000..5e5dbd91d31 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -0,0 +1,279 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius Top"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Radius Bottom"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Depth"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cone_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN( + sizeof(NodeGeometryMeshCone), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static int vert_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + int vert_total = 0; + if (!top_is_point) { + vert_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + vert_total++; + } + } + else { + vert_total++; + } + if (!bottom_is_point) { + vert_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + vert_total++; + } + } + else { + vert_total++; + } + + return vert_total; +} + +static int edge_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + if (top_is_point && bottom_is_point) { + return 1; + } + + int edge_total = 0; + if (!top_is_point) { + edge_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + edge_total += verts_num; + } + } + + edge_total += verts_num; + + if (!bottom_is_point) { + edge_total += verts_num; + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + edge_total += verts_num; + } + } + + return edge_total; +} + +static int corner_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int corner_total = 0; + if (!top_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += verts_num; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + corner_total += verts_num * 3; + } + } + + if (!top_is_point && !bottom_is_point) { + corner_total += verts_num * 4; + } + else { + corner_total += verts_num * 3; + } + + if (!bottom_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += verts_num; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + corner_total += verts_num * 3; + } + } + + return corner_total; +} + +static int face_total(const GeometryNodeMeshCircleFillType fill_type, + const int verts_num, + const bool top_is_point, + const bool bottom_is_point) +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int face_total = 0; + if (!top_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + face_total += verts_num; + } + } + + face_total += verts_num; + + if (!bottom_is_point) { + if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; + } + else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { + face_total += verts_num; + } + } + + return face_total; +} + +Mesh *create_cylinder_or_cone_mesh(const float3 location, + const float3 rotation, + const float radius_top, + const float radius_bottom, + const float depth, + const int verts_num, + const GeometryNodeMeshCircleFillType fill_type) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(1)); + + const bool top_is_point = radius_top == 0.0f; + const bool bottom_is_point = radius_bottom == 0.0f; + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = { + vert_total(fill_type, verts_num, top_is_point, bottom_is_point), + edge_total(fill_type, verts_num, top_is_point, bottom_is_point), + corner_total(fill_type, verts_num, top_is_point, bottom_is_point), + face_total(fill_type, verts_num, top_is_point, bottom_is_point)}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE); + const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN); + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b " + "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b", + verts_num, + radius_bottom, + radius_top, + cap_end, + cap_tri, + depth, + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius_top = params.extract_input<float>("Radius Top"); + const float radius_bottom = params.extract_input<float>("Radius Bottom"); + const float depth = params.extract_input<float>("Depth"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cylinder_or_cone_mesh( + location, rotation, radius_top, radius_bottom, depth, verts_num, fill_type); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cone() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cone_in, geo_node_mesh_primitive_cone_out); + node_type_init(&ntype, geo_node_mesh_primitive_cone_init); + node_type_storage( + &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; + ntype.draw_buttons = geo_node_mesh_primitive_cone_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc new file mode 100644 index 00000000000..5c32ff6b687 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cube_in[] = { + {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *create_cube_mesh(const float3 location, const float3 rotation, const float size) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(size)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {8, 12, 24, 6}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_cube matrix=%m4 size=%f calc_uvs=%b", + transform.values, + size, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) +{ + const float size = params.extract_input<float>("Size"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_cube_mesh(location, rotation, size); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cube() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cube_in, geo_node_mesh_primitive_cube_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cube_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc new file mode 100644 index 00000000000..158491f40e7 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -0,0 +1,103 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_in[] = { + {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Depth"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCylinder *node_storage = (NodeGeometryMeshCylinder *)MEM_callocN( + sizeof(NodeGeometryMeshCylinder), __func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; + + node->storage = node_storage; +} + +namespace blender::nodes { + +static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const NodeGeometryMeshCylinder &storage = *(const NodeGeometryMeshCylinder *)node.storage; + + const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) + storage.fill_type; + + const int verts_num = params.extract_input<int>("Vertices"); + if (verts_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float depth = params.extract_input<float>("Depth"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ + Mesh *mesh = create_cylinder_or_cone_mesh( + location, rotation, radius, radius, depth, verts_num, fill_type); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_cylinder() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_cylinder_in, geo_node_mesh_primitive_cylinder_out); + node_type_init(&ntype, geo_node_mesh_primitive_cylinder_init); + node_type_storage( + &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cylinder_exec; + ntype.draw_buttons = geo_node_mesh_primitive_cylinder_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc new file mode 100644 index 00000000000..9aaccb7d805 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -0,0 +1,91 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = { + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 0, 7}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *create_ico_sphere_mesh(const float3 location, + const float3 rotation, + const int subdivisions, + const float radius) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(1.0f)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {0, 0, 0, 0}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + subdivisions, + std::abs(radius), + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) +{ + const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 10); + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_ico_sphere_mesh(location, rotation, subdivisions, radius); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_ico_sphere() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_ico_sphere_in, geo_node_mesh_primitive_ico_sphere_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_ico_sphere_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc new file mode 100644 index 00000000000..a7d40571c39 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -0,0 +1,188 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_line_in[] = { + {SOCK_INT, N_("Count"), 10, 0.0f, 0.0f, 0.0f, 1, 10000}, + {SOCK_FLOAT, N_("Resolution"), 1.0f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, + N_("Start Location"), + 0.0f, + 0.0f, + 0.0f, + 1.0f, + -FLT_MAX, + FLT_MAX, + PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_line_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_primitive_line_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + if (RNA_enum_get(ptr, "mode") == GEO_NODE_MESH_LINE_MODE_END_POINTS) { + uiItemR(layout, ptr, "count_mode", 0, "", ICON_NONE); + } +} + +static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshLine *node_storage = (NodeGeometryMeshLine *)MEM_callocN( + sizeof(NodeGeometryMeshLine), __func__); + + node_storage->mode = GEO_NODE_MESH_LINE_MODE_OFFSET; + node_storage->count_mode = GEO_NODE_MESH_LINE_COUNT_TOTAL; + + node->storage = node_storage; +} + +static void geo_node_mesh_primitive_line_update(bNodeTree *UNUSED(tree), bNode *node) +{ + bNodeSocket *count_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *resolution_socket = count_socket->next; + bNodeSocket *start_socket = resolution_socket->next; + bNodeSocket *end_and_offset_socket = start_socket->next; + + const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)node->storage; + const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode; + const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode) + storage.count_mode; + + node_sock_label(end_and_offset_socket, + (mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) ? N_("End Location") : + N_("Offset")); + + nodeSetSocketAvailability(resolution_socket, + mode == GEO_NODE_MESH_LINE_MODE_END_POINTS && + count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION); + nodeSetSocketAvailability(count_socket, + mode == GEO_NODE_MESH_LINE_MODE_OFFSET || + count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL); +} + +namespace blender::nodes { + +static void fill_edge_data(MutableSpan<MEdge> edges) +{ + for (const int i : edges.index_range()) { + edges[i].v1 = i; + edges[i].v2 = i + 1; + edges[i].flag |= ME_LOOSEEDGE; + } +} + +static Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) +{ + if (count < 1) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + + short normal[3]; + normal_float_to_short_v3(normal, delta.normalized()); + + float3 co = start; + for (const int i : verts.index_range()) { + copy_v3_v3(verts[i].co, co); + copy_v3_v3_short(verts[i].no, normal); + co += delta; + } + + fill_edge_data(edges); + + return mesh; +} + +static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) +{ + const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)params.node().storage; + const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode; + const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode) + storage.count_mode; + + Mesh *mesh = nullptr; + const float3 start = params.extract_input<float3>("Start Location"); + if (mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) { + /* The label switches to "End Location", but the same socket is used. */ + const float3 end = params.extract_input<float3>("Offset"); + const float3 total_delta = end - start; + + if (count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.extract_input<float>("Resolution"), 0.0001f); + const int count = total_delta.length() / resolution + 1; + const float3 delta = total_delta.normalized() * resolution; + mesh = create_line_mesh(start, delta, count); + } + else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) { + const int count = params.extract_input<int>("Count"); + if (count > 1) { + const float3 delta = total_delta / (float)(count - 1); + mesh = create_line_mesh(start, delta, count); + } + } + } + else if (mode == GEO_NODE_MESH_LINE_MODE_OFFSET) { + const float3 delta = params.extract_input<float3>("Offset"); + const int count = params.extract_input<int>("Count"); + mesh = create_line_mesh(start, delta, count); + } + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_line() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Line", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_line_in, geo_node_mesh_primitive_line_out); + node_type_init(&ntype, geo_node_mesh_primitive_line_init); + node_type_update(&ntype, geo_node_mesh_primitive_line_update); + node_type_storage( + &ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_line_exec; + ntype.draw_buttons = geo_node_mesh_primitive_line_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc new file mode 100644 index 00000000000..adb1d4a9eb4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc @@ -0,0 +1,183 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_map.hh" +#include "BLI_math_matrix.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_plane_in[] = { + {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_INT, N_("Vertices X"), 10, 0.0f, 0.0f, 0.0f, 2, 1000}, + {SOCK_INT, N_("Vertices Y"), 10, 0.0f, 0.0f, 0.0f, 2, 1000}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_plane_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void calculate_uvs(Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( + "uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); + MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + + for (const int i : loops.index_range()) { + const float3 &co = verts[loops[i].v].co; + uvs[i].x = (co.x + size) / (size * 2.0f); + uvs[i].y = (co.y + size) / (size * 2.0f); + } + + uv_attribute.apply_span_and_save(); +} + +static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float size) +{ + const int edges_x = verts_x - 1; + const int edges_y = verts_y - 1; + Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y, + edges_x * verts_y + edges_y * verts_x, + 0, + edges_x * edges_y * 4, + edges_x * edges_y); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + { + const float dx = size * 2.0f / edges_x; + const float dy = size * 2.0f / edges_y; + float x = -size; + for (const int x_index : IndexRange(verts_x)) { + float y = -size; + for (const int y_index : IndexRange(verts_y)) { + const int vert_index = x_index * verts_y + y_index; + verts[vert_index].co[0] = x; + verts[vert_index].co[1] = y; + verts[vert_index].co[2] = 0.0f; + y += dy; + } + x += dx; + } + } + + /* Point all vertex normals in the up direction. */ + const short up_normal[3] = {0, 0, SHRT_MAX}; + for (MVert &vert : verts) { + copy_v3_v3_short(vert.no, up_normal); + } + + /* Build the horizontal edges in the X direction. */ + const int y_edges_start = 0; + int edge_index = 0; + for (const int x : IndexRange(verts_x)) { + for (const int y : IndexRange(edges_y)) { + const int vert_index = x * verts_y + y; + MEdge &edge = edges[edge_index++]; + edge.v1 = vert_index; + edge.v2 = vert_index + 1; + } + } + + /* Build the vertical edges in the Y direction. */ + const int x_edges_start = edge_index; + for (const int y : IndexRange(verts_y)) { + for (const int x : IndexRange(edges_x)) { + const int vert_index = x * verts_y + y; + MEdge &edge = edges[edge_index++]; + edge.v1 = vert_index; + edge.v2 = vert_index + verts_y; + } + } + + int loop_index = 0; + int poly_index = 0; + for (const int x : IndexRange(edges_x)) { + for (const int y : IndexRange(edges_y)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 4; + const int vert_index = x * verts_y + y; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = vert_index; + loop_a.e = x_edges_start + edges_x * y + x; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = vert_index + verts_y; + loop_b.e = y_edges_start + edges_y * (x + 1) + y; + MLoop &loop_c = loops[loop_index++]; + loop_c.v = vert_index + verts_y + 1; + loop_c.e = x_edges_start + edges_x * (y + 1) + x; + MLoop &loop_d = loops[loop_index++]; + loop_d.v = vert_index + 1; + loop_d.e = y_edges_start + edges_y * x + y; + } + } + + calculate_uvs(mesh, verts, loops, size); + + return mesh; +} + +static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params) +{ + const float size = params.extract_input<float>("Size"); + const int verts_x = params.extract_input<int>("Vertices X"); + const int verts_y = params.extract_input<int>("Vertices Y"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + if (verts_x < 2 || verts_y < 2) { + params.set_output("Geometry", GeometrySet()); + return; + } + + Mesh *mesh = create_plane_mesh(verts_x, verts_y, size); + BLI_assert(BKE_mesh_is_valid(mesh)); + + transform_mesh(mesh, location, rotation, float3(1)); + + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_plane() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_PLANE, "Plane", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_plane_in, geo_node_mesh_primitive_plane_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_plane_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc new file mode 100644 index 00000000000..e164ad247a3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -0,0 +1,131 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = { + {SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024}, + {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024}, + {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static int sphere_vert_total(const int segments, const int rings) +{ + return segments * (rings - 1) + 2; +} + +static int sphere_edge_total(const int segments, const int rings) +{ + return segments * (rings * 2 - 1); +} + +static int sphere_corner_total(const int segments, const int rings) +{ + const int quad_corners = 4 * segments * (rings - 2); + const int tri_corners = 3 * segments * 2; + return quad_corners + tri_corners; +} + +static int sphere_face_total(const int segments, const int rings) +{ + const int quads = segments * (rings - 2); + const int triangles = segments * 2; + return quads + triangles; +} + +static Mesh *create_uv_sphere_mesh_bmesh(const float3 location, + const float3 rotation, + const float radius, + const int segments, + const int rings) +{ + float4x4 transform; + loc_eul_size_to_mat4(transform.values, location, rotation, float3(radius)); + + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings), + sphere_edge_total(segments, rings), + sphere_corner_total(segments, rings), + sphere_face_total(segments, rings)}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b", + segments, + rings, + radius, + transform.values, + true); + + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr); + BM_mesh_free(bm); + + return mesh; +} + +static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) +{ + const int segments_num = params.extract_input<int>("Segments"); + const int rings_num = params.extract_input<int>("Rings"); + if (segments_num < 3 || rings_num < 3) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const float radius = params.extract_input<float>("Radius"); + const float3 location = params.extract_input<float3>("Location"); + const float3 rotation = params.extract_input<float3>("Rotation"); + + Mesh *mesh = create_uv_sphere_mesh_bmesh(location, rotation, radius, segments_num, rings_num); + params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_primitive_uv_sphere() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_mesh_primitive_uv_sphere_in, geo_node_mesh_primitive_uv_sphere_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_uv_sphere_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 1bc280c4bb7..4795970377a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -92,18 +92,17 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) return {looptris, looptris_len}; } -static int sample_mesh_surface(const Mesh &mesh, - const float4x4 &transform, - const float base_density, - const FloatReadAttribute *density_factors, - const int seed, - Vector<float3> &r_positions, - Vector<float3> &r_bary_coords, - Vector<int> &r_looptri_indices) +static void sample_mesh_surface(const Mesh &mesh, + const float4x4 &transform, + const float base_density, + const FloatReadAttribute *density_factors, + const int seed, + Vector<float3> &r_positions, + Vector<float3> &r_bary_coords, + Vector<int> &r_looptri_indices) { Span<MLoopTri> looptris = get_mesh_looptris(mesh); - int points_len = 0; for (const int looptri_index : looptris.index_range()) { const MLoopTri &looptri = looptris[looptri_index]; const int v0_loop = looptri.tri[0]; @@ -140,20 +139,17 @@ static int sample_mesh_surface(const Mesh &mesh, r_positions.append(point_pos); r_bary_coords.append(bary_coord); r_looptri_indices.append(looptri_index); - points_len++; } } - return points_len; } -BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_array, +BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_all, const int initial_points_len) { KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len); int i_point = 0; - for (const int i_instance : positions_array.index_range()) { - Span<float3> positions = positions_array[i_instance]; + for (const Vector<float3> &positions : positions_all) { for (const float3 position : positions) { BLI_kdtree_3d_insert(kdtree, i_point, position); i_point++; @@ -164,7 +160,8 @@ BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_array } BLI_NOINLINE static void update_elimination_mask_for_close_points( - Span<Vector<float3>> positions_array, + Span<Vector<float3>> positions_all, + Span<int> instance_start_offsets, const float minimum_distance, MutableSpan<bool> elimination_mask, const int initial_points_len) @@ -173,26 +170,27 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( return; } - KDTree_3d *kdtree = build_kdtree(positions_array, initial_points_len); + KDTree_3d *kdtree = build_kdtree(positions_all, initial_points_len); /* The elimination mask is a flattened array for every point, * so keep track of the index to it separately. */ - int i_point = 0; - for (Span<float3> positions : positions_array) { - for (const float3 position : positions) { - if (elimination_mask[i_point]) { - i_point++; + for (const int i_instance : positions_all.index_range()) { + Span<float3> positions = positions_all[i_instance]; + const int offset = instance_start_offsets[i_instance]; + + for (const int i : positions.index_range()) { + if (elimination_mask[offset + i]) { continue; } struct CallbackData { int index; MutableSpan<bool> elimination_mask; - } callback_data = {i_point, elimination_mask}; + } callback_data = {offset + i, elimination_mask}; BLI_kdtree_3d_range_search_cb( kdtree, - position, + positions[i], minimum_distance, [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { CallbackData &callback_data = *static_cast<CallbackData *>(user_data); @@ -202,8 +200,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( return true; }, &callback_data); - - i_point++; } } BLI_kdtree_3d_free(kdtree); @@ -233,8 +229,8 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); - const float probablity = attribute_math::mix3<float>( - bary_coord, v0_density_factor, v1_density_factor, v2_density_factor); + const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + + v2_density_factor * bary_coord.z; const float hash = BLI_hash_int_01(bary_coord.hash()); if (hash > probablity) { @@ -314,6 +310,23 @@ BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh, } template<typename T> +BLI_NOINLINE static void interpolate_attribute_polygon(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<T> data_in, + MutableSpan<T> data_out) +{ + BLI_assert(data_in.size() == mesh.totpoly); + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : data_out.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const int poly_index = looptri.poly; + data_out[i] = data_in[poly_index]; + } +} + +template<typename T> BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, Span<float3> bary_coords, Span<int> looptri_indices, @@ -331,6 +344,10 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, mesh, bary_coords, looptri_indices, source_span, output_span); break; } + case ATTR_DOMAIN_POLYGON: { + interpolate_attribute_polygon<T>(mesh, looptri_indices, source_span, output_span); + break; + } default: { /* Not supported currently. */ return; @@ -339,9 +356,9 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, } BLI_NOINLINE static void interpolate_existing_attributes( - Span<GeometryInstanceGroup> sets, - Span<int> group_start_indices, - Map<std::string, AttributeKind> &attributes, + Span<GeometryInstanceGroup> set_groups, + Span<int> instance_start_offsets, + const Map<std::string, AttributeKind> &attributes, GeometryComponent &component, Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) @@ -352,70 +369,64 @@ BLI_NOINLINE static void interpolate_existing_attributes( /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ OutputAttributePtr attribute_out = component.attribute_try_get_for_output( attribute_name, ATTR_DOMAIN_POINT, output_data_type); - BLI_assert(attribute_out); if (!attribute_out) { continue; } - attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { - using T = decltype(dummy); + fn::GMutableSpan out_span = attribute_out->get_span_for_write_only(); - MutableSpan<T> out_span = attribute_out->get_span_for_write_only<T>(); - - int i_set_with_mesh = 0; - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : sets) { - const GeometrySet &set = set_group.geometry_set; - if (!set.has_mesh()) { - continue; - } - const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *source_component.get_for_read(); - - /* Use a dummy read without specifying a domain or data type in order to - * get the existing attribute's domain. Interpolation is done manually based - * on the bary coords in #interpolate_attribute. */ - ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read( - attribute_name); - if (!dummy_attribute) { - i_instance += set_group.transforms.size(); - i_set_with_mesh++; - continue; - } + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *source_component.get_for_read(); + + /* Use a dummy read without specifying a domain or data type in order to + * get the existing attribute's domain. Interpolation is done manually based + * on the bary coords in #interpolate_attribute. */ + ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read( + attribute_name); + if (!dummy_attribute) { + i_instance += set_group.transforms.size(); + continue; + } - const AttributeDomain source_domain = dummy_attribute->domain(); - ReadAttributePtr source_attribute = source_component.attribute_get_for_read( - attribute_name, source_domain, output_data_type, nullptr); - BLI_assert(source_attribute); - Span<T> source_span = source_attribute->get_span<T>(); + const AttributeDomain source_domain = dummy_attribute->domain(); + ReadAttributePtr source_attribute = source_component.attribute_get_for_read( + attribute_name, source_domain, output_data_type, nullptr); + if (!source_attribute) { + i_instance += set_group.transforms.size(); + continue; + } + fn::GSpan source_span = source_attribute->get_span(); - if (!source_attribute) { - i_instance += set_group.transforms.size(); - i_set_with_mesh++; - continue; - } + attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { + using T = decltype(dummy); - int i_point = group_start_indices[i_set_with_mesh]; for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - Span<float3> bary_coords = bary_coords_array[i_instance].as_span(); - Span<int> looptri_indices = looptri_indices_array[i_instance].as_span(); + const int offset = instance_start_offsets[i_instance]; + Span<float3> bary_coords = bary_coords_array[i_instance]; + Span<int> looptri_indices = looptri_indices_array[i_instance]; + + MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size()); + interpolate_attribute<T>(mesh, + bary_coords, + looptri_indices, + source_domain, + source_span.typed<T>(), + instance_span); - MutableSpan<T> instance_span = out_span.slice(i_point, bary_coords.size()); - interpolate_attribute<T>( - mesh, bary_coords, looptri_indices, source_domain, source_span, instance_span); - - i_point += bary_coords.size(); i_instance++; } - i_set_with_mesh++; - } - }); + }); + } attribute_out.apply_span_and_save(); } } BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> sets, + Span<int> instance_start_offsets, GeometryComponent &component, Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) @@ -427,28 +438,29 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - MutableSpan<int> ids_full = id_attribute->get_span_for_write_only<int>(); - MutableSpan<float3> normals_full = normal_attribute->get_span_for_write_only<float3>(); - MutableSpan<float3> rotations_full = rotation_attribute->get_span_for_write_only<float3>(); + MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>(); + MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>(); + MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>(); - int i_point = 0; int i_instance = 0; for (const GeometryInstanceGroup &set_group : sets) { const GeometrySet &set = set_group.geometry_set; - if (!set.has_mesh()) { - continue; - } - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *component.get_for_read(); Span<MLoopTri> looptris = get_mesh_looptris(mesh); - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - Span<float3> bary_coords = bary_coords_array[i_instance].as_span(); - Span<int> looptri_indices = looptri_indices_array[i_instance].as_span(); - MutableSpan<int> ids = ids_full.slice(i_point, bary_coords.size()); - MutableSpan<float3> normals = normals_full.slice(i_point, bary_coords.size()); - MutableSpan<float3> rotations = rotations_full.slice(i_point, bary_coords.size()); + for (const float4x4 &transform : set_group.transforms) { + const int offset = instance_start_offsets[i_instance]; + + Span<float3> bary_coords = bary_coords_array[i_instance]; + Span<int> looptri_indices = looptri_indices_array[i_instance]; + MutableSpan<int> ids = result_ids.slice(offset, bary_coords.size()); + MutableSpan<float3> normals = result_normals.slice(offset, bary_coords.size()); + MutableSpan<float3> rotations = result_rotations.slice(offset, bary_coords.size()); + + /* Use one matrix multiplication per point instead of three (for each triangle corner). */ + float rotation_matrix[3][3]; + mat4_to_rot(rotation_matrix, transform.values); for (const int i : bary_coords.index_range()) { const int looptri_index = looptri_indices[i]; @@ -458,17 +470,17 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> const int v0_index = mesh.mloop[looptri.tri[0]].v; const int v1_index = mesh.mloop[looptri.tri[1]].v; const int v2_index = mesh.mloop[looptri.tri[2]].v; - const float3 v0_pos = mesh.mvert[v0_index].co; - const float3 v1_pos = mesh.mvert[v1_index].co; - const float3 v2_pos = mesh.mvert[v2_index].co; + const float3 v0_pos = float3(mesh.mvert[v0_index].co); + const float3 v1_pos = float3(mesh.mvert[v1_index].co); + const float3 v2_pos = float3(mesh.mvert[v2_index].co); - ids[i] = (int)(bary_coord.hash()) + (uint64_t)looptri_index; + ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index); normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos); + mul_m3_v3(rotation_matrix, normals[i]); rotations[i] = normal_to_euler_rotation(normals[i]); } i_instance++; - i_point += bary_coords.size(); } } @@ -478,172 +490,239 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> } BLI_NOINLINE static void add_remaining_point_attributes( - Span<GeometryInstanceGroup> sets, - Span<int> group_start_indices, - Map<std::string, AttributeKind> &attributes, + Span<GeometryInstanceGroup> set_groups, + Span<int> instance_start_offsets, + const Map<std::string, AttributeKind> &attributes, GeometryComponent &component, Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) { - interpolate_existing_attributes( - sets, group_start_indices, attributes, component, bary_coords_array, looptri_indices_array); - compute_special_attributes(sets, component, bary_coords_array, looptri_indices_array); + interpolate_existing_attributes(set_groups, + instance_start_offsets, + attributes, + component, + bary_coords_array, + looptri_indices_array); + compute_special_attributes( + set_groups, instance_start_offsets, component, bary_coords_array, looptri_indices_array); +} + +static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, + const StringRef density_attribute_name, + const float density, + const int seed, + MutableSpan<Vector<float3>> positions_all, + MutableSpan<Vector<float3>> bary_coords_all, + MutableSpan<Vector<int>> looptri_indices_all) +{ + /* If there is an attribute name, the default value for the densities should be zero so that + * points are only scattered where the attribute exists. Otherwise, just "ignore" the density + * factors. */ + const bool use_one_default = density_attribute_name.is_empty(); + + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); + const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( + density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); + const Mesh &mesh = *component.get_for_read(); + for (const float4x4 &transform : set_group.transforms) { + Vector<float3> &positions = positions_all[i_instance]; + Vector<float3> &bary_coords = bary_coords_all[i_instance]; + Vector<int> &looptri_indices = looptri_indices_all[i_instance]; + sample_mesh_surface(mesh, + transform, + density, + &density_factors, + seed, + positions, + bary_coords, + looptri_indices); + i_instance++; + } + } +} + +static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups, + const StringRef density_attribute_name, + const float density, + const int seed, + const float minimum_distance, + MutableSpan<Vector<float3>> positions_all, + MutableSpan<Vector<float3>> bary_coords_all, + MutableSpan<Vector<int>> looptri_indices_all) +{ + Array<int> instance_start_offsets(positions_all.size()); + int initial_points_len = 0; + int i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *component.get_for_read(); + for (const float4x4 &transform : set_group.transforms) { + Vector<float3> &positions = positions_all[i_instance]; + Vector<float3> &bary_coords = bary_coords_all[i_instance]; + Vector<int> &looptri_indices = looptri_indices_all[i_instance]; + sample_mesh_surface( + mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices); + + instance_start_offsets[i_instance] = initial_points_len; + initial_points_len += positions.size(); + i_instance++; + } + } + + /* If there is an attribute name, the default value for the densities should be zero so that + * points are only scattered where the attribute exists. Otherwise, just "ignore" the density + * factors. */ + const bool use_one_default = density_attribute_name.is_empty(); + + /* Unlike the other result arrays, the elimination mask in stored as a flat array for every + * point, in order to simplify culling points from the KDTree (which needs to know about all + * points at once). */ + Array<bool> elimination_mask(initial_points_len, false); + update_elimination_mask_for_close_points(positions_all, + instance_start_offsets, + minimum_distance, + elimination_mask, + initial_points_len); + + i_instance = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *component.get_for_read(); + const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( + density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); + + for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + Vector<float3> &positions = positions_all[i_instance]; + Vector<float3> &bary_coords = bary_coords_all[i_instance]; + Vector<int> &looptri_indices = looptri_indices_all[i_instance]; + + const int offset = instance_start_offsets[i_instance]; + update_elimination_mask_based_on_density_factors( + mesh, + density_factors, + bary_coords, + looptri_indices, + elimination_mask.as_mutable_span().slice(offset, positions.size())); + + eliminate_points_based_on_mask(elimination_mask.as_span().slice(offset, positions.size()), + positions, + bary_coords, + looptri_indices); + + i_instance++; + } + } } static void geo_node_point_distribute_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; const GeometryNodePointDistributeMode distribute_method = static_cast<GeometryNodePointDistributeMode>(params.node().custom1); - if (!geometry_set.has_mesh() && !geometry_set.has_instances()) { - params.error_message_add(NodeWarningType::Error, "Geometry must contain a mesh."); - params.set_output("Geometry", std::move(geometry_set_out)); - return; - } - + const int seed = params.get_input<int>("Seed"); const float density = params.extract_input<float>("Density Max"); const std::string density_attribute_name = params.extract_input<std::string>( "Density Attribute"); if (density <= 0.0f) { - params.set_output("Geometry", std::move(geometry_set_out)); + params.set_output("Geometry", GeometrySet()); return; } - const int seed = params.get_input<int>("Seed"); - - Vector<GeometryInstanceGroup> sets = bke::geometry_set_gather_instances(geometry_set); - int instances_len = 0; - for (GeometryInstanceGroup set_group : sets) { - instances_len += set_group.transforms.size(); + Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set); + if (set_groups.is_empty()) { + params.set_output("Geometry", GeometrySet()); + return; } - /* Store data per-instance in order to simplify attribute access after the scattering, - * and to make the point elimination simpler for the poisson disk mode. Node that some - * vectors will be empty if any instances don't contain mesh data. */ - Array<Vector<float3>> positions_array(instances_len); - Array<Vector<float3>> bary_coords_array(instances_len); - Array<Vector<int>> looptri_indices_array(instances_len); - - int initial_points_len = 0; - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : sets) { - const GeometrySet &set = set_group.geometry_set; + /* Remove any set inputs that don't contain a mesh, to avoid checking later on. */ + for (int i = set_groups.size() - 1; i >= 0; i--) { + const GeometrySet &set = set_groups[i].geometry_set; if (!set.has_mesh()) { - continue; + set_groups.remove_and_reorder(i); } + } - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *component.get_for_read(); - for (const float4x4 &transform : set_group.transforms) { - Vector<float3> &positions = positions_array[i_instance]; - Vector<float3> &bary_coords = bary_coords_array[i_instance]; - Vector<int> &looptri_indices = looptri_indices_array[i_instance]; - - switch (distribute_method) { - case GEO_NODE_POINT_DISTRIBUTE_RANDOM: { - const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( - density_attribute_name, ATTR_DOMAIN_CORNER, 1.0f); - initial_points_len += sample_mesh_surface(mesh, - transform, - density, - &density_factors, - seed, - positions, - bary_coords, - looptri_indices); - break; - } - case GEO_NODE_POINT_DISTRIBUTE_POISSON: - initial_points_len += sample_mesh_surface( - mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices); - break; - } - i_instance++; - } + if (set_groups.is_empty()) { + params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh")); + params.set_output("Geometry", GeometrySet()); + return; } - if (distribute_method == GEO_NODE_POINT_DISTRIBUTE_POISSON) { - /* Unlike the other result arrays, the elimination mask in stored as a flat array for every - * point, in order to simplify culling points from the KDTree (which needs to know about all - * points at once). */ - Array<bool> elimination_mask(initial_points_len, false); - const float minimum_distance = params.get_input<float>("Distance Min"); - update_elimination_mask_for_close_points( - positions_array, minimum_distance, elimination_mask, initial_points_len); - - int i_point = 0; - i_instance = 0; - for (const GeometryInstanceGroup &set_group : sets) { - const GeometrySet &set = set_group.geometry_set; - if (!set.has_mesh()) { - continue; - } + int instances_len = 0; + for (GeometryInstanceGroup &set_group : set_groups) { + instances_len += set_group.transforms.size(); + } - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *component.get_for_read(); - const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( - density_attribute_name, ATTR_DOMAIN_CORNER, 1.0f); - - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - Vector<float3> &positions = positions_array[i_instance]; - Vector<float3> &bary_coords = bary_coords_array[i_instance]; - Vector<int> &looptri_indices = looptri_indices_array[i_instance]; - - update_elimination_mask_based_on_density_factors( - mesh, - density_factors, - bary_coords, - looptri_indices, - elimination_mask.as_mutable_span().slice(i_point, positions.size())); - - /* The positions vector's size is changed, temporarily store the - * original size to properly advance the elimination mask index. */ - const int initial_positions_size = positions.size(); - eliminate_points_based_on_mask(elimination_mask.as_span().slice(i_point, positions.size()), - positions, - bary_coords, - looptri_indices); - - i_point += initial_positions_size; - i_instance++; - } + /* Store data per-instance in order to simplify attribute access after the scattering, + * and to make the point elimination simpler for the poisson disk mode. Note that some + * vectors will be empty if any instances don't contain mesh data. */ + Array<Vector<float3>> positions_all(instances_len); + Array<Vector<float3>> bary_coords_all(instances_len); + Array<Vector<int>> looptri_indices_all(instances_len); + + switch (distribute_method) { + case GEO_NODE_POINT_DISTRIBUTE_RANDOM: { + distribute_points_random(set_groups, + density_attribute_name, + density, + seed, + positions_all, + bary_coords_all, + looptri_indices_all); + break; + } + case GEO_NODE_POINT_DISTRIBUTE_POISSON: { + const float minimum_distance = params.extract_input<float>("Distance Min"); + distribute_points_poisson_disk(set_groups, + density_attribute_name, + density, + seed, + minimum_distance, + positions_all, + bary_coords_all, + looptri_indices_all); + break; } } int final_points_len = 0; - Array<int> group_start_indices(sets.size()); - for (const int i : positions_array.index_range()) { - Vector<float3> &positions = positions_array[i]; - group_start_indices[i] = final_points_len; + Array<int> instance_start_offsets(set_groups.size()); + for (const int i : positions_all.index_range()) { + Vector<float3> &positions = positions_all[i]; + instance_start_offsets[i] = final_points_len; final_points_len += positions.size(); } PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); - int i_point = 0; - for (Vector<float3> &positions : positions_array) { - memcpy(pointcloud->co + i_point, positions.data(), sizeof(float3) * positions.size()); - i_point += positions.size(); + for (const int instance_index : positions_all.index_range()) { + const int offset = instance_start_offsets[instance_index]; + Span<float3> positions = positions_all[instance_index]; + memcpy(pointcloud->co + offset, positions.data(), sizeof(float3) * positions.size()); } - MutableSpan(pointcloud->radius, pointcloud->totpoint).fill(0.05f); + uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + GeometrySet geometry_set_out = GeometrySet::create_with_pointcloud(pointcloud); PointCloudComponent &point_component = geometry_set_out.get_component_for_write<PointCloudComponent>(); - point_component.replace(pointcloud); Map<std::string, AttributeKind> attributes; bke::gather_attribute_info( - attributes, {GeometryComponentType::Mesh}, sets, {"position", "normal", "id"}); - add_remaining_point_attributes(sets, - group_start_indices, + attributes, {GEO_COMPONENT_TYPE_MESH}, set_groups, {"position", "normal", "id"}); + add_remaining_point_attributes(set_groups, + instance_start_offsets, attributes, point_component, - bary_coords_array, - looptri_indices_array); + bary_coords_all, + looptri_indices_all); params.set_output("Geometry", std::move(geometry_set_out)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 669b5ee4614..dbbb73bd36d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -103,7 +103,7 @@ static void get_instanced_data__collection( if (BLI_listbase_is_empty(&collection->children) && BLI_listbase_is_empty(&collection->gobject)) { - params.error_message_add(NodeWarningType::Info, "Collection is empty."); + params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty")); return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc index cb3cd012c77..015f4cd38e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -46,6 +46,9 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co { OutputAttributePtr position_attribute = component.attribute_try_get_for_output( "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + if (!position_attribute) { + return; + } ReadAttributePtr attribute = params.get_input_attribute( "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr); if (!attribute) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface_simple.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc index 2a0cb727cd6..06c5586a3ff 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface_simple.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc @@ -25,20 +25,20 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_subdivision_surface_simple_in[] = { +static bNodeSocketTemplate geo_node_subdivide_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6}, {-1, ""}, }; -static bNodeSocketTemplate geo_node_subdivision_surface_simple_out[] = { +static bNodeSocketTemplate geo_node_subdivide_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, {-1, ""}, }; namespace blender::nodes { -static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params) +static void geo_node_subdivide_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -49,7 +49,7 @@ static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params) #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, - "Disabled, Blender was built without OpenSubdiv"); + TIP_("Disabled, Blender was built without OpenSubdiv")); params.set_output("Geometry", std::move(geometry_set)); return; #endif @@ -91,7 +91,8 @@ static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params) Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); BKE_mesh_calc_normals(mesh_out); - geometry_set.replace_mesh(mesh_out); + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out); BKE_subdiv_free(subdiv); @@ -99,17 +100,12 @@ static void geo_node_subdivision_surface_simple_exec(GeoNodeExecParams params) } } // namespace blender::nodes -void register_node_type_geo_subdivision_surface_simple() +void register_node_type_geo_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, - GEO_NODE_SUBDIVISION_SURFACE_SIMPLE, - "Simple Subdivision Surface", - NODE_CLASS_GEOMETRY, - 0); - node_type_socket_templates( - &ntype, geo_node_subdivision_surface_simple_in, geo_node_subdivision_surface_simple_out); - ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_simple_exec; + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE, "Subdivide", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_subdivide_in, geo_node_subdivide_out); + ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 47304a0de68..b14a512ce28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -69,7 +69,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) #else const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); - /* Only process subdivion if level is greater than 0. */ + /* Only process subdivision if level is greater than 0. */ if (subdiv_level == 0) { params.set_output("Geometry", std::move(geometry_set)); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 539a7551be9..ef6f0be40f2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -57,14 +57,16 @@ static bool use_translate(const float3 rotation, const float3 scale) return true; } -static void transform_mesh(Mesh *mesh, - const float3 translation, - const float3 rotation, - const float3 scale) +void transform_mesh(Mesh *mesh, + const float3 translation, + const float3 rotation, + const float3 scale) { /* Use only translation if rotation and scale are zero. */ if (use_translate(rotation, scale)) { - BKE_mesh_translate(mesh, translation, true); + if (!translation.is_zero()) { + BKE_mesh_translate(mesh, translation, true); + } } else { float mat[4][4]; |