Blender V5.0
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_matrix.h"
12#include "BLI_math_matrix.hh"
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 translate_pointcloud(PointCloud &pointcloud, const float3 translation)
42{
43 if (math::is_zero(translation)) {
44 return;
45 }
46
47 std::optional<Bounds<float3>> bounds;
48 if (pointcloud.runtime->bounds_cache.is_cached()) {
49 bounds = pointcloud.runtime->bounds_cache.data();
50 }
51
52 bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
54 "position", bke::AttrDomain::Point);
55 translate_positions(position.span, translation);
56 position.finish();
57
58 if (bounds) {
59 bounds->min += translation;
60 bounds->max += translation;
61 pointcloud.runtime->bounds_cache.ensure([&](Bounds<float3> &r_data) { r_data = *bounds; });
62 }
63}
64
65static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
66{
67 bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
69 "position", bke::AttrDomain::Point);
71 position.finish();
72}
73
74static void translate_greasepencil(GreasePencil &grease_pencil, const float3 translation)
75{
76 using namespace blender::bke::greasepencil;
77 for (const int layer_index : grease_pencil.layers().index_range()) {
78 Layer &layer = grease_pencil.layer(layer_index);
79 float4x4 local_transform = layer.local_transform();
80 local_transform.location() += translation;
81 layer.set_local_transform(local_transform);
82 }
83}
84
85static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &transform)
86{
87 using namespace blender::bke::greasepencil;
88 for (const int layer_index : grease_pencil.layers().index_range()) {
89 Layer &layer = grease_pencil.layer(layer_index);
90 float4x4 local_transform = layer.local_transform();
91 local_transform = transform * local_transform;
92 layer.set_local_transform(local_transform);
93 }
94}
95
96static void translate_instances(bke::Instances &instances, const float3 translation)
97{
98 MutableSpan<float4x4> transforms = instances.transforms_for_write();
99 threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
100 for (float4x4 &instance_transform : transforms.slice(range)) {
101 add_v3_v3(instance_transform.ptr()[3], translation);
102 }
103 });
104}
105
107{
108 MutableSpan<float4x4> transforms = instances.transforms_for_write();
109 threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
110 for (float4x4 &instance_transform : transforms.slice(range)) {
111 instance_transform = transform * instance_transform;
112 }
113 });
114}
115
116static void transform_volume(Volume &volume,
117 const float4x4 &transform,
118 TransformGeometryErrors &r_errors)
119{
120#ifdef WITH_OPENVDB
121 openvdb::Mat4s vdb_matrix;
122 memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
123 openvdb::Mat4d vdb_matrix_d{vdb_matrix};
124
125 const int grids_num = BKE_volume_num_grids(&volume);
126 for (const int i : IndexRange(grids_num)) {
127 bke::VolumeGridData *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
128
130 grid_matrix = transform * grid_matrix;
131 const float determinant = math::determinant(grid_matrix);
133 r_errors.volume_too_small = true;
134 /* Clear the tree because it is too small. */
136 if (determinant == 0) {
137 /* Reset rotation and scale. */
138 grid_matrix.x_axis() = float3(1, 0, 0);
139 grid_matrix.y_axis() = float3(0, 1, 0);
140 grid_matrix.z_axis() = float3(0, 0, 1);
141 }
142 else {
143 /* Keep rotation but reset scale. */
144 grid_matrix.x_axis() = math::normalize(grid_matrix.x_axis());
145 grid_matrix.y_axis() = math::normalize(grid_matrix.y_axis());
146 grid_matrix.z_axis() = math::normalize(grid_matrix.z_axis());
147 }
148 }
149 try {
151 }
152 catch (...) {
153 r_errors.bad_volume_transform = true;
154 }
155 }
156
157#else
158 UNUSED_VARS(volume, transform, r_errors);
159#endif
160}
161
162static void translate_volume(Volume &volume, const float3 translation)
163{
165 transform_volume(volume, math::from_location<float4x4>(translation), errors);
166}
167
169{
170 if (const std::optional<MutableSpan<float3>> positions = edit_hints.positions_for_write()) {
172 }
173 float3x3 deform_mat;
174 copy_m3_m4(deform_mat.ptr(), transform.ptr());
175 if (edit_hints.deform_mats.has_value()) {
176 MutableSpan<float3x3> deform_mats = *edit_hints.deform_mats;
177 threading::parallel_for(deform_mats.index_range(), 1024, [&](const IndexRange range) {
178 for (const int64_t i : range) {
179 deform_mats[i] = deform_mat * deform_mats[i];
180 }
181 });
182 }
183 else {
184 edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
185 }
186}
187
189 const float4x4 &transform)
190{
191 if (!edit_hints.drawing_hints) {
192 return;
193 }
194
195 for (bke::GreasePencilDrawingEditHints &drawing_hints : *edit_hints.drawing_hints) {
196 if (const std::optional<MutableSpan<float3>> positions = drawing_hints.positions_for_write()) {
198 }
199 float3x3 deform_mat = transform.view<3, 3>();
200 if (drawing_hints.deform_mats.has_value()) {
201 MutableSpan<float3x3> deform_mats = *drawing_hints.deform_mats;
202 threading::parallel_for(deform_mats.index_range(), 1024, [&](const IndexRange range) {
203 for (const int64_t i : range) {
204 deform_mats[i] = deform_mat * deform_mats[i];
205 }
206 });
207 }
208 else {
209 drawing_hints.deform_mats.emplace(drawing_hints.drawing_orig->strokes().points_num(),
210 deform_mat);
211 }
212 }
213}
214
216{
217 for (float4x4 &m : edit_hints.gizmo_transforms.values()) {
218 m = transform * m;
219 }
220}
221
222static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
223{
224 if (const std::optional<MutableSpan<float3>> positions = edit_hints.positions_for_write()) {
225 translate_positions(*positions, translation);
226 }
227}
228
229static void translate_gizmos_edit_hints(bke::GizmoEditHints &edit_hints, const float3 &translation)
230{
231 for (float4x4 &m : edit_hints.gizmo_transforms.values()) {
232 m.location() += translation;
233 }
234}
235
237{
238 if (math::is_zero(translation)) {
239 return;
240 }
241 if (Curves *curves = geometry.get_curves_for_write()) {
242 curves->geometry.wrap().translate(translation);
243 }
244 if (Mesh *mesh = geometry.get_mesh_for_write()) {
245 bke::mesh_translate(*mesh, translation, false);
246 }
247 if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
248 translate_pointcloud(*pointcloud, translation);
249 }
250 if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
251 translate_greasepencil(*grease_pencil, translation);
252 }
253 if (Volume *volume = geometry.get_volume_for_write()) {
254 translate_volume(*volume, translation);
255 }
256 if (bke::Instances *instances = geometry.get_instances_for_write()) {
257 translate_instances(*instances, translation);
258 }
259 if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
260 translate_curve_edit_hints(*curve_edit_hints, translation);
261 }
262 if (bke::GizmoEditHints *gizmo_edit_hints = geometry.get_gizmo_edit_hints_for_write()) {
263 translate_gizmos_edit_hints(*gizmo_edit_hints, translation);
264 }
265}
266
267std::optional<TransformGeometryErrors> transform_geometry(bke::GeometrySet &geometry,
268 const float4x4 &transform)
269{
270 if (transform == float4x4::identity()) {
271 return std::nullopt;
272 }
274 if (Curves *curves = geometry.get_curves_for_write()) {
275 curves->geometry.wrap().transform(transform);
276 }
277 if (Mesh *mesh = geometry.get_mesh_for_write()) {
279 }
280 if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
281 transform_pointcloud(*pointcloud, transform);
282 }
283 if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
284 transform_greasepencil(*grease_pencil, transform);
285 }
286 if (Volume *volume = geometry.get_volume_for_write()) {
287 transform_volume(*volume, transform, errors);
288 }
289 if (bke::Instances *instances = geometry.get_instances_for_write()) {
290 transform_instances(*instances, transform);
291 }
292 if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
293 transform_curve_edit_hints(*curve_edit_hints, transform);
294 }
295 if (bke::GreasePencilEditHints *grease_pencil_edit_hints =
296 geometry.get_grease_pencil_edit_hints_for_write())
297 {
298 transform_grease_pencil_edit_hints(*grease_pencil_edit_hints, transform);
299 }
300 if (bke::GizmoEditHints *gizmo_edit_hints = geometry.get_gizmo_edit_hints_for_write()) {
301 transform_gizmo_edit_hints(*gizmo_edit_hints, transform);
302 }
303
304 if (errors.volume_too_small) {
305 return errors;
306 }
307 return std::nullopt;
308}
309
311 const float3 translation,
312 const math::Quaternion rotation,
313 const float3 scale)
314{
315 const float4x4 matrix = math::from_loc_rot_scale<float4x4>(translation, rotation, scale);
316 bke::mesh_transform(mesh, matrix, false);
317}
318
319} // namespace blender::geometry
Low-level operations for curves.
Low-level operations for grease pencil.
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(...)
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
std::optional< MutableSpan< float3 > > positions_for_write()
std::optional< Array< float3x3 > > deform_mats
std::optional< MutableSpan< float3 > > positions_for_write()
std::optional< Array< float3x3 > > deform_mats
std::optional< Array< GreasePencilDrawingEditHints > > drawing_hints
MutableSpan< float4x4 > transforms_for_write()
Definition instances.cc:235
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
const bke::CurvesGeometry & strokes() const
void set_local_transform(const float4x4 &transform)
float determinant(MatBase< C, R >) RET
VecBase< float, 3 > float3
float4x4 get_transform_matrix(const VolumeGridData &grid)
void clear_tree(VolumeGridData &grid)
void set_transform_matrix(VolumeGridData &grid, const float4x4 &matrix)
void mesh_transform(Mesh &mesh, const float4x4 &transform, bool do_shape_keys)
void mesh_translate(Mesh &mesh, const float3 &translation, bool do_shape_keys)
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)
void translate_geometry(bke::GeometrySet &geometry, const float3 translation)
static void transform_grease_pencil_edit_hints(bke::GreasePencilEditHints &edit_hints, const float4x4 &transform)
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 transform_volume(Volume &volume, const float4x4 &transform, TransformGeometryErrors &r_errors)
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &transform)
QuaternionBase< float > Quaternion
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)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
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:93
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
PointCloudRuntimeHandle * runtime
const c_style_mat & ptr() const
i
Definition text_draw.cc:230