This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
Jacques Lucke 0f8487f640 Geometry Nodes: add more details about field handling to node declaration
This is part of D16858 but is also useful for other purposes.

The changes to the node declaration in this commit allow us to figure
out which fields might be evaluated on which geometries statically (without
executing the node tree). This allows for deterministic anonymous attribute
handling, which will be committed separately. Furthermore, this is necessary
for usability features that help the user to avoid creating links that don't
make sense (e.g. because a field can't be evaluated on a certain geometry).
This also allows us to better separate fields which depend or don't depend
on anonymous attributes.

The main idea is that each node defines some relations between its sockets.
There are four relations:
* Propagate relation: Indicates that attributes on a geometry input can be
  propagated to a geometry output.
* Reference relation: Indicates that an output field references an inputs field.
  So if the input field depends on an anonymous attribute, the output field
  does as well.
* Eval relation: Indicates that an input field is evaluated on an input geometry.
* Available relation: Indicates that an output field has anonymous attributes
  that are available on an output geometry.

These relations can also be computed for node groups automatically, but that
is not part of this commit.
2023-01-03 12:24:00 +01:00

105 lines
4.2 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_task.hh"
#include "BKE_instances.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_rotate_instances_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Instances")).only_instances();
b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().field_on_all();
b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).field_on_all();
b.add_input<decl::Vector>(N_("Pivot Point")).subtype(PROP_TRANSLATION).field_on_all();
b.add_input<decl::Bool>(N_("Local Space")).default_value(true).field_on_all();
b.add_output<decl::Geometry>(N_("Instances")).propagate_all();
}
static void rotate_instances(GeoNodeExecParams &params, bke::Instances &instances)
{
const bke::InstancesFieldContext context{instances};
fn::FieldEvaluator evaluator{context, instances.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Rotation"));
evaluator.add(params.extract_input<Field<float3>>("Pivot Point"));
evaluator.add(params.extract_input<Field<bool>>("Local Space"));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
const VArray<float3> rotations = evaluator.get_evaluated<float3>(0);
const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
MutableSpan<float4x4> transforms = instances.transforms();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
const int i = selection[i_selection];
const float3 pivot = pivots[i];
const float3 euler = rotations[i];
float4x4 &instance_transform = transforms[i];
float4x4 rotation_matrix;
float3 used_pivot;
if (local_spaces[i]) {
/* Find rotation axis from the matrix. This should work even if the instance is skewed. */
const float3 rotation_axis_x = instance_transform.values[0];
const float3 rotation_axis_y = instance_transform.values[1];
const float3 rotation_axis_z = instance_transform.values[2];
/* Create rotations around the individual axis. This could be optimized to skip some axis
* when the angle is zero. */
float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3];
axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x);
axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y);
axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z);
/* Combine the previously computed rotations into the final rotation matrix. */
float rotation[3][3];
mul_m3_series(rotation, rotation_z, rotation_y, rotation_x);
copy_m4_m3(rotation_matrix.values, rotation);
/* Transform the passed in pivot into the local space of the instance. */
used_pivot = instance_transform * pivot;
}
else {
used_pivot = pivot;
eul_to_mat4(rotation_matrix.values, euler);
}
/* Move the pivot to the origin so that we can rotate around it. */
sub_v3_v3(instance_transform.values[3], used_pivot);
/* Perform the actual rotation. */
mul_m4_m4_pre(instance_transform.values, rotation_matrix.values);
/* Undo the pivot shifting done before. */
add_v3_v3(instance_transform.values[3], used_pivot);
}
});
}
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
if (bke::Instances *instances = geometry_set.get_instances_for_write()) {
rotate_instances(params, *instances);
}
params.set_output("Instances", std::move(geometry_set));
}
} // namespace blender::nodes::node_geo_rotate_instances_cc
void register_node_type_geo_rotate_instances()
{
namespace file_ns = blender::nodes::node_geo_rotate_instances_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ROTATE_INSTANCES, "Rotate Instances", NODE_CLASS_GEOMETRY);
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}