Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_scale_instances.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_task.hh" | |||||
| #include "node_geometry_util.hh" | |||||
| namespace blender::nodes { | |||||
| static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) | |||||
| { | |||||
| b.add_input<decl::Geometry>("Geometry"); | |||||
| b.add_input<decl::Vector>("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field(); | |||||
| b.add_input<decl::Vector>("Pivot Point").subtype(PROP_TRANSLATION).supports_field(); | |||||
| b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); | |||||
| b.add_input<decl::Bool>("Local Space").default_value(true).supports_field(); | |||||
| b.add_output<decl::Geometry>("Geometry"); | |||||
| }; | |||||
| static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) | |||||
| { | |||||
| GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; | |||||
| const int domain_size = instances_component.instances_amount(); | |||||
| fn::FieldEvaluator selection_evaluator{field_context, domain_size}; | |||||
| selection_evaluator.add(params.get_input<Field<bool>>("Selection")); | |||||
| selection_evaluator.evaluate(); | |||||
| const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); | |||||
| fn::FieldEvaluator transforms_evaluator{field_context, &selection}; | |||||
| const VArray<float3> *scales = nullptr; | |||||
| const VArray<float3> *pivots = nullptr; | |||||
| const VArray<bool> *local_spaces = nullptr; | |||||
| transforms_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales); | |||||
HooglyBoogly: Since this is only called once, this could call `extract_input` | |||||
| transforms_evaluator.add(params.get_input<Field<float3>>("Pivot Point"), &pivots); | |||||
HooglyBooglyUnsubmitted Done Inline ActionsSince this is only called once, these could call extract_input HooglyBoogly: Since this is only called once, these could call `extract_input` | |||||
| transforms_evaluator.add(params.get_input<Field<bool>>("Local Space"), &local_spaces); | |||||
| transforms_evaluator.evaluate(); | |||||
| MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); | |||||
| threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) { | |||||
| for (const int i : range) { | |||||
HooglyBooglyUnsubmitted Done Inline ActionsI don't think this is quite right, the index here is the index into the mask, not the index of the index. Here's an example from the curve reverse node: threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
splines[selection[i]]->reverse();
}
});HooglyBoogly: I don't think this is quite right, the index here is the index into the mask, not the index of… | |||||
| const float3 scale = scales->get(i); | |||||
| const float3 pivot = pivots->get(i); | |||||
| const bool local_space = local_spaces->get(i); | |||||
| float4x4 &instance_transform = instance_transforms[i]; | |||||
| const float4x4 pivot_matrix = float4x4::from_location(pivot); | |||||
| const float4x4 scale_matrix = float4x4::from_loc_eul_scale({0, 0, 0}, {0, 0, 0}, scale); | |||||
HooglyBooglyUnsubmitted Done Inline ActionsConstructing a transform matrix from an Euler rotation is very slow. We should try to avoid this constructor whenever we can. I wonder if this matrix is actually necessary, or if float4x4::apply_scale(scale) would work. HooglyBoogly: Constructing a transform matrix from an Euler rotation is very slow. We should try to avoid… | |||||
| if (local_space) { | |||||
| mul_m4_m4_post(instance_transform.values, pivot_matrix.values); | |||||
| mul_m4_m4_post(instance_transform.values, scale_matrix.values); | |||||
| mul_m4_m4_post(instance_transform.values, pivot_matrix.inverted().values); | |||||
| } | |||||
| else { | |||||
| const float4x4 old_matrix = float4x4(instance_transform); | |||||
| mul_m4_m4_post(instance_transform.values, instance_transform.inverted().values); | |||||
| mul_m4_m4_post(instance_transform.values, pivot_matrix.values); | |||||
| mul_m4_m4_post(instance_transform.values, scale_matrix.values); | |||||
| mul_m4_m4_post(instance_transform.values, pivot_matrix.inverted().values); | |||||
| mul_m4_m4_post(instance_transform.values, old_matrix.values); | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| static void geo_node_scale_instances_exec(GeoNodeExecParams params) | |||||
| { | |||||
| GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); | |||||
| if (geometry_set.has_instances()) { | |||||
| InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); | |||||
| scale_instances(params, instances); | |||||
| } | |||||
| params.set_output("Geometry", std::move(geometry_set)); | |||||
| } | |||||
| } // namespace blender::nodes | |||||
| void register_node_type_geo_scale_instances() | |||||
| { | |||||
| static bNodeType ntype; | |||||
| geo_node_type_base(&ntype, GEO_NODE_SCALE_INSTANCES, "Scale Instances", NODE_CLASS_GEOMETRY, 0); | |||||
| ntype.geometry_node_execute = blender::nodes::geo_node_scale_instances_exec; | |||||
| ntype.declare = blender::nodes::geo_node_scale_instances_declare; | |||||
| nodeRegisterType(&ntype); | |||||
| } | |||||
Since this is only called once, this could call extract_input