/* * 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_noise.hh" #include "BLI_span.hh" #include "BLI_task.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_spline.hh" #include "node_geometry_util.hh" #include "UI_interface.h" #include "UI_resources.h" namespace blender::nodes::node_geo_create_faces_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input(N_("Geometry")); b.add_input(N_("Selection")).hide_value().default_value(true).supports_field(); b.add_input(N_("Group Index")).supports_field(); b.add_output(N_("Geometry")); b.add_output(N_("New Geometry")).dependent_field(); } // static void node_init(bNodeTree *UNUSED(tree), bNode *node) // { // NodeGeometryDuplicateElements *data = MEM_cnew(__func__); // data->domain = ATTR_DOMAIN_POINT; // node->storage = data; //} // static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) // { // uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); // } static int hash_vertex_list(MutableSpan verts) { std::sort(verts.begin(), verts.end()); int hash = 351313515; for (const int i_vert : verts.index_range()) { hash = noise::hash(verts[i_vert], hash); } return hash; } static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); Field selection = params.extract_input>("Selection"); Field group = params.extract_input>("Group Index"); geometry_set.modify_geometry_sets([&](GeometrySet geometry_set) { if (!geometry_set.has_mesh()) { return; } MeshComponent &mesh_component = geometry_set.get_component_for_write(); Mesh *mesh = mesh_component.get_for_write(); /* Create a hash for each face based on the sorted list of verts in each face. Put it in a set * for lookup later. */ Set face_hashes; Span polys(mesh->mpoly, mesh->totpoly); for (const int i : polys.index_range()) { Span loops(&mesh->mloop[polys[i].loopstart], polys[i].totloop); Array verts(polys[i].totloop); for (const int i_loop : loops.index_range()) { verts[i_loop] = loops[i_loop].v; } face_hashes.add(hash_vertex_list(verts.as_mutable_span())); } /* Create an edge map to use for looking up if vertex pairs already share an edge. */ Map, int> vert_to_edge_map; Span edges(mesh->medge, mesh->totedge); for (const int i : edges.index_range()) { vert_to_edge_map.add( {std::min(edges[i].v1, edges[i].v2), std::max(edges[i].v1, edges[i].v2)}, i); } /* Evlaute the group and selection fields. */ GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{context, mesh->totvert}; evaluator.add(group); evaluator.set_selection(selection); evaluator.evaluate(); const VArray group_eval = evaluator.get_evaluated(0); const IndexMask mask = evaluator.get_evaluated_selection_as_mask(); /* Group the new face vertices together. */ Set groups; for (const int i : mask) { groups.add(group_eval[i]); } Array keep(groups.size(), false); Array> new_face_verts(groups.size()); Array new_face_hashes(groups.size()); for (const int i : mask) { new_face_verts[group_eval[i]].append(i); } /* For each new face group, get a hash and see if it is in the face hash set. If it * is, we will ignore that new face set. */ int new_faces = 0; for (const int i : new_face_verts.index_range()) { const int hash = hash_vertex_list(new_face_verts[i]); if (face_hashes.contains(hash)) { keep[i] = false; continue; } keep[i] = true; new_faces++; } /* Figure out order, then see what edges will be needed. */ // TODO /* Expand Mesh. */ // TODO: Use code from Extrude? /* Add new face and edge data into new mesh. */ // TODO /* Transfer any needed attruibutes for faces and edges. */ // TODO }); params.set_output("Geometry", geometry_set); } } // namespace blender::nodes::node_geo_create_faces_cc void register_node_type_geo_create_faces() { namespace file_ns = blender::nodes::node_geo_create_faces_cc; static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CREATE_FACES, "Create Faces Elements", NODE_CLASS_GEOMETRY); node_type_init(&ntype, file_ns::node_init); ntype.draw_buttons = file_ns::node_layout; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); }