Point Cloud: Support set origin and apply scale operators

Move some functions for transforming a span of vectors to a header
and call them from these functions, just like was done for curves
objects recently.

Resolves T102027, T102026

Differential Revision: https://developer.blender.org/D16883
This commit is contained in:
Hans Goudey 2023-01-02 10:49:57 -05:00
parent 4ae8c52a95
commit adb4dd911b
Notes: blender-bot 2023-02-14 06:19:41 +01:00
Referenced by issue #102026, Cannot apply transformations for Point Clouds
Referenced by issue #102027, Cannot change the origin of a Point Cloud
1 changed files with 79 additions and 4 deletions

View File

@ -19,6 +19,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "BLI_array.hh"
@ -44,6 +45,7 @@
#include "BKE_mesh.h"
#include "BKE_multires.h"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_tracking.h"
@ -639,6 +641,17 @@ static bool apply_objects_internal_need_single_user(bContext *C)
return (ID_REAL_USERS(ob->data) > CTX_DATA_COUNT(C, selected_editable_objects));
}
static void transform_positions(blender::MutableSpan<blender::float3> positions,
const blender::float4x4 &matrix)
{
using namespace blender;
threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
for (float3 &position : positions.slice(range)) {
position = matrix * position;
}
});
}
static int apply_objects_internal(bContext *C,
ReportList *reports,
bool apply_loc,
@ -647,6 +660,7 @@ static int apply_objects_internal(bContext *C,
bool do_props,
bool do_single_user)
{
using namespace blender;
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -696,7 +710,8 @@ static int apply_objects_internal(bContext *C,
OB_SURF,
OB_FONT,
OB_GPENCIL,
OB_CURVES)) {
OB_CURVES,
OB_POINTCLOUD)) {
ID *obdata = static_cast<ID *>(ob->data);
if (!do_multi_user && ID_REAL_USERS(obdata) > 1) {
BKE_reportf(reports,
@ -932,6 +947,14 @@ static int apply_objects_internal(bContext *C,
blender::bke::CurvesGeometry::wrap(curves.geometry).transform(mat);
blender::bke::CurvesGeometry::wrap(curves.geometry).calculate_bezier_auto_handles();
}
else if (ob->type == OB_POINTCLOUD) {
PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
bke::SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
"position", ATTR_DOMAIN_POINT);
transform_positions(position.span, float4x4(mat));
position.finish();
}
else if (ob->type == OB_CAMERA) {
MovieClip *clip = BKE_object_movieclip_get(scene, ob, false);
@ -1230,8 +1253,29 @@ enum {
ORIGIN_TO_CENTER_OF_MASS_VOLUME,
};
static float3 calculate_mean(const blender::Span<blender::float3> values)
{
if (values.is_empty()) {
return float3(0);
}
/* TODO: Use a method that avoids overflow. */
return std::accumulate(values.begin(), values.end(), float3(0)) / values.size();
}
static void translate_positions(blender::MutableSpan<blender::float3> positions,
const blender::float3 &translation)
{
using namespace blender;
threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
for (float3 &position : positions.slice(range)) {
position += translation;
}
});
}
static int object_origin_set_exec(bContext *C, wmOperator *op)
{
using namespace blender;
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C);
@ -1653,9 +1697,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {
Span<float3> positions = curves.positions();
cent = std::accumulate(positions.begin(), positions.end(), float3(0)) /
curves.points_num();
cent = calculate_mean(curves.positions());
}
tot_change++;
@ -1663,6 +1705,39 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
curves_id.id.tag |= LIB_TAG_DOIT;
do_inverse_offset = true;
}
else if (ob->type == OB_POINTCLOUD) {
PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
bke::SpanAttributeWriter positions = attributes.lookup_or_add_for_write_span<float3>(
"position", ATTR_DOMAIN_POINT);
if (ELEM(centermode, ORIGIN_TO_CENTER_OF_MASS_SURFACE, ORIGIN_TO_CENTER_OF_MASS_VOLUME) ||
!ELEM(around, V3D_AROUND_CENTER_BOUNDS, V3D_AROUND_CENTER_MEDIAN)) {
BKE_report(op->reports,
RPT_WARNING,
"Point cloud object does not support this set origin operation");
continue;
}
if (centermode == ORIGIN_TO_CURSOR) {
/* Done. */
}
else if (around == V3D_AROUND_CENTER_BOUNDS) {
float3 min(std::numeric_limits<float>::max());
float3 max(-std::numeric_limits<float>::max());
if (pointcloud.bounds_min_max(min, max)) {
cent = math::midpoint(min, max);
}
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {
cent = calculate_mean(positions.span);
}
tot_change++;
translate_positions(positions.span, -cent);
positions.finish();
pointcloud.id.tag |= LIB_TAG_DOIT;
do_inverse_offset = true;
}
/* offset other selected objects */
if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) {