Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_mesh_bisect.cc
- This file was added.
| /* | |||||
| * 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_geom.h" | |||||
| #include "BKE_mesh.h" | |||||
| #include "BKE_mesh_runtime.h" | |||||
| #include "bmesh.h" | |||||
| #include "bmesh_tools.h" | |||||
| #include "UI_interface.h" | |||||
| #include "UI_resources.h" | |||||
| #include "node_geometry_util.hh" | |||||
| namespace blender::nodes { | |||||
| struct BisectMeshAttributeOutputs { | |||||
| StrongAnonymousAttributeID positive_side_id; | |||||
| StrongAnonymousAttributeID negative_side_id; | |||||
| StrongAnonymousAttributeID cut_edges_id; | |||||
| }; | |||||
| static void geo_node_mesh_bisect_declare(NodeDeclarationBuilder &b) | |||||
| { | |||||
| b.add_input<decl::Geometry>(N_("Mesh")) | |||||
| .only_realized_data() | |||||
| .supported_type(GEO_COMPONENT_TYPE_MESH); | |||||
| b.add_input<decl::Vector>(N_("Offset")) | |||||
| .default_value({0.0f, 0.0f, 0.0f}) | |||||
| .subtype(PROP_TRANSLATION) | |||||
| .description(N_("Offset vector to the center of the bisect plane")); | |||||
| b.add_input<decl::Vector>(N_("Normal")).default_value({0.0f, 0.0f, 1.0f}); | |||||
| b.add_input<decl::Float>(N_("Epsilon")) | |||||
| .default_value(0.0001f) | |||||
| .min(0.000001f) | |||||
| .description(N_("Epsilon used to test points for plane intersection")); | |||||
| b.add_output<decl::Geometry>(N_("Bisect Mesh")); | |||||
| b.add_output<decl::Bool>(N_("Positive Side")).field_source(); | |||||
| b.add_output<decl::Bool>(N_("Negative Side")).field_source(); | |||||
| b.add_output<decl::Bool>(N_("Cut Edges")).field_source(); | |||||
| } | |||||
| static Mesh *mesh_bisect(const Mesh &mesh_in, | |||||
| MeshComponent &mesh_component, | |||||
| const float3 offset, | |||||
| const float3 normal, | |||||
| const float bisect_distance, | |||||
| BisectMeshAttributeOutputs &attribute_outputs) | |||||
| { | |||||
| BMeshCreateParams bmesh_create_params{}; | |||||
| bmesh_create_params.use_toolflags = true; | |||||
| const BMAllocTemplate allocsize = {0, 0, 0, 0}; | |||||
| BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params); | |||||
| BMeshFromMeshParams bmesh_from_mesh_params{}; | |||||
| BM_mesh_bm_from_me(bm, &mesh_in, &bmesh_from_mesh_params); | |||||
| float plane[4]; | |||||
| plane_from_point_normal_v3(plane, offset, normal); | |||||
| BM_mesh_bisect_plane(bm, plane, false, false, 0, 0, bisect_distance); | |||||
| Mesh *mesh_out = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, &mesh_in); | |||||
| mesh_component.replace(mesh_out, GeometryOwnershipType::Editable); | |||||
| OutputAttribute_Typed<bool> attribute_positive_side; | |||||
| OutputAttribute_Typed<bool> attribute_negative_side; | |||||
| OutputAttribute_Typed<bool> attribute_cut_edges; | |||||
| MutableSpan<bool> selection_positive_side; | |||||
| MutableSpan<bool> selection_negative_side; | |||||
| MutableSpan<bool> selection_cut_edges; | |||||
| if (attribute_outputs.positive_side_id) { | |||||
| attribute_positive_side = mesh_component.attribute_try_get_for_output_only<bool>( | |||||
| attribute_outputs.positive_side_id.get(), ATTR_DOMAIN_FACE); | |||||
| selection_positive_side = attribute_positive_side.as_span(); | |||||
| selection_positive_side.fill(false); | |||||
| } | |||||
| if (attribute_outputs.negative_side_id) { | |||||
| attribute_negative_side = mesh_component.attribute_try_get_for_output_only<bool>( | |||||
| attribute_outputs.negative_side_id.get(), ATTR_DOMAIN_FACE); | |||||
| selection_negative_side = attribute_negative_side.as_span(); | |||||
| selection_negative_side.fill(false); | |||||
| } | |||||
| if (attribute_outputs.cut_edges_id) { | |||||
| attribute_cut_edges = mesh_component.attribute_try_get_for_output_only<bool>( | |||||
| attribute_outputs.cut_edges_id.get(), ATTR_DOMAIN_EDGE); | |||||
| selection_cut_edges = attribute_cut_edges.as_span(); | |||||
| selection_cut_edges.fill(false); | |||||
| } | |||||
| float plane_positive[4], plane_negative[4]; | |||||
| copy_v3_v3(plane_positive, plane); | |||||
| copy_v3_v3(plane_negative, plane); | |||||
| plane_positive[3] = plane[3] - bisect_distance; | |||||
| plane_negative[3] = plane[3] + bisect_distance; | |||||
| if (attribute_outputs.positive_side_id || attribute_outputs.negative_side_id) { | |||||
| BMIter fiter; | |||||
| BMVert *v; | |||||
| BMFace *f; | |||||
| int i = 0; | |||||
| BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { | |||||
| BMIter viter; | |||||
| BM_ITER_ELEM (v, &viter, f, BM_VERTS_OF_FACE) { | |||||
| if (attribute_outputs.positive_side_id && | |||||
| plane_point_side_v3(plane_positive, v->co) > 0.0f) { | |||||
| selection_positive_side[i] = true; | |||||
| break; | |||||
| } | |||||
| if (attribute_outputs.negative_side_id && | |||||
| plane_point_side_v3(plane_negative, v->co) < 0.0f) { | |||||
| selection_negative_side[i] = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| i++; | |||||
| } | |||||
| if (attribute_outputs.positive_side_id) { | |||||
| attribute_positive_side.save(); | |||||
| } | |||||
| if (attribute_outputs.negative_side_id) { | |||||
| attribute_negative_side.save(); | |||||
| } | |||||
| } | |||||
| if (attribute_outputs.cut_edges_id) { | |||||
| BMIter iter; | |||||
| BMEdge *e; | |||||
| int i = 0; | |||||
| BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { | |||||
| if (abs(dot_v3v3(e->v1->co, plane) + plane[3]) <= bisect_distance && | |||||
| abs(dot_v3v3(e->v2->co, plane) + plane[3]) <= bisect_distance) { | |||||
| selection_cut_edges[i] = true; | |||||
| } | |||||
| i++; | |||||
| } | |||||
| attribute_cut_edges.save(); | |||||
| } | |||||
| BKE_mesh_normals_tag_dirty(mesh_out); | |||||
| BM_mesh_free(bm); | |||||
| return mesh_out; | |||||
| } | |||||
| static void geo_node_mesh_bisect_exec(GeoNodeExecParams params) | |||||
| { | |||||
| GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); | |||||
| float3 offset = params.extract_input<float3>("Offset"); | |||||
| float3 normal = params.extract_input<float3>("Normal"); | |||||
| float bisect_distance = params.extract_input<float>("Epsilon"); | |||||
| BisectMeshAttributeOutputs attribute_outputs; | |||||
| if (params.output_is_required("Positive Side")) { | |||||
| attribute_outputs.positive_side_id = StrongAnonymousAttributeID("positive_side"); | |||||
| } | |||||
| if (params.output_is_required("Negative Side")) { | |||||
| attribute_outputs.negative_side_id = StrongAnonymousAttributeID("negative_side"); | |||||
| } | |||||
| if (params.output_is_required("Cut Edges")) { | |||||
| attribute_outputs.cut_edges_id = StrongAnonymousAttributeID("cut_edges"); | |||||
| } | |||||
| geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { | |||||
| if (!geometry_set.has_mesh()) { | |||||
| return; | |||||
| } | |||||
| geometry_set.replace_mesh(mesh_bisect(*geometry_set.get_mesh_for_read(), | |||||
| geometry_set.get_component_for_write<MeshComponent>(), | |||||
| offset, | |||||
| normal, | |||||
| bisect_distance, | |||||
| attribute_outputs)); | |||||
| }); | |||||
| if (attribute_outputs.positive_side_id) { | |||||
| params.set_output( | |||||
| "Positive Side", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.positive_side_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| if (attribute_outputs.negative_side_id) { | |||||
| params.set_output( | |||||
| "Negative Side", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.negative_side_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| if (attribute_outputs.cut_edges_id) { | |||||
| params.set_output( | |||||
| "Cut Edges", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.cut_edges_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| params.set_output("Bisect Mesh", std::move(geometry_set)); | |||||
| } | |||||
| } // namespace blender::nodes | |||||
| void register_node_type_geo_mesh_bisect() | |||||
| { | |||||
| static bNodeType ntype; | |||||
| geo_node_type_base(&ntype, GEO_NODE_MESH_BISECT, "Bisect Mesh", NODE_CLASS_GEOMETRY, 0); | |||||
| ntype.declare = blender::nodes::geo_node_mesh_bisect_declare; | |||||
| ntype.geometry_node_execute = blender::nodes::geo_node_mesh_bisect_exec; | |||||
| nodeRegisterType(&ntype); | |||||
| } | |||||