Blender V4.3
geometry/intern/transform.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#ifdef WITH_OPENVDB
6# include <openvdb/openvdb.h>
7#endif
8
9#include "GEO_transform.hh"
10
11#include "BLI_math_base.h"
12#include "BLI_math_matrix.h"
13#include "BLI_math_vector.hh"
14#include "BLI_task.hh"
15
17#include "DNA_mesh_types.h"
19
20#include "BKE_attribute.hh"
21#include "BKE_curves.hh"
23#include "BKE_geometry_set.hh"
24#include "BKE_grease_pencil.hh"
25#include "BKE_instances.hh"
26#include "BKE_mesh.hh"
27#include "BKE_pointcloud.hh"
28#include "BKE_volume.hh"
29
30namespace blender::geometry {
31
32static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
33{
34 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
35 for (float3 &position : positions.slice(range)) {
36 position += translation;
37 }
38 });
39}
40
41static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
42{
43 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
44 for (float3 &position : positions.slice(range)) {
45 position = math::transform_point(matrix, position);
46 }
47 });
48}
49
50static void transform_mesh(Mesh &mesh, const float4x4 &transform)
51{
52 transform_positions(mesh.vert_positions_for_write(), transform);
53 mesh.tag_positions_changed();
54}
55
56static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
57{
58 if (math::is_zero(translation)) {
59 return;
60 }
61
62 std::optional<Bounds<float3>> bounds;
63 if (pointcloud.runtime->bounds_cache.is_cached()) {
64 bounds = pointcloud.runtime->bounds_cache.data();
65 }
66
67 bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
68 bke::SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
69 "position", bke::AttrDomain::Point);
70 translate_positions(position.span, translation);
71 position.finish();
72
73 if (bounds) {
74 bounds->min += translation;
75 bounds->max += translation;
76 pointcloud.runtime->bounds_cache.ensure([&](Bounds<float3> &r_data) { r_data = *bounds; });
77 }
78}
79
80static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
81{
82 bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
83 bke::SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
84 "position", bke::AttrDomain::Point);
85 transform_positions(position.span, transform);
86 position.finish();
87}
88
89static void translate_greasepencil(GreasePencil &grease_pencil, const float3 translation)
90{
91 using namespace blender::bke::greasepencil;
92 for (const int layer_index : grease_pencil.layers().index_range()) {
93 Layer &layer = grease_pencil.layer(layer_index);
94 float4x4 local_transform = layer.local_transform();
95 local_transform.location() += translation;
96 layer.set_local_transform(local_transform);
97 }
98}
99
100static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &transform)
101{
102 using namespace blender::bke::greasepencil;
103 for (const int layer_index : grease_pencil.layers().index_range()) {
104 Layer &layer = grease_pencil.layer(layer_index);
105 float4x4 local_transform = layer.local_transform();
106 local_transform = transform * local_transform;
107 layer.set_local_transform(local_transform);
108 }
109}
110
111static void translate_instances(bke::Instances &instances, const float3 translation)
112{
113 MutableSpan<float4x4> transforms = instances.transforms_for_write();
114 threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
115 for (float4x4 &instance_transform : transforms.slice(range)) {
116 add_v3_v3(instance_transform.ptr()[3], translation);
117 }
118 });
119}
120
121static void transform_instances(bke::Instances &instances, const float4x4 &transform)
122{
123 MutableSpan<float4x4> transforms = instances.transforms_for_write();
124 threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
125 for (float4x4 &instance_transform : transforms.slice(range)) {
126 instance_transform = transform * instance_transform;
127 }
128 });
129}
130
131static bool transform_volume(Volume &volume, const float4x4 &transform)
132{
133 bool found_too_small_scale = false;
134#ifdef WITH_OPENVDB
135 openvdb::Mat4s vdb_matrix;
136 memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
137 openvdb::Mat4d vdb_matrix_d{vdb_matrix};
138
139 const int grids_num = BKE_volume_num_grids(&volume);
140 for (const int i : IndexRange(grids_num)) {
141 bke::VolumeGridData *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
142
143 float4x4 grid_matrix = bke::volume_grid::get_transform_matrix(*volume_grid);
144 grid_matrix = transform * grid_matrix;
145 const float determinant = math::determinant(grid_matrix);
147 found_too_small_scale = true;
148 /* Clear the tree because it is too small. */
149 bke::volume_grid::clear_tree(*volume_grid);
150 if (determinant == 0) {
151 /* Reset rotation and scale. */
152 grid_matrix.x_axis() = float3(1, 0, 0);
153 grid_matrix.y_axis() = float3(0, 1, 0);
154 grid_matrix.z_axis() = float3(0, 0, 1);
155 }
156 else {
157 /* Keep rotation but reset scale. */
158 grid_matrix.x_axis() = math::normalize(grid_matrix.x_axis());
159 grid_matrix.y_axis() = math::normalize(grid_matrix.y_axis());
160 grid_matrix.z_axis() = math::normalize(grid_matrix.z_axis());
161 }
162 }
163 bke::volume_grid::set_transform_matrix(*volume_grid, grid_matrix);
164 }
165
166#else
167 UNUSED_VARS(volume, transform);
168#endif
169 return found_too_small_scale;
170}
171
172static void translate_volume(Volume &volume, const float3 translation)
173{
175}
176
177static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
178{
179 if (const std::optional<MutableSpan<float3>> positions = edit_hints.positions_for_write()) {
180 transform_positions(*positions, transform);
181 }
182 float3x3 deform_mat;
183 copy_m3_m4(deform_mat.ptr(), transform.ptr());
184 if (edit_hints.deform_mats.has_value()) {
185 MutableSpan<float3x3> deform_mats = *edit_hints.deform_mats;
186 threading::parallel_for(deform_mats.index_range(), 1024, [&](const IndexRange range) {
187 for (const int64_t i : range) {
188 deform_mats[i] = deform_mat * deform_mats[i];
189 }
190 });
191 }
192 else {
193 edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
194 }
195}
196
197static void transform_gizmo_edit_hints(bke::GizmoEditHints &edit_hints, const float4x4 &transform)
198{
199 for (float4x4 &m : edit_hints.gizmo_transforms.values()) {
200 m = transform * m;
201 }
202}
203
204static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
205{
206 if (const std::optional<MutableSpan<float3>> positions = edit_hints.positions_for_write()) {
207 translate_positions(*positions, translation);
208 }
209}
210
211static void translate_gizmos_edit_hints(bke::GizmoEditHints &edit_hints, const float3 &translation)
212{
213 for (float4x4 &m : edit_hints.gizmo_transforms.values()) {
214 m.location() += translation;
215 }
216}
217
218void translate_geometry(bke::GeometrySet &geometry, const float3 translation)
219{
220 if (Curves *curves = geometry.get_curves_for_write()) {
221 curves->geometry.wrap().translate(translation);
222 }
223 if (Mesh *mesh = geometry.get_mesh_for_write()) {
224 BKE_mesh_translate(mesh, translation, false);
225 }
226 if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
227 translate_pointcloud(*pointcloud, translation);
228 }
229 if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
230 translate_greasepencil(*grease_pencil, translation);
231 }
232 if (Volume *volume = geometry.get_volume_for_write()) {
233 translate_volume(*volume, translation);
234 }
235 if (bke::Instances *instances = geometry.get_instances_for_write()) {
236 translate_instances(*instances, translation);
237 }
238 if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
239 translate_curve_edit_hints(*curve_edit_hints, translation);
240 }
241 if (bke::GizmoEditHints *gizmo_edit_hints = geometry.get_gizmo_edit_hints_for_write()) {
242 translate_gizmos_edit_hints(*gizmo_edit_hints, translation);
243 }
244}
245
246std::optional<TransformGeometryErrors> transform_geometry(bke::GeometrySet &geometry,
247 const float4x4 &transform)
248{
250 if (Curves *curves = geometry.get_curves_for_write()) {
251 curves->geometry.wrap().transform(transform);
252 }
253 if (Mesh *mesh = geometry.get_mesh_for_write()) {
254 transform_mesh(*mesh, transform);
255 }
256 if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
257 transform_pointcloud(*pointcloud, transform);
258 }
259 if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
260 transform_greasepencil(*grease_pencil, transform);
261 }
262 if (Volume *volume = geometry.get_volume_for_write()) {
263 errors.volume_too_small = transform_volume(*volume, transform);
264 }
265 if (bke::Instances *instances = geometry.get_instances_for_write()) {
266 transform_instances(*instances, transform);
267 }
268 if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
269 transform_curve_edit_hints(*curve_edit_hints, transform);
270 }
271 if (bke::GizmoEditHints *gizmo_edit_hints = geometry.get_gizmo_edit_hints_for_write()) {
272 transform_gizmo_edit_hints(*gizmo_edit_hints, transform);
273 }
274
275 if (errors.volume_too_small) {
276 return errors;
277 }
278 return std::nullopt;
279}
280
282 const float3 translation,
283 const math::Quaternion rotation,
284 const float3 scale)
285{
286 const float4x4 matrix = math::from_loc_rot_scale<float4x4>(translation, rotation, scale);
287 transform_mesh(mesh, matrix);
288}
289
290} // namespace blender::geometry
Low-level operations for curves.
Low-level operations for grease pencil.
void BKE_mesh_translate(Mesh *mesh, const float offset[3], bool do_keys)
General operations for point clouds.
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
blender::bke::VolumeGridData * BKE_volume_grid_get_for_write(Volume *volume, int grid_index)
bool BKE_volume_grid_determinant_valid(double determinant)
void copy_m3_m4(float m1[3][3], const float m2[4][4])
#define UNUSED_VARS(...)
static void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
btScalar determinant() const
Return the determinant of the matrix.
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
std::optional< MutableSpan< float3 > > positions_for_write()
std::optional< Array< float3x3 > > deform_mats
static void transform_positions(const Span< blender::float3 > src, const blender::float4x4 &transform, blender::MutableSpan< blender::float3 > dst)
static void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
static void translate_greasepencil(GreasePencil &grease_pencil, const float3 translation)
static void transform_instances(bke::Instances &instances, const float4x4 &transform)
static bool transform_volume(Volume &volume, const float4x4 &transform)
void translate_geometry(bke::GeometrySet &geometry, const float3 translation)
static void translate_gizmos_edit_hints(bke::GizmoEditHints &edit_hints, const float3 &translation)
static void translate_instances(bke::Instances &instances, const float3 translation)
static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
static void transform_gizmo_edit_hints(bke::GizmoEditHints &edit_hints, const float4x4 &transform)
void transform_mesh(Mesh &mesh, float3 translation, math::Quaternion rotation, float3 scale)
static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
static void translate_volume(Volume &volume, const float3 translation)
std::optional< TransformGeometryErrors > transform_geometry(bke::GeometrySet &geometry, const float4x4 &transform)
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &transform)
bool is_zero(const T &a)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
MatT from_location(const typename MatT::loc_type &location)
T determinant(const MatBase< T, Size, Size > &mat)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
PointCloudRuntimeHandle * runtime
const c_style_mat & ptr() const