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.extract_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.extract_input<Field<float3>>("Scale"), &scales); | |||||
| transforms_evaluator.add(params.extract_input<Field<float3>>("Pivot Point"), &pivots); | |||||
HooglyBoogly: Since this is only called once, this could call `extract_input` | |||||
| transforms_evaluator.add(params.extract_input<Field<bool>>("Local Space"), &local_spaces); | |||||
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.evaluate(); | |||||
| MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); | |||||
| threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) { | |||||
| for (const int i : range) { | |||||
| const float3 scale = scales->get(selection[i]); | |||||
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 pivot = pivots->get(selection[i]); | |||||
| const bool local_space = local_spaces->get(selection[i]); | |||||
| float4x4 &instance_transform = instance_transforms[selection[i]]; | |||||
| const float4x4 pivot_matrix = float4x4::from_location(pivot); | |||||
| if (local_space) { | |||||
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… | |||||
| instance_transform *= pivot_matrix; | |||||
| rescale_m4(instance_transform.values, scale); | |||||
| instance_transform *= pivot_matrix.inverted(); | |||||
| } | |||||
| else { | |||||
| const float4x4 org_transform = float4x4(instance_transform); | |||||
| instance_transform = float4x4::identity(); | |||||
| instance_transform *= pivot_matrix; | |||||
| rescale_m4(instance_transform.values, scale); | |||||
| instance_transform *= pivot_matrix.inverted(); | |||||
| instance_transform *= org_transform; | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| 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