Geometry Nodes: new Material Assign node
authorJacques Lucke <jacques@blender.org>
Thu, 20 May 2021 10:22:13 +0000 (12:22 +0200)
committerJacques Lucke <jacques@blender.org>
Thu, 20 May 2021 10:22:13 +0000 (12:22 +0200)
This adds a new Material Assign node. It can be used to change the
material used by an existing mesh or to assign a material to a mesh
that has been generated from scratch.

Differential Revision: https://developer.blender.org/D11155

release/scripts/startup/nodeitems_builtins.py
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.cc
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_geometry.h
source/blender/nodes/NOD_static_types.h
source/blender/nodes/geometry/nodes/node_geo_material_assign.cc [new file with mode: 0644]

index 96c825c528515ef8abc2e88fe54fb4f099a61bca..7faed2d4bceac3d040c4ac627f65e122542e5506 100644 (file)
@@ -525,6 +525,9 @@ geometry_node_categories = [
         NodeItem("FunctionNodeInputVector"),
         NodeItem("GeometryNodeIsViewport"),
     ]),
+    GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
+        NodeItem("GeometryNodeMaterialAssign"),
+    ]),
     GeometryNodeCategory("GEO_MESH", "Mesh", items=[
         NodeItem("GeometryNodeBoolean"),
         NodeItem("GeometryNodeTriangulate"),
index 8824ffc5695cb93bf983c29658a84209e64ee366..5ee4f5f5a993c4ee3a174e40c2bc27fb869c5b6d 100644 (file)
@@ -1423,6 +1423,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
 #define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
 #define GEO_NODE_CURVE_RESAMPLE 1047
 #define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
+#define GEO_NODE_MATERIAL_ASSIGN 1049
 
 /** \} */
 
index a0e444186f2c74860e481c08f6df37a43ba3cc71..6f961657d162e57558b7af2fea88d77d69fe3783 100644 (file)
@@ -5055,6 +5055,7 @@ static void registerGeometryNodes()
   register_node_type_geo_edge_split();
   register_node_type_geo_is_viewport();
   register_node_type_geo_join_geometry();
+  register_node_type_geo_material_assign();
   register_node_type_geo_mesh_primitive_circle();
   register_node_type_geo_mesh_primitive_cone();
   register_node_type_geo_mesh_primitive_cube();
index a1d7e6c8522ec19e3b42f95946d2de0be5068955..ed2a6a24569d8c30a006a876d0c3face85642659 100644 (file)
@@ -167,6 +167,7 @@ set(SRC
   geometry/nodes/node_geo_edge_split.cc
   geometry/nodes/node_geo_is_viewport.cc
   geometry/nodes/node_geo_join_geometry.cc
+  geometry/nodes/node_geo_material_assign.cc
   geometry/nodes/node_geo_mesh_primitive_circle.cc
   geometry/nodes/node_geo_mesh_primitive_cone.cc
   geometry/nodes/node_geo_mesh_primitive_cube.cc
index 76c9ab8a5026af0472cf17019244b2abc7c45781..879092f1476afb14315850b79d1732e927c2c3b3 100644 (file)
@@ -55,6 +55,7 @@ void register_node_type_geo_curve_resample(void);
 void register_node_type_geo_edge_split(void);
 void register_node_type_geo_is_viewport(void);
 void register_node_type_geo_join_geometry(void);
+void register_node_type_geo_material_assign(void);
 void register_node_type_geo_mesh_primitive_circle(void);
 void register_node_type_geo_mesh_primitive_cone(void);
 void register_node_type_geo_mesh_primitive_cube(void);
index 8e4839310d14da8d7f1a7079fb198893f7e72d52..3b2c7feb00ec5267c7f5c2d8a6fb9391d505f7b4 100644 (file)
@@ -294,6 +294,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "
 DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
 DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
 DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "")
 DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "")
 DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
 DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
new file mode 100644 (file)
index 0000000..8c245af
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_assign_in[] = {
+    {SOCK_GEOMETRY, N_("Geometry")},
+    {SOCK_MATERIAL, N_("Material")},
+    {SOCK_STRING, N_("Selection")},
+    {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_assign_out[] = {
+    {SOCK_GEOMETRY, N_("Geometry")},
+    {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
+{
+  int new_material_index = -1;
+  for (const int i : IndexRange(mesh.totcol)) {
+    Material *other_material = mesh.mat[i];
+    if (other_material == material) {
+      new_material_index = i;
+      break;
+    }
+  }
+  if (new_material_index == -1) {
+    /* Append a new material index. */
+    new_material_index = mesh.totcol;
+    BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
+  }
+
+  mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
+  for (const int i : IndexRange(mesh.totpoly)) {
+    if (face_mask[i]) {
+      MPoly &poly = mesh.mpoly[i];
+      poly.mat_nr = new_material_index;
+    }
+  }
+}
+
+static void geo_node_material_assign_exec(GeoNodeExecParams params)
+{
+  Material *material = params.extract_input<Material *>("Material");
+  const std::string mask_name = params.extract_input<std::string>("Selection");
+
+  GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+  geometry_set = geometry_set_realize_instances(geometry_set);
+
+  if (geometry_set.has<MeshComponent>()) {
+    MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+    Mesh *mesh = mesh_component.get_for_write();
+    if (mesh != nullptr) {
+      GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
+          mask_name, ATTR_DOMAIN_FACE, true);
+      assign_material_to_faces(*mesh, face_mask, material);
+    }
+  }
+
+  params.set_output("Geometry", std::move(geometry_set));
+}
+
+}  // namespace blender::nodes
+
+void register_node_type_geo_material_assign()
+{
+  static bNodeType ntype;
+
+  geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
+  node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out);
+  ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec;
+  nodeRegisterType(&ntype);
+}