diff --git a/scripts/startup/bl_ui/node_add_menu_geometry.py b/scripts/startup/bl_ui/node_add_menu_geometry.py index 1aecad347db..86e55f041a6 100644 --- a/scripts/startup/bl_ui/node_add_menu_geometry.py +++ b/scripts/startup/bl_ui/node_add_menu_geometry.py @@ -312,11 +312,13 @@ class NODE_MT_geometry_node_GEO_INSTANCE(Menu): node_add_menu.add_node_type(layout, "GeometryNodeInstanceOnPoints", search_weight=2.0) node_add_menu.add_node_type(layout, "GeometryNodeInstancesToPoints") layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeTransformInstances") node_add_menu.add_node_type(layout, "GeometryNodeRealizeInstances", search_weight=1.0) node_add_menu.add_node_type(layout, "GeometryNodeRotateInstances") node_add_menu.add_node_type(layout, "GeometryNodeScaleInstances") node_add_menu.add_node_type(layout, "GeometryNodeTranslateInstances") layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeInstanceTransform") node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceRotation") node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceScale") node_add_menu.draw_assets_for_catalog(layout, self.bl_label) diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index b97ba0c2c78..0253b36d1f6 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -1296,6 +1296,8 @@ void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index #define GEO_NODE_SDF_GRID_BOOLEAN 2131 #define GEO_NODE_TOOL_VIEWPORT_TRANSFORM 2132 #define GEO_NODE_TOOL_MOUSE_POSITION 2133 +#define GEO_NODE_TRANSFORM_INSTANCES 2134 +#define GEO_NODE_INPUT_INSTANCE_TRANSFORM 2135 /** \} */ diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5f09091a805..be64cce254e 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -364,6 +364,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "Re DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "Retrieve the rotation of each instance in the geometry") DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "Retrieve the scale of each instance in the geometry") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "Retrieve the index of the material used for each element in the geometry's list of materials") +DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_TRANSFORM, 0, "INPUT_INSTANCE_TRANSFORM", InstanceTransform, "Instance Transform", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "Output a single material") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "Calculate the surface area of each face in a mesh") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS",InputMeshEdgeNeighbors, "Edge Neighbors", "Retrieve the number of faces that use each edge as one of their sides") @@ -477,6 +478,7 @@ DefNode(GeometryNode, GEO_NODE_TOOL_SET_FACE_SET, 0, "TOOL_SET_FACE_SET", ToolSe DefNode(GeometryNode, GEO_NODE_TOOL_SET_SELECTION, 0, "TOOL_SELECTION_SET", ToolSetSelection, "Set Selection", "Set selection of the edited geometry, for tool execution") DefNode(GeometryNode, GEO_NODE_TOOL_VIEWPORT_TRANSFORM, 0, "VIEWPORT_TRANFORM", ViewportTransform, "Viewport Transform", "Retrieve the view direction and location of the 3D viewport") DefNode(GeometryNode, GEO_NODE_TRANSFORM_GEOMETRY, 0, "TRANSFORM_GEOMETRY", Transform, "Transform Geometry", "Translate, rotate or scale the geometry") +DefNode(GeometryNode, GEO_NODE_TRANSFORM_INSTANCES, 0, "TRANSFORM_INSTANCES", TransformInstances, "Transform Instances", "") DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances", "Move top-level geometry instances in local or global space") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, 0, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces") DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, 0, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end") diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index cd8607f482a..1901bfa319e 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -96,6 +96,7 @@ set(SRC nodes/node_geo_input_index.cc nodes/node_geo_input_instance_rotation.cc nodes/node_geo_input_instance_scale.cc + nodes/node_geo_input_instance_transform.cc nodes/node_geo_input_material.cc nodes/node_geo_input_material_index.cc nodes/node_geo_input_mesh_edge_angle.cc @@ -201,6 +202,7 @@ set(SRC nodes/node_geo_tool_set_face_set.cc nodes/node_geo_tool_set_selection.cc nodes/node_geo_transform_geometry.cc + nodes/node_geo_transform_instances.cc nodes/node_geo_translate_instances.cc nodes/node_geo_triangulate.cc nodes/node_geo_uv_pack_islands.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_transform.cc new file mode 100644 index 00000000000..cd7bec95c2d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_transform.cc @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_geometry_fields.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_instance_transform_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Transform").field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field position_field{AttributeFieldInput::Create("instance_transform")}; + params.set_output("Transform", std::move(position_field)); +} + +static void node_register() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_INSTANCE_TRANSFORM, "Instance Transform", NODE_CLASS_INPUT); + ntype.geometry_node_execute = node_geo_exec; + ntype.declare = node_declare; + nodeRegisterType(&ntype); +} +NOD_REGISTER_NODE(node_register) + +} // namespace blender::nodes::node_geo_input_instance_transform_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_transform_instances.cc new file mode 100644 index 00000000000..f441b42f124 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_transform_instances.cc @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_geometry_fields.hh" +#include "BKE_geometry_set.hh" +#include "BKE_instances.hh" + +#include "BLI_math_matrix.hh" + +#include "FN_field.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_transform_instances_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Instances"); + b.add_input("Selection").default_value(true).hide_value().field_on_all(); + b.add_input("Transform") + .implicit_field_on_all(implicit_field_inputs::instance_transform); + b.add_input("To Apply").field_on_all(); + b.add_output("Instances").propagate_all(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Instances"); + Field transform_field = params.extract_input>("Transform"); + Field transformation_field = params.extract_input>("To Apply"); + const Field selection_field = params.extract_input>("Selection"); + + static const auto apply_fn = mf::build::SI2_SO( + "Apply Transform", [](float4x4 a, float4x4 b) { return a * b; }); + + const Field transform_field_to_stor(FieldOperation::Create( + apply_fn, {std::move(transform_field), std::move(transformation_field)})); + + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + const bke::InstancesFieldContext context(*instances); + bke::try_capture_field_on_geometry(instances->attributes_for_write(), + context, + "instance_transform", + bke::AttrDomain::Instance, + selection_field, + transform_field_to_stor); + } + + params.set_output("Instances", std::move(geometry_set)); +} + +static void register_node() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSFORM_INSTANCES, "Transform Instances", NODE_CLASS_GEOMETRY); + ntype.declare = node_declare; + ntype.geometry_node_execute = node_geo_exec; + nodeRegisterType(&ntype); +} +NOD_REGISTER_NODE(register_node) + +} // namespace blender::nodes::node_geo_transform_instances_cc